Files
paseo/packages/protocol/src/messages.ts
T
Mohamed Boudra d0bc188a5b Show agent messages and completion notifications with sender identity (#6373)
* feat(agents): show agent messages and completion notifications with sender identity

* fix(agents): preserve human envelope examples and attachment context

* fix(agents): separate message provenance from timeline presentation
2026-10-08 14:42:34 +01:00

7614 lines
265 KiB
TypeScript

import { AgentMessageSchema } from "./agent-message.js";
import { PluginRegistryIdentitySchema } from "./plugin-registry.js";
import { AgentProfileSchema, AgentSkillSelectionSchema } from "./agent-profile.js";
export {
AgentProfileSchema,
AgentSkillSelectionSchema,
type AgentProfile,
type AgentSkillSelection,
} from "./agent-profile.js";
import { PluginIdSchema, PluginRequirementsSchema, PluginSourceSchema } from "./plugin-config.js";
export {
PluginIdSchema,
PluginRequirementsSchema,
DirectoryPluginSourceSchema,
PluginSourceSchema,
type PluginRequirements,
type PluginSource,
} from "./plugin-config.js";
import { TerminalProfileSchema } from "./terminal-profile.js";
export { TerminalProfileSchema, type TerminalProfile } from "./terminal-profile.js";
import { z } from "zod";
import { TerminalActivitySchema } from "./terminal-activity.js";
import { CLIENT_CAPS } from "./client-capabilities.js";
import { AGENT_LIFECYCLE_STATUSES } from "./agent-lifecycle.js";
import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from "./agent-title-limits.js";
import { AgentProviderSchema } from "./provider-manifest.js";
import { ProviderPaseoToolsPolicySchema } from "./provider-config.js";
import { TOOL_CALL_ICON_NAMES } from "./agent-types.js";
import { WORKSPACE_LABEL_COLORS } from "./workspace-labels.js";
import {
ChatCreateRequestSchema,
ChatListRequestSchema,
ChatInspectRequestSchema,
ChatDeleteRequestSchema,
ChatPostRequestSchema,
ChatReadRequestSchema,
ChatWaitRequestSchema,
ChatCreateResponseSchema,
ChatListResponseSchema,
ChatInspectResponseSchema,
ChatDeleteResponseSchema,
ChatPostResponseSchema,
ChatReadResponseSchema,
ChatWaitResponseSchema,
} from "./chat/rpc-schemas.js";
import {
ScheduleCreateRequestSchema,
ScheduleListRequestSchema,
ScheduleInspectRequestSchema,
ScheduleLogsRequestSchema,
SchedulePauseRequestSchema,
ScheduleResumeRequestSchema,
ScheduleDeleteRequestSchema,
ScheduleRunOnceRequestSchema,
ScheduleUpdateRequestSchema,
ScheduleCreateResponseSchema,
ScheduleListResponseSchema,
ScheduleInspectResponseSchema,
ScheduleLogsResponseSchema,
SchedulePauseResponseSchema,
ScheduleResumeResponseSchema,
ScheduleDeleteResponseSchema,
ScheduleRunOnceResponseSchema,
ScheduleUpdateResponseSchema,
} from "./schedule/rpc-schemas.js";
import {
LoopRunRequestSchema,
LoopListRequestSchema,
LoopInspectRequestSchema,
LoopLogsRequestSchema,
LoopStopRequestSchema,
LoopRunResponseSchema,
LoopListResponseSchema,
LoopInspectResponseSchema,
LoopLogsResponseSchema,
LoopStopResponseSchema,
} from "./loop/rpc-schemas.js";
import {
BROWSER_AUTOMATION_COMMAND_NAMES,
BrowserAutomationExecuteRequestSchema,
BrowserAutomationExecuteResponseSchema,
} from "./browser-automation/rpc-schemas.js";
import { BrowserAutomationHostCapabilitySchema } from "./browser-automation/capabilities.js";
import {
PaseoConfigRawSchema,
PaseoLifecycleCommandRawSchema,
PaseoMetadataGenerationEntrySchema,
PaseoMetadataGenerationSchema,
PaseoScriptEntryRawSchema,
PaseoWorktreeConfigRawSchema,
PaseoConfigRevisionSchema,
ProjectConfigRpcErrorSchema,
type PaseoConfigRaw,
type PaseoConfigRevision,
type PaseoMetadataGeneration,
type PaseoMetadataGenerationEntry,
type PaseoScriptEntryRaw,
type ProjectConfigRpcError,
} from "./paseo-config-schema.js";
export {
PaseoConfigRawSchema,
PaseoLifecycleCommandRawSchema,
PaseoMetadataGenerationEntrySchema,
PaseoMetadataGenerationSchema,
PaseoScriptEntryRawSchema,
PaseoWorktreeConfigRawSchema,
type PaseoConfigRaw,
type PaseoConfigRevision,
type PaseoMetadataGeneration,
type PaseoMetadataGenerationEntry,
type PaseoScriptEntryRaw,
type ProjectConfigRpcError,
};
// ---------------------------------------------------------------------------
// Mutable daemon config schemas (shared between server store and client)
// ---------------------------------------------------------------------------
export const DAEMON_PERMISSIONS = [
"daemon.read",
"daemon.manage",
"tunnel.manage",
"access.manage",
"workspace.read",
"workspace.write",
"workspace.manage",
"automation.manage",
"hub.execute",
] as const;
export const DaemonPermissionSchema = z.enum(DAEMON_PERMISSIONS);
export type DaemonPermission = z.infer<typeof DaemonPermissionSchema>;
const MutableDaemonProviderModelSchema = z
.object({
id: z.string().min(1),
label: z.string().min(1),
description: z.string().optional(),
isDefault: z.boolean().optional(),
})
.passthrough();
const MutableDaemonProviderConfigSchema = z
.object({
paseoTools: ProviderPaseoToolsPolicySchema.optional(),
enabled: z.boolean().optional(),
additionalModels: z.array(MutableDaemonProviderModelSchema).optional(),
})
.passthrough();
const MutableStructuredGenerationProviderSchema = z
.object({
provider: z.string().min(1),
model: z.string().min(1).optional(),
thinkingOptionId: z.string().min(1).optional(),
})
.passthrough();
const MutableMetadataGenerationConfigSchema = z
.object({
providers: z.array(MutableStructuredGenerationProviderSchema).default([]),
})
.passthrough();
const MutableBrowserToolsConfigSchema = z
.object({
enabled: z.boolean().default(false),
})
.passthrough();
const MutableRelayConfigSchema = z
.object({
enabled: z.boolean(),
})
.passthrough();
export const MutableDaemonConfigSchema = z
.object({
// COMPAT(relayConfig): added in v0.2.6, remove after 2027-01-31 when old daemons are unsupported.
relay: MutableRelayConfigSchema.optional(),
mcp: z
.object({
enabled: z.boolean().optional(),
injectIntoAgents: z.boolean(),
})
.passthrough(),
hostnames: z.union([z.literal(true), z.array(z.string())]).optional(),
cors: z
.object({
allowedOrigins: z.array(z.string()),
})
.passthrough()
.optional(),
trustedProxies: z.union([z.literal(true), z.array(z.string())]).optional(),
git: z
.object({
maxProcessesPerSecond: z.number().int().positive(),
maxProcessConcurrency: z.number().int().positive(),
})
.optional(),
app: z.object({ baseUrl: z.string() }).optional(),
catalogRefreshTimeoutMs: z.number().int().positive().optional(),
browserTools: MutableBrowserToolsConfigSchema.default({ enabled: false }),
providers: z.record(z.string(), MutableDaemonProviderConfigSchema).default({}),
metadataGeneration: MutableMetadataGenerationConfigSchema.default({ providers: [] }),
autoArchiveAfterMerge: z.boolean().default(false),
enableTerminalAgentHooks: z.boolean().default(false),
appendSystemPrompt: z.string().default(""),
terminalProfiles: z.array(TerminalProfileSchema).optional(),
agentProfiles: z.array(AgentProfileSchema).optional(),
skills: z.object({ selection: AgentSkillSelectionSchema.optional() }).strict().optional(),
pluginsEnabled: z.boolean().optional(),
plugins: z.record(PluginIdSchema, PluginSourceSchema).optional(),
})
.passthrough();
export const MutableDaemonConfigPatchSchema = z
.object({
relay: MutableRelayConfigSchema.partial().optional(),
mcp: z.object({ injectIntoAgents: z.boolean().optional() }).passthrough().optional(),
browserTools: MutableBrowserToolsConfigSchema.partial().optional(),
providers: z
.record(z.string(), MutableDaemonProviderConfigSchema.partial().passthrough())
.optional(),
removeProviders: z.array(z.string().min(1)).optional(),
metadataGeneration: MutableMetadataGenerationConfigSchema.partial().optional(),
autoArchiveAfterMerge: z.boolean().optional(),
enableTerminalAgentHooks: z.boolean().optional(),
appendSystemPrompt: z.string().optional(),
terminalProfiles: z.array(TerminalProfileSchema).optional(),
agentProfiles: z.array(AgentProfileSchema).optional(),
pluginsEnabled: z.boolean().optional(),
plugins: z.record(PluginIdSchema, PluginSourceSchema).optional(),
})
.partial()
.passthrough();
export type MutableDaemonConfig = z.infer<typeof MutableDaemonConfigSchema>;
export type MutableDaemonConfigPatch = z.infer<typeof MutableDaemonConfigPatchSchema>;
import type {
AgentCapabilityFlags,
AgentModelDefinition,
AgentMode,
AgentPermissionRequest,
AgentPermissionResponse,
AgentPersistenceHandle,
ProviderStatus,
AgentRuntimeInfo,
AgentTimelineItem,
AgentProviderNotice,
ToolCallDetail,
ToolCallTimelineItem,
AgentUsage,
JsonValue,
} from "./agent-types.js";
// WebSocket payloads have already crossed JSON serialization. Keeping this as
// unknown avoids zod-aot's recursive z.json() object-codegen regression.
const JsonWireValueSchema = z.unknown() as z.ZodType<JsonValue>;
export const AgentStatusSchema = z.enum(AGENT_LIFECYCLE_STATUSES);
const AgentModeSchema: z.ZodType<AgentMode> = z.object({
id: z.string(),
label: z.string(),
description: z.string().optional(),
icon: z.string().optional(),
colorTier: z.string().optional(),
});
const ProviderStatusSchema: z.ZodType<ProviderStatus> = z.enum([
"ready",
"loading",
"error",
"unavailable",
]);
const AgentSelectOptionSchema = z.object({
id: z.string(),
label: z.string(),
description: z.string().optional(),
isDefault: z.boolean().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
});
const AgentProviderNoticeSchema: z.ZodType<AgentProviderNotice> = z.discriminatedUnion("type", [
z.object({ type: z.literal("info"), message: z.string() }),
z.object({ type: z.literal("warning"), message: z.string() }),
z.object({ type: z.literal("error"), message: z.string() }),
]);
export const AgentFeatureToggleSchema = z.object({
type: z.literal("toggle"),
id: z.string(),
label: z.string(),
description: z.string().optional(),
tooltip: z.string().optional(),
icon: z.string().optional(),
value: z.boolean(),
});
export const AgentFeatureSelectSchema = z.object({
type: z.literal("select"),
id: z.string(),
label: z.string(),
description: z.string().optional(),
tooltip: z.string().optional(),
icon: z.string().optional(),
desktopTrigger: z.enum(["icon", "label"]).optional(),
value: z.string().nullable(),
options: z.array(AgentSelectOptionSchema),
});
export const AgentFeatureSchema = z.discriminatedUnion("type", [
AgentFeatureToggleSchema,
AgentFeatureSelectSchema,
]);
const AgentModelDefinitionSchema = z.object({
provider: AgentProviderSchema,
id: z.string(),
aliases: z.array(z.string()).optional(),
isSelectable: z.boolean().optional(),
label: z.string(),
description: z.string().optional(),
isDefault: z.boolean().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
contextWindowMaxTokens: z.number().optional(),
thinkingOptions: z.array(AgentSelectOptionSchema).optional(),
defaultThinkingOptionId: z.string().optional(),
}) satisfies z.ZodType<AgentModelDefinition>;
export const ProviderSnapshotEntrySchema = z.object({
provider: AgentProviderSchema,
status: ProviderStatusSchema,
enabled: z.boolean().optional().default(true),
source: z.enum(["builtin", "custom"]).optional(),
error: z.string().optional(),
models: z.array(AgentModelDefinitionSchema).optional(),
modes: z.array(AgentModeSchema).optional(),
fetchedAt: z.string().optional(),
label: z.string().optional(),
description: z.string().optional(),
iconSvg: z.string().optional(),
defaultModeId: z.string().nullable().optional(),
});
export const CompactProviderSnapshotModelSchema = AgentModelDefinitionSchema.omit({
provider: true,
thinkingOptions: true,
}).extend({
thinkingSet: z.number().int().nonnegative().optional(),
});
export const ProviderSnapshotThinkingSetSchema = z.object({
options: z.array(AgentSelectOptionSchema),
defaultOptionId: z.string().optional(),
});
export const CompactProviderSnapshotEntrySchema = ProviderSnapshotEntrySchema.omit({
models: true,
}).extend({
models: z.array(CompactProviderSnapshotModelSchema).optional(),
});
export const CompactProviderSnapshotSchema = z.object({
entries: z.array(CompactProviderSnapshotEntrySchema),
thinkingSets: z.array(ProviderSnapshotThinkingSetSchema),
});
const AgentCapabilityFlagsSchema: z.ZodType<AgentCapabilityFlags> = z
.object({
supportsStreaming: z.boolean(),
supportsSessionPersistence: z.boolean(),
supportsSessionListing: z.boolean().optional(),
supportsDynamicModes: z.boolean(),
supportsMcpServers: z.boolean(),
supportsReasoningStream: z.boolean(),
supportsToolInvocations: z.boolean(),
// COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X.
supportsRewindConversation: z.boolean().optional().default(false),
// COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X.
supportsRewindFiles: z.boolean().optional().default(false),
// COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X.
supportsRewindBoth: z.boolean().optional().default(false),
})
.catchall(z.boolean());
const AgentUsageSchema: z.ZodType<AgentUsage> = z.object({
inputTokens: z.number().optional(),
cachedInputTokens: z.number().optional(),
outputTokens: z.number().optional(),
totalCostUsd: z.number().optional(),
contextWindowMaxTokens: z.number().optional(),
contextWindowUsedTokens: z.number().optional(),
});
const McpStdioServerConfigSchema = z.object({
type: z.literal("stdio"),
command: z.string(),
args: z.array(z.string()).optional(),
env: z.record(z.string(), z.string()).optional(),
alwaysLoad: z.boolean().optional(),
});
const McpHttpServerConfigSchema = z.object({
type: z.literal("http"),
url: z.string(),
headers: z.record(z.string(), z.string()).optional(),
alwaysLoad: z.boolean().optional(),
});
const McpSseServerConfigSchema = z.object({
type: z.literal("sse"),
url: z.string(),
headers: z.record(z.string(), z.string()).optional(),
alwaysLoad: z.boolean().optional(),
});
const McpServerConfigSchema = z.discriminatedUnion("type", [
McpStdioServerConfigSchema,
McpHttpServerConfigSchema,
McpSseServerConfigSchema,
]);
const ProviderOptionsSchema = z.record(z.string(), z.unknown());
const McpToolRefSchema = z
.object({
kind: z.literal("mcp"),
server: z.string().trim().min(1),
tool: z.string().trim().min(1),
})
.strict();
const ToolPolicySchema = z
.object({
preapproved: z.array(McpToolRefSchema),
})
.strict();
const AgentSessionConfigSchema = z.object({
provider: AgentProviderSchema,
cwd: z.string(),
modeId: z.string().optional(),
model: z.string().optional(),
thinkingOptionId: z.string().optional(),
featureValues: z.record(z.string(), z.unknown()).optional(),
title: z.string().trim().min(1).max(MAX_EXPLICIT_AGENT_TITLE_CHARS).optional().nullable(),
providerOptions: ProviderOptionsSchema.optional(),
toolPolicy: ToolPolicySchema.optional(),
systemPrompt: z.string().optional(),
mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
});
const AgentPermissionUpdateSchema = z.record(z.string(), z.unknown());
const AgentPermissionActionSchema = z.object({
id: z.string(),
label: z.string(),
behavior: z.enum(["allow", "deny"]),
variant: z.enum(["primary", "secondary", "danger"]).optional(),
intent: z.enum(["implement", "implement_resume", "dismiss"]).optional(),
});
export const AgentPermissionResponseSchema: z.ZodType<AgentPermissionResponse> =
z.discriminatedUnion("behavior", [
z.object({
behavior: z.literal("allow"),
selectedActionId: z.string().optional(),
updatedInput: z.record(z.string(), z.unknown()).optional(),
updatedPermissions: z.array(AgentPermissionUpdateSchema).optional(),
}),
z.object({
behavior: z.literal("deny"),
selectedActionId: z.string().optional(),
message: z.string().optional(),
interrupt: z.boolean().optional(),
}),
]);
export const AgentPermissionRequestPayloadSchema: z.ZodType<AgentPermissionRequest, unknown> =
z.object({
id: z.string(),
provider: AgentProviderSchema,
name: z.string(),
kind: z.enum(["tool", "plan", "question", "mode", "other"]),
title: z.string().optional(),
description: z.string().optional(),
input: z.record(z.string(), z.unknown()).optional(),
detail: z.lazy(() => ToolCallDetailPayloadSchema).optional(),
suggestions: z.array(AgentPermissionUpdateSchema).optional(),
actions: z.array(AgentPermissionActionSchema).optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
});
const UnknownValueSchema = z.union([
z.null(),
z.boolean(),
z.number(),
z.string(),
z.array(z.unknown()),
z.object({}).passthrough(),
]);
const NonNullUnknownSchema = z.union([
z.boolean(),
z.number(),
z.string(),
z.array(z.unknown()),
z.object({}).passthrough(),
]);
const WorktreeSetupCommandSnapshotSchema = z.object({
index: z.number().int().positive(),
command: z.string(),
cwd: z.string(),
log: z.string().optional().default(""),
status: z.enum(["running", "completed", "failed"]),
exitCode: z.number().nullable(),
durationMs: z.number().nonnegative().optional(),
});
const WorktreeSetupDetailPayloadSchema = z.object({
type: z.literal("worktree_setup"),
worktreePath: z.string(),
branchName: z.string(),
log: z.string(),
commands: z.array(WorktreeSetupCommandSnapshotSchema),
truncated: z.boolean().optional(),
});
const ToolCallDetailPayloadSchema: z.ZodType<ToolCallDetail, unknown> = z.discriminatedUnion(
"type",
[
WorktreeSetupDetailPayloadSchema,
z.object({
type: z.literal("shell"),
command: z.string(),
cwd: z.string().optional(),
output: z.string().optional(),
exitCode: z.number().nullable().optional(),
}),
z.object({
type: z.literal("read"),
filePath: z.string(),
content: z.string().optional(),
offset: z.number().optional(),
limit: z.number().optional(),
}),
z.object({
type: z.literal("edit"),
filePath: z.string(),
oldString: z.string().optional(),
newString: z.string().optional(),
unifiedDiff: z.string().optional(),
}),
z.object({
type: z.literal("write"),
filePath: z.string(),
content: z.string().optional(),
}),
z.object({
type: z.literal("search"),
query: z.string(),
toolName: z.enum(["search", "grep", "glob", "web_search"]).optional(),
content: z.string().optional(),
filePaths: z.array(z.string()).optional(),
webResults: z
.array(
z.object({
title: z.string(),
url: z.string(),
}),
)
.optional(),
annotations: z.array(z.string()).optional(),
numFiles: z.number().optional(),
numMatches: z.number().optional(),
durationMs: z.number().optional(),
durationSeconds: z.number().optional(),
truncated: z.boolean().optional(),
mode: z.enum(["content", "files_with_matches", "count"]).optional(),
}),
z.object({
type: z.literal("fetch"),
url: z.string(),
prompt: z.string().optional(),
result: z.string().optional(),
code: z.number().optional(),
codeText: z.string().optional(),
bytes: z.number().optional(),
durationMs: z.number().optional(),
}),
z.object({
type: z.literal("sub_agent"),
subAgentType: z.string().optional(),
description: z.string().optional(),
childSessionId: z.string().optional(),
log: z.string(),
// Compat cruft for clients <= 0.1.65-beta.3 that required this field. Producers still
// emit `[]`; nothing reads it. Drop the field (and the `[]` emissions) once those
// clients are no longer in the field.
actions: z
.array(
z.object({
index: z.number().int().positive(),
toolName: z.string(),
summary: z.string().optional(),
}),
)
.optional(),
}),
z.object({
type: z.literal("plain_text"),
label: z.string().optional(),
text: z.string().optional(),
icon: z.enum(TOOL_CALL_ICON_NAMES).optional(),
}),
z.object({
type: z.literal("plan"),
text: z.string(),
}),
z.object({
type: z.literal("unknown"),
input: UnknownValueSchema,
output: UnknownValueSchema,
}),
],
);
const ToolCallBasePayloadSchema = z.object({
agentMessage: AgentMessageSchema.optional(),
type: z.literal("tool_call"),
callId: z.string(),
name: z.string(),
detail: ToolCallDetailPayloadSchema,
metadata: z.record(z.string(), z.unknown()).optional(),
});
const ToolCallRunningPayloadSchema = ToolCallBasePayloadSchema.extend({
status: z.literal("running"),
error: z.null(),
});
const ToolCallCompletedPayloadSchema = ToolCallBasePayloadSchema.extend({
status: z.literal("completed"),
error: z.null(),
});
const ToolCallFailedPayloadSchema = ToolCallBasePayloadSchema.extend({
status: z.literal("failed"),
error: NonNullUnknownSchema,
});
const ToolCallCanceledPayloadSchema = ToolCallBasePayloadSchema.extend({
status: z.literal("canceled"),
error: z.null(),
});
const ToolCallTimelineItemPayloadSchema: z.ZodType<ToolCallTimelineItem, unknown> =
z.discriminatedUnion("status", [
ToolCallRunningPayloadSchema,
ToolCallCompletedPayloadSchema,
ToolCallFailedPayloadSchema,
ToolCallCanceledPayloadSchema,
]);
// zod-aot 0.20.4 miscompiles this as a nested discriminated union by omitting
// the inner tool_call branch from the generated outer dispatch.
export const AgentTimelineItemPayloadSchema: z.ZodType<AgentTimelineItem, unknown> = z.union([
z.object({
type: z.literal("user_message"),
text: z.string(),
messageId: z.string().optional(),
clientMessageId: z.string().optional(),
}),
z.object({
type: z.literal("assistant_message"),
text: z.string(),
messageId: z.string().optional(),
}),
z.object({
type: z.literal("reasoning"),
text: z.string(),
}),
ToolCallTimelineItemPayloadSchema,
z.object({
type: z.literal("todo"),
items: z.array(
z.object({
text: z.string(),
completed: z.boolean(),
id: z.string().optional(),
status: z.enum(["pending", "in_progress", "completed"]).optional(),
activeForm: z.string().optional(),
}),
),
}),
z.object({
type: z.literal("error"),
message: z.string(),
}),
z.object({
type: z.literal("notification"),
level: z.enum(["info", "warning", "error"]),
message: z.string(),
}),
z.object({
type: z.literal("compaction"),
status: z.enum(["loading", "completed"]),
trigger: z.enum(["auto", "manual"]).optional(),
preTokens: z.number().optional(),
}),
z.object({
type: z.literal("plugin"),
id: z.string(),
pluginId: PluginIdSchema,
kind: z.string(),
version: z.number(),
data: JsonWireValueSchema,
}),
]);
export const AgentStreamEventPayloadSchema = z.discriminatedUnion("type", [
z.object({
type: z.literal("thread_started"),
sessionId: z.string(),
provider: AgentProviderSchema,
}),
z.object({
type: z.literal("turn_started"),
provider: AgentProviderSchema,
turnId: z.string().optional(),
}),
z.object({
type: z.literal("turn_completed"),
provider: AgentProviderSchema,
turnId: z.string().optional(),
usage: AgentUsageSchema.optional(),
}),
z.object({
type: z.literal("turn_failed"),
provider: AgentProviderSchema,
turnId: z.string().optional(),
error: z.string(),
code: z.string().optional(),
diagnostic: z.string().optional(),
}),
z.object({
type: z.literal("turn_canceled"),
provider: AgentProviderSchema,
turnId: z.string().optional(),
reason: z.string(),
}),
z.object({
type: z.literal("timeline"),
provider: AgentProviderSchema,
item: AgentTimelineItemPayloadSchema,
turnId: z.string().optional(),
}),
z.object({
type: z.literal("permission_requested"),
provider: AgentProviderSchema,
request: AgentPermissionRequestPayloadSchema,
}),
z.object({
type: z.literal("permission_resolved"),
provider: AgentProviderSchema,
requestId: z.string(),
resolution: AgentPermissionResponseSchema,
}),
z.object({
type: z.literal("attention_required"),
provider: AgentProviderSchema,
reason: z.enum(["finished", "error", "permission"]),
timestamp: z.string(),
shouldNotify: z.boolean(),
notification: z
.object({
title: z.string(),
body: z.string(),
data: z.object({
serverId: z.string(),
workspaceId: z.string().optional(),
agentId: z.string(),
reason: z.enum(["finished", "error", "permission"]),
}),
})
.optional(),
}),
]);
const AgentPersistenceHandleSchema: z.ZodType<AgentPersistenceHandle | null> = z
.object({
provider: AgentProviderSchema,
sessionId: z.string(),
nativeHandle: z.string().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
.nullable();
const AgentRuntimeInfoSchema: z.ZodType<AgentRuntimeInfo> = z.object({
provider: AgentProviderSchema,
sessionId: z.string().nullable(),
model: z.string().nullable().optional(),
thinkingOptionId: z.string().nullable().optional(),
modeId: z.string().nullable().optional(),
extra: z.record(z.string(), z.unknown()).optional(),
});
const AgentActiveTurnPayloadSchema = z.object({
turnId: z.string(),
startedAt: z.string().nullable(),
});
export const AgentSnapshotPayloadSchema = z.object({
id: z.string(),
provider: AgentProviderSchema,
cwd: z.string(),
workspaceId: z.string().optional(),
model: z.string().nullable(),
features: z.array(AgentFeatureSchema).optional(),
thinkingOptionId: z.string().nullable().optional(),
effectiveThinkingOptionId: z.string().nullable().optional(),
createdAt: z.string(),
updatedAt: z.string(),
lastUserMessageAt: z.string().nullable(),
status: AgentStatusSchema,
activeTurn: AgentActiveTurnPayloadSchema.nullable().optional(),
capabilities: AgentCapabilityFlagsSchema,
currentModeId: z.string().nullable(),
availableModes: z.array(AgentModeSchema),
pendingPermissions: z.array(AgentPermissionRequestPayloadSchema),
persistence: AgentPersistenceHandleSchema.nullable(),
runtimeInfo: AgentRuntimeInfoSchema.optional(),
lastUsage: AgentUsageSchema.optional(),
lastError: z.string().optional(),
title: z.string().nullable(),
labels: z.record(z.string(), z.string()).default({}),
requiresAttention: z.boolean().optional(),
attentionReason: z.enum(["finished", "error", "permission"]).nullable().optional(),
attentionTimestamp: z.string().nullable().optional(),
archivedAt: z.string().nullable().optional(),
providerUnavailable: z.boolean().optional(),
});
export type AgentSnapshotPayload = z.infer<typeof AgentSnapshotPayloadSchema>;
export const AgentListItemPayloadSchema = z.object({
id: z.string(),
shortId: z.string(),
title: z.string().nullable(),
provider: AgentProviderSchema,
model: z.string().nullable(),
thinkingOptionId: z.string().nullable().optional(),
effectiveThinkingOptionId: z.string().nullable().optional(),
status: AgentStatusSchema,
cwd: z.string(),
createdAt: z.string(),
updatedAt: z.string(),
lastUserMessageAt: z.string().nullable(),
archivedAt: z.string().nullable().optional(),
requiresAttention: z.boolean().optional(),
attentionReason: z.enum(["finished", "error", "permission"]).nullable().optional(),
attentionTimestamp: z.string().nullable().optional(),
labels: z.record(z.string(), z.string()).default({}),
providerUnavailable: z.boolean().optional(),
});
export type AgentListItemPayload = z.infer<typeof AgentListItemPayloadSchema>;
export type AgentStreamEventPayload = z.infer<typeof AgentStreamEventPayloadSchema>;
export const RecentProviderSessionDescriptorPayloadSchema = z.object({
providerId: z.string(),
providerLabel: z.string(),
providerHandleId: z.string(),
cwd: z.string(),
title: z.string().nullable(),
firstPromptPreview: z.string().nullable(),
lastPromptPreview: z.string().nullable(),
lastActivityAt: z.string(),
});
export type RecentProviderSessionDescriptorPayload = z.infer<
typeof RecentProviderSessionDescriptorPayloadSchema
>;
// ============================================================================
// Session Inbound Messages (Session receives these)
// ============================================================================
export const VoiceAudioChunkMessageSchema = z.object({
type: z.literal("voice_audio_chunk"),
audio: z.string(), // base64 encoded
format: z.string(),
isLast: z.boolean(),
});
export const AbortRequestMessageSchema = z.object({
type: z.literal("abort_request"),
});
export const AudioPlayedMessageSchema = z.object({
type: z.literal("audio_played"),
id: z.string(),
});
const AgentDirectoryFilterSchema = z.object({
labels: z.record(z.string(), z.string()).optional(),
projectKeys: z.array(z.string()).optional(),
statuses: z.array(AgentStatusSchema).optional(),
includeArchived: z.boolean().optional(),
requiresAttention: z.boolean().optional(),
thinkingOptionId: z.string().nullable().optional(),
});
export const DeleteAgentRequestMessageSchema = z.object({
type: z.literal("delete_agent_request"),
agentId: z.string(),
requestId: z.string(),
});
export const ArchiveAgentRequestMessageSchema = z.object({
type: z.literal("archive_agent_request"),
agentId: z.string(),
requestId: z.string(),
});
export const CloseItemsRequestMessageSchema = z.object({
type: z.literal("close_items_request"),
agentIds: z.array(z.string()).default([]),
terminalIds: z.array(z.string()).default([]),
requestId: z.string(),
});
export const UpdateAgentRequestMessageSchema = z.object({
type: z.literal("update_agent_request"),
agentId: z.string(),
name: z.string().optional(),
labels: z.record(z.string(), z.string()).optional(),
requestId: z.string(),
});
// The daemon accepts only image bytes chosen or acquired by the client. It must
// never fetch a user-provided URL on the host's network.
export const ProjectIconSourceSchema = z.discriminatedUnion("type", [
z.object({ type: z.literal("automatic") }),
z.object({ type: z.literal("upload"), data: z.string() }),
]);
export const ProjectRenameRequestSchema = z.object({
type: z.literal("project.rename.request"),
projectId: z.string(),
// Null or empty string clears the override and reverts to the derived name.
customName: z.string().nullable(),
requestId: z.string(),
});
export const ProjectIconSetRequestSchema = z.object({
type: z.literal("project.icon.set.request"),
projectId: z.string(),
source: ProjectIconSourceSchema,
requestId: z.string(),
});
export const ProjectRemoveRequestSchema = z.object({
type: z.literal("project.remove.request"),
projectId: z.string(),
requestId: z.string(),
});
export const WorkspaceTitleSetRequestSchema = z.object({
type: z.literal("workspace.title.set.request"),
workspaceId: z.string(),
// Null or empty string clears the title and reverts to the derived name.
title: z.string().nullable(),
requestId: z.string(),
});
export const WorkspacePinSetRequestSchema = z.object({
type: z.literal("workspace.pin.set.request"),
workspaceId: z.string(),
pinned: z.boolean(),
requestId: z.string(),
});
export const WorkspaceLabelColorSchema = z.enum(WORKSPACE_LABEL_COLORS);
export const WorkspaceLabelDefinitionSchema = z.object({
name: z.string(),
color: WorkspaceLabelColorSchema,
});
const WorkspaceLabelSyncCursorSchema = z.object({
generation: z.string(),
afterSeq: z.number().int().nonnegative(),
});
export const WorkspaceLabelListRequestSchema = z.object({
type: z.literal("workspace.label.list.request"),
requestId: z.string(),
subscribe: z.object({ subscriptionId: z.string().optional() }).optional(),
sync: WorkspaceLabelSyncCursorSchema.optional(),
});
export const WorkspaceLabelAssignmentSetRequestSchema = z.object({
type: z.literal("workspace.label.assignment.set.request"),
requestId: z.string(),
workspaceId: z.string(),
label: WorkspaceLabelDefinitionSchema,
assigned: z.boolean(),
});
/**
* Editing a label is one operation. A name and a colour are two fields of one thing, and two
* RPCs can land half-applied — leaving the catalog in a state the user never asked for and the
* UI with nothing true to say. Both fields are optional; omitting one leaves it alone.
*/
export const WorkspaceLabelUpdateRequestSchema = z.object({
type: z.literal("workspace.label.update.request"),
requestId: z.string(),
name: z.string(),
newName: z.string().optional(),
color: WorkspaceLabelColorSchema.optional(),
});
export const WorkspaceLabelDeleteRequestSchema = z.object({
type: z.literal("workspace.label.delete.request"),
requestId: z.string(),
name: z.string(),
});
export const WorkspaceLabelDeleteInspectRequestSchema = z.object({
type: z.literal("workspace.label.delete.inspect.request"),
requestId: z.string(),
name: z.string(),
});
export const WorkspaceRecoveryInspectRequestSchema = z.object({
type: z.literal("workspace.recovery.inspect.request"),
workspaceId: z.string(),
requestId: z.string(),
});
export const WorkspaceRecoveryRestoreRequestSchema = z.object({
type: z.literal("workspace.recovery.restore.request"),
workspaceId: z.string(),
requestId: z.string(),
});
export const SetVoiceModeMessageSchema = z.object({
type: z.literal("set_voice_mode"),
enabled: z.boolean(),
agentId: z.string().optional(),
requestId: z.string().optional(),
});
// COMPAT(githubAttachmentKinds): legacy wire attachment retained when
// forge-neutral attachments shipped in v0.2.0-beta.1. Stop emitting it after
// 2027-01-17 once supported client and daemon floors are >= v0.2.0.
export const GitHubPrAttachmentSchema = z.object({
type: z.literal("github_pr"),
mimeType: z.literal("application/github-pr"),
number: z.number().int().positive(),
title: z.string(),
url: z.string(),
body: z.string().nullable().optional(),
baseRefName: z.string().nullable().optional(),
headRefName: z.string().nullable().optional(),
});
export const ForgeChangeRequestAttachmentSchema = z.object({
type: z.literal("forge_change_request"),
mimeType: z.literal("application/paseo-forge-change-request"),
forge: z.string().optional().default("github"),
number: z.number().int().positive(),
title: z.string(),
url: z.string(),
body: z.string().nullable().optional(),
projectPath: z.string().optional(),
baseRefName: z.string().nullable().optional(),
headRefName: z.string().nullable().optional(),
});
// COMPAT(githubAttachmentKinds): legacy wire attachment retained when
// forge-neutral attachments shipped in v0.2.0-beta.1. Stop emitting it after
// 2027-01-17 once supported client and daemon floors are >= v0.2.0.
export const GitHubIssueAttachmentSchema = z.object({
type: z.literal("github_issue"),
mimeType: z.literal("application/github-issue"),
number: z.number().int().positive(),
title: z.string(),
url: z.string(),
body: z.string().nullable().optional(),
});
export const ForgeIssueAttachmentSchema = z.object({
type: z.literal("forge_issue"),
mimeType: z.literal("application/paseo-forge-issue"),
forge: z.string().optional().default("github"),
number: z.number().int().positive(),
title: z.string(),
url: z.string(),
body: z.string().nullable().optional(),
projectPath: z.string().optional(),
});
export const ExternalResourceAttachmentMetadataSchema = z.object({
provider: z.string(),
providerLabel: z.string(),
resourceType: z.string(),
id: z.string(),
identifier: z.string(),
title: z.string(),
url: z.string(),
});
export const TextAttachmentSchema = z
.object({
type: z.literal("text"),
mimeType: z.literal("text/plain"),
contextKind: z.string().optional(),
title: z.string().nullable().optional(),
text: z.string(),
externalResource: ExternalResourceAttachmentMetadataSchema.optional(),
})
.transform(({ contextKind, ...attachment }) => ({
...attachment,
...(contextKind === "chat_history" ? { contextKind } : {}),
}));
export const ReviewAttachmentContextLineSchema = z.object({
oldLineNumber: z.number().int().positive().nullable(),
newLineNumber: z.number().int().positive().nullable(),
type: z.enum(["add", "remove", "context"]),
content: z.string(),
});
export const ReviewAttachmentCommentSchema = z.object({
filePath: z.string(),
side: z.enum(["old", "new"]),
lineNumber: z.number().int().positive(),
body: z.string(),
context: z.object({
hunkHeader: z.string(),
targetLine: ReviewAttachmentContextLineSchema,
lines: z.array(ReviewAttachmentContextLineSchema),
}),
});
export const ReviewAttachmentSchema = z.object({
type: z.literal("review"),
mimeType: z.literal("application/paseo-review"),
cwd: z.string(),
mode: z.enum(["uncommitted", "base"]),
baseRef: z.string().nullable().optional(),
comments: z.array(ReviewAttachmentCommentSchema),
});
export const UploadedFileAttachmentSchema = z.object({
type: z.literal("uploaded_file"),
id: z.string(),
fileName: z.string(),
mimeType: z.string(),
size: z.number().int().nonnegative(),
path: z.string(),
});
export const AgentAttachmentSchema = z.discriminatedUnion("type", [
ForgeChangeRequestAttachmentSchema,
ForgeIssueAttachmentSchema,
GitHubPrAttachmentSchema,
GitHubIssueAttachmentSchema,
TextAttachmentSchema,
ReviewAttachmentSchema,
UploadedFileAttachmentSchema,
]);
function normalizeAgentAttachments(input: unknown): AgentAttachment[] {
if (!Array.isArray(input)) {
return [];
}
const normalized: AgentAttachment[] = [];
for (const item of input) {
const parsed = AgentAttachmentSchema.safeParse(item);
if (parsed.success) {
normalized.push(parsed.data);
}
}
return normalized;
}
const AgentAttachmentsSchema = z.unknown().transform(normalizeAgentAttachments).optional();
export const ChangeRequestCheckoutSourceSchema = z.object({
kind: z.literal("change_request"),
forge: z.string().optional(),
number: z.number().int().positive(),
projectPath: z.string().optional(),
});
const ImageAttachmentSchema = z.object({
data: z.string(), // base64 encoded image
mimeType: z.string(), // e.g., "image/jpeg", "image/png"
});
export const ActiveTurnBehaviorSchema = z.enum(["interrupt", "steer"]);
export type ActiveTurnBehavior = z.infer<typeof ActiveTurnBehaviorSchema>;
export const SendAgentMessageSchema = z.object({
type: z.literal("send_agent_message"),
agentId: z.string(),
text: z.string(),
messageId: z.string().optional(), // Client-provided ID for deduplication
activeTurnBehavior: ActiveTurnBehaviorSchema.optional(),
images: z.array(ImageAttachmentSchema).optional(),
attachments: AgentAttachmentsSchema,
});
// ============================================================================
// Agent RPCs (requestId-correlated)
// ============================================================================
const DirectorySyncRequestSchema = z.object({
generation: z.string().optional(),
afterSeq: z.number().int().nonnegative().optional(),
});
const DirectorySyncRemovalSchema = z.object({
id: z.string(),
seq: z.number().int().positive(),
});
const DirectorySyncMetadataSchema = z.object({
generation: z.string(),
headSeq: z.number().int().nonnegative(),
mode: z.enum(["snapshot", "changes"]),
reason: z
.enum(["no_cursor", "generation_changed", "cursor_expired", "changes_too_large"])
.optional(),
removals: z.array(DirectorySyncRemovalSchema),
});
export const FetchAgentsRequestMessageSchema = z.object({
type: z.literal("fetch_agents_request"),
requestId: z.string(),
scope: z.enum(["active"]).optional(),
filter: AgentDirectoryFilterSchema.optional(),
sort: z
.array(
z.object({
key: z.enum(["status_priority", "created_at", "updated_at", "title"]),
direction: z.enum(["asc", "desc"]),
}),
)
.optional(),
page: z
.object({
limit: z.number().int().positive().max(200),
cursor: z.string().min(1).optional(),
})
.optional(),
subscribe: z
.object({
subscriptionId: z.string().optional(),
})
.optional(),
// COMPAT(directorySync): added in v0.3.x, remove optional after 2027-02-12.
sync: DirectorySyncRequestSchema.optional(),
});
const WorkspaceStateBucketSchema = z.enum([
"needs_input",
"failed",
"running",
"attention",
"done",
]);
export const FetchWorkspacesRequestMessageSchema = z.object({
type: z.literal("fetch_workspaces_request"),
requestId: z.string(),
filter: z
.object({
query: z.string().optional(),
projectId: z.string().optional(),
// Unused: accepted so older clients still parse, but the server does not filter on it.
idPrefix: z.string().optional(),
})
.optional(),
sort: z
.array(
z.object({
key: z.enum(["status_priority", "activity_at", "name", "project_id"]),
direction: z.enum(["asc", "desc"]),
}),
)
.optional(),
page: z
.object({
limit: z.number().int().positive().max(200),
cursor: z.string().min(1).optional(),
})
.optional(),
subscribe: z
.object({
subscriptionId: z.string().optional(),
})
.optional(),
// COMPAT(directorySync): added in v0.3.x, remove optional after 2027-02-12.
sync: DirectorySyncRequestSchema.optional(),
});
export const ProjectListRequestMessageSchema = z.object({
type: z.literal("project.list.request"),
requestId: z.string(),
// COMPAT(directorySync): added in v0.3.x, remove optional after 2027-02-12.
sync: DirectorySyncRequestSchema.optional(),
});
export const FetchAgentHistoryRequestMessageSchema = z.object({
type: z.literal("fetch_agent_history_request"),
requestId: z.string(),
filter: AgentDirectoryFilterSchema.optional(),
// A free-text filter over agent title, workspace name, branch, and project name.
// Matching rows follow the requested sort and cursor pagination.
search: z.string().optional(),
sort: z
.array(
z.object({
key: z.enum(["status_priority", "created_at", "updated_at", "title"]),
direction: z.enum(["asc", "desc"]),
}),
)
.optional(),
page: z
.object({
limit: z.number().int().positive().max(200),
cursor: z.string().min(1).optional(),
})
.optional(),
});
export const FetchRecentProviderSessionsRequestMessageSchema = z.object({
type: z.literal("fetch_recent_provider_sessions_request"),
requestId: z.string(),
cwd: z.string().optional(),
providers: z.array(z.string()).optional(),
since: z.string().optional(),
limit: z.number().int().positive().max(200).optional(),
query: z.string().optional(),
});
export const FetchAgentRequestMessageSchema = z.object({
type: z.literal("fetch_agent_request"),
requestId: z.string(),
/** Accepts full ID, unique prefix, or exact full title (server resolves). */
agentId: z.string(),
});
export const SendAgentMessageRequestSchema = z.object({
type: z.literal("send_agent_message_request"),
requestId: z.string(),
/** Accepts full ID, unique prefix, or exact full title (server resolves). */
agentId: z.string(),
text: z.string(),
/** Opaque sender identity for agent-originated prompts. */
sourceAgentId: z.string().min(1).optional(),
messageId: z.string().optional(), // Client-provided ID for deduplication
activeTurnBehavior: ActiveTurnBehaviorSchema.optional(),
images: z.array(ImageAttachmentSchema).optional(),
attachments: AgentAttachmentsSchema,
});
export const WaitForFinishRequestSchema = z.object({
type: z.literal("wait_for_finish_request"),
requestId: z.string(),
/** Accepts full ID, unique prefix, or exact full title (server resolves). */
agentId: z.string(),
timeoutMs: z.number().int().positive().optional(),
});
export const DaemonGetStatusRequestSchema = z.object({
type: z.literal("daemon.get_status.request"),
requestId: z.string(),
});
export const DaemonGetPairingOfferRequestSchema = z.object({
type: z.literal("daemon.get_pairing_offer.request"),
requestId: z.string(),
});
export const DaemonConfigReloadRequestSchema = z.object({
type: z.literal("daemon.config.reload.request"),
requestId: z.string(),
});
export const HubManagementDaemonConnectRequestSchema = z.object({
type: z.literal("hub.management.daemon.connect.request"),
requestId: z.string(),
hubUrl: z.string(),
token: z.string(),
permissions: z.array(DaemonPermissionSchema).default([]),
});
export const HubManagementDaemonGetStatusRequestSchema = z.object({
type: z.literal("hub.management.daemon.get_status.request"),
requestId: z.string(),
});
export const HubManagementDaemonDisconnectRequestSchema = z.object({
type: z.literal("hub.management.daemon.disconnect.request"),
requestId: z.string(),
force: z.boolean().optional(),
});
export const HubManagementDaemonPermissionsUpdateRequestSchema = z.object({
type: z.literal("hub.management.daemon.permissions.update.request"),
requestId: z.string(),
grant: z.array(DaemonPermissionSchema).default([]),
revoke: z.array(DaemonPermissionSchema).default([]),
});
export const DiagnosticsRequestSchema = z.object({
type: z.literal("diagnostics.request"),
requestId: z.string(),
});
export const PluginCatalogGetRequestSchema = z.object({
type: z.literal("plugin.catalog.get.request"),
requestId: z.string(),
});
export const PluginListRequestSchema = z.object({
type: z.literal("plugin.list.request"),
requestId: z.string(),
});
export const PluginLogsGetRequestSchema = z.object({
type: z.literal("plugin.logs.get.request"),
requestId: z.string(),
pluginId: PluginIdSchema,
});
export const PluginDirectoryInstallRequestSchema = z.object({
type: z.literal("plugin.directory.install.request"),
requestId: z.string(),
path: z.string().min(1),
id: PluginIdSchema.optional(),
});
export const PluginDirectoryInspectRequestSchema = z.object({
type: z.literal("plugin.directory.inspect.request"),
requestId: z.string(),
path: z.string().min(1),
});
export const PluginSourceInstallRequestSchema = z.object({
type: z.literal("plugin.source.install.request"),
requestId: z.string(),
source: z.string().min(1),
id: PluginIdSchema.optional(),
ref: z.string().min(1).optional(),
// COMPAT(plugin-source-path): accepted for v0.7 clients; remove after 2027-09-01.
pluginPath: z.string().min(1).optional(),
});
export const PluginSourceIdentitySchema = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("directory"), path: z.string() }),
z.object({
kind: z.literal("git"),
remote: z.string(),
pluginPath: z.string(),
registry: PluginRegistryIdentitySchema.optional(),
}),
z.object({
kind: z.literal("npm"),
packageName: z.string(),
pluginPath: z.string(),
registry: PluginRegistryIdentitySchema.optional(),
}),
]);
export const PluginInstallationSchema = z.object({
identity: PluginSourceIdentitySchema,
currentRevision: z.string().optional(),
});
export type PluginInstallation = z.infer<typeof PluginInstallationSchema>;
export const PluginUpdateTargetSchema = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("git"), commit: z.string().regex(/^[0-9a-f]{40,64}$/) }),
z.object({
kind: z.literal("npm"),
version: z.string().min(1),
resolved: z.string().url(),
integrity: z.string().min(1),
}),
]);
export type PluginUpdateTarget = z.infer<typeof PluginUpdateTargetSchema>;
export const PluginUpdateProposalSchema = z.object({
id: PluginIdSchema,
expected: z.object({
identity: PluginSourceIdentitySchema,
installationRoot: z.string(),
revision: z.string(),
}),
target: PluginUpdateTargetSchema,
});
export type PluginUpdateProposal = z.infer<typeof PluginUpdateProposalSchema>;
export const PluginUpdateSelectionSchema = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("git"), ref: z.string().min(1) }),
z.object({ kind: z.literal("npm"), version: z.string().min(1) }),
]);
export type PluginUpdateSelection = z.infer<typeof PluginUpdateSelectionSchema>;
export const PluginUpdatePreviewRequestSchema = z.object({
type: z.literal("plugin.source.update.preview.request"),
requestId: z.string(),
pluginId: PluginIdSchema.optional(),
target: PluginUpdateSelectionSchema.optional(),
});
export const PluginUpdateApplyRequestSchema = z.object({
type: z.literal("plugin.source.update.apply.request"),
requestId: z.string(),
proposals: z.array(PluginUpdateProposalSchema).min(1),
});
export const PluginSourceStatusRequestSchema = z.object({
type: z.literal("plugin.source.status.request"),
requestId: z.string(),
pluginId: PluginIdSchema.optional(),
});
export const PluginSourceUpdateRequestSchema = z.object({
type: z.literal("plugin.source.update.request"),
requestId: z.string(),
pluginId: PluginIdSchema.optional(),
});
function pluginIdRequest<const Type extends string>(type: Type) {
return z.object({ type: z.literal(type), requestId: z.string(), pluginId: PluginIdSchema });
}
export const PluginReloadRequestSchema = pluginIdRequest("plugin.reload.request");
export const PluginEnableRequestSchema = pluginIdRequest("plugin.enable.request");
export const PluginDisableRequestSchema = pluginIdRequest("plugin.disable.request");
export const PluginRemoveRequestSchema = pluginIdRequest("plugin.remove.request");
export const PluginRpcInvokeRequestSchema = z.object({
type: z.literal("plugin.rpc.invoke.request"),
requestId: z.string(),
pluginId: PluginIdSchema,
method: z.string().min(1),
input: z.unknown(),
});
export const AgentTimelineAppendRequestSchema = z.object({
type: z.literal("agent.timeline.append.request"),
requestId: z.string(),
agentId: z.string(),
item: z.object({
type: z.literal("plugin"),
id: z.string(),
kind: z.string(),
version: z.number().int().positive(),
data: JsonWireValueSchema,
}),
});
export const AgentSkillOperationSchema = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("add"), name: z.string() }).strict(),
z.object({ kind: z.literal("update"), name: z.string() }).strict(),
z.object({ kind: z.literal("delete"), name: z.string() }).strict(),
]);
export type AgentSkillOperation = z.infer<typeof AgentSkillOperationSchema>;
export const AgentSkillsStatusSchema = z.object({
state: z.enum(["not-installed", "up-to-date", "drift"]),
ops: z.array(AgentSkillOperationSchema),
available: z.array(z.string()),
installed: z.array(z.string()),
selection: AgentSkillSelectionSchema,
});
export type AgentSkillsStatus = z.infer<typeof AgentSkillsStatusSchema>;
export const AgentSkillsConfirmationSchema = z.object({ removals: z.array(z.string()) }).strict();
export type AgentSkillsConfirmation = z.infer<typeof AgentSkillsConfirmationSchema>;
export const AgentSkillsSaveResultSchema = AgentSkillsStatusSchema.extend({
confirmationRequired: AgentSkillsConfirmationSchema.nullable(),
});
export type AgentSkillsSaveResult = z.infer<typeof AgentSkillsSaveResultSchema>;
function agentSkillsRequest<const Type extends string>(type: Type) {
return z.object({ type: z.literal(type), requestId: z.string() }).strict();
}
export const AgentSkillsGetStatusRequestSchema = agentSkillsRequest(
"agent.skills.get_status.request",
);
export const AgentSkillsReconcileRequestSchema = agentSkillsRequest(
"agent.skills.reconcile.request",
);
export const AgentSkillsUninstallRequestSchema = agentSkillsRequest(
"agent.skills.uninstall.request",
);
export const AgentSkillsSaveSelectionRequestSchema = z
.object({
type: z.literal("agent.skills.save_selection.request"),
requestId: z.string(),
selection: AgentSkillSelectionSchema,
confirmedRemovals: z.array(z.string()).optional(),
})
.strict();
export const AgentSkillsImportLegacySelectionRequestSchema = z
.object({
type: z.literal("agent.skills.import_legacy_selection.request"),
requestId: z.string(),
selection: AgentSkillSelectionSchema,
})
.strict();
export const GetDaemonConfigRequestMessageSchema = z.object({
type: z.literal("get_daemon_config_request"),
requestId: z.string(),
});
export const SetDaemonConfigRequestMessageSchema = z.object({
type: z.literal("set_daemon_config_request"),
requestId: z.string(),
config: MutableDaemonConfigPatchSchema,
});
export const ReadProjectConfigRequestMessageSchema = z.object({
type: z.literal("read_project_config_request"),
requestId: z.string(),
repoRoot: z.string(),
});
export const WriteProjectConfigRequestMessageSchema = z.object({
type: z.literal("write_project_config_request"),
requestId: z.string(),
repoRoot: z.string(),
config: PaseoConfigRawSchema,
expectedRevision: PaseoConfigRevisionSchema.nullable(),
});
// ============================================================================
// Dictation Streaming (lossless, resumable)
// ============================================================================
export const DictationStreamStartMessageSchema = z.object({
type: z.literal("dictation_stream_start"),
dictationId: z.string(),
format: z.string(), // e.g. "audio/pcm;rate=16000;bits=16"
});
export const DictationStreamChunkMessageSchema = z.object({
type: z.literal("dictation_stream_chunk"),
dictationId: z.string(),
seq: z.number().int().nonnegative(),
audio: z.string(), // base64 encoded chunk
format: z.string(), // e.g. "audio/pcm;rate=16000;bits=16"
});
export const DictationStreamFinishMessageSchema = z.object({
type: z.literal("dictation_stream_finish"),
dictationId: z.string(),
finalSeq: z.number().int().nonnegative(),
});
export const DictationStreamCancelMessageSchema = z.object({
type: z.literal("dictation_stream_cancel"),
dictationId: z.string(),
});
const GitSetupOptionsSchema = z.object({
baseBranch: z.string().optional(),
createNewBranch: z.boolean().optional(),
newBranchName: z.string().optional(),
createWorktree: z.boolean().optional(),
worktreeSlug: z.string().optional(),
refName: z.string().min(1).optional(),
action: z.enum(["branch-off", "checkout"]).optional(),
checkoutSource: ChangeRequestCheckoutSourceSchema.optional(),
// COMPAT(githubPrNumber): legacy GitHub checkout input retained when
// checkoutSource shipped in v0.2.0-beta.1. Remove after 2027-01-17 once the
// supported client floor is >= v0.2.0.
githubPrNumber: z.number().int().positive().optional(),
});
export type GitSetupOptions = z.infer<typeof GitSetupOptionsSchema>;
export const CreateAgentWorktreeTargetSchema = z.discriminatedUnion("mode", [
z.object({
mode: z.literal("branch-off"),
newBranch: z.string().min(1),
base: z.string().min(1).optional(),
}),
z.object({
mode: z.literal("checkout-branch"),
branch: z.string().min(1),
}),
z.object({
mode: z.literal("checkout-pr"),
prNumber: z.number().int().positive(),
}),
]);
export type CreateAgentWorktreeTarget = z.infer<typeof CreateAgentWorktreeTargetSchema>;
export const CreateAgentRequestMessageSchema = z.object({
type: z.literal("create_agent_request"),
// Legacy create_agent_request uses a separate initial-message receipt when keyed.
idempotencyKey: z.string().min(1).max(512).optional(),
config: AgentSessionConfigSchema,
env: z.record(z.string(), z.string()).optional(),
workspaceId: z.string().optional(),
// Optional caller context lets managed CLI invocations use the same daemon-owned
// workspace and parentage policy as agent-scoped MCP creation.
callerAgentId: z.string().optional(),
worktreeName: z.string().optional(),
initialPrompt: z.string().optional(),
clientMessageId: z.string().optional(),
outputSchema: z.record(z.string(), z.unknown()).optional(),
images: z.array(ImageAttachmentSchema).optional(),
attachments: AgentAttachmentsSchema,
git: GitSetupOptionsSchema.optional(),
worktree: CreateAgentWorktreeTargetSchema.optional(),
autoArchive: z.boolean().optional(),
labels: z.record(z.string(), z.string()).default({}),
requestId: z.string(),
});
/** Resource IDs are optional on creation; supplied IDs retain their identity on replay. */
export const AgentCreateRequestSchema = CreateAgentRequestMessageSchema.extend({
type: z.literal("agent.create.request"),
agentId: z.uuid().optional(),
attachments: z.array(AgentAttachmentSchema).optional(),
subscribe: z.boolean().optional(),
});
export const WorkspaceInitialAgentSchema = AgentCreateRequestSchema.omit({
type: true,
requestId: true,
idempotencyKey: true,
subscribe: true,
workspaceId: true,
worktree: true,
worktreeName: true,
git: true,
});
export const CreationSubscribeRequestSchema = z.object({
type: z.literal("creation.subscribe.request"),
requestId: z.string(),
kind: z.enum(["workspace", "agent"]),
idempotencyKey: z.string().min(1).max(512),
subscribe: z.boolean().optional(),
});
export const ListProviderModelsRequestMessageSchema = z.object({
type: z.literal("list_provider_models_request"),
provider: AgentProviderSchema,
cwd: z.string().optional(),
requestId: z.string(),
});
export const ListProviderModesRequestMessageSchema = z.object({
type: z.literal("list_provider_modes_request"),
provider: AgentProviderSchema,
cwd: z.string().optional(),
requestId: z.string(),
});
export const ListAvailableProvidersRequestMessageSchema = z.object({
type: z.literal("list_available_providers_request"),
requestId: z.string(),
});
export const GetProvidersSnapshotRequestMessageSchema = z.object({
type: z.literal("get_providers_snapshot_request"),
cwd: z.string().optional(),
// COMPAT(compactProviderSnapshots): old daemons ignore this field and return a full snapshot.
ifNoneMatch: z.string().optional(),
requestId: z.string(),
});
export const RefreshProvidersSnapshotRequestMessageSchema = z.object({
type: z.literal("refresh_providers_snapshot_request"),
cwd: z.string().optional(),
providers: z.array(AgentProviderSchema).optional(),
requestId: z.string(),
});
export const ProviderDiagnosticRequestMessageSchema = z.object({
type: z.literal("provider_diagnostic_request"),
provider: AgentProviderSchema,
requestId: z.string(),
});
export const ProviderUsageListRequestMessageSchema = z.object({
type: z.literal("provider.usage.list.request"),
requestId: z.string(),
});
export const UsageListReportsRequestMessageSchema = z.object({
type: z.literal("usage.list_reports.request"),
agentId: z.string().optional(),
requestId: z.string(),
reportIds: z.array(z.string()).optional(),
forceRefresh: z.boolean().optional(),
});
export const ResumeAgentRequestMessageSchema = z.object({
type: z.literal("resume_agent_request"),
handle: AgentPersistenceHandleSchema,
overrides: AgentSessionConfigSchema.partial().optional(),
requestId: z.string(),
});
export const ImportAgentRequestMessageSchema = z.object({
type: z.literal("import_agent_request"),
provider: AgentProviderSchema.optional(),
providerId: z.string().optional(),
sessionId: z.string().optional(),
providerHandleId: z.string().optional(),
cwd: z.string().optional(),
workspaceId: z.string().optional(),
labels: z.record(z.string(), z.string()).optional(),
requestId: z.string(),
});
export const RefreshAgentRequestMessageSchema = z.object({
type: z.literal("refresh_agent_request"),
agentId: z.string(),
requestId: z.string(),
});
export const CancelAgentRequestMessageSchema = z.object({
type: z.literal("cancel_agent_request"),
agentId: z.string(),
requestId: z.string().optional(),
});
export const RestartServerRequestMessageSchema = z.object({
type: z.literal("restart_server_request"),
reason: z.string().optional(),
requestId: z.string(),
});
export const ShutdownServerRequestMessageSchema = z.object({
type: z.literal("shutdown_server_request"),
requestId: z.string(),
});
export const DaemonUpdateRequestMessageSchema = z.object({
type: z.literal("daemon.update.request"),
requestId: z.string(),
});
export const AgentTimelineCursorSchema = z.object({
epoch: z.string(),
seq: z.number().int().nonnegative(),
});
export const FetchAgentTimelineRequestMessageSchema = z.object({
type: z.literal("fetch_agent_timeline_request"),
agentId: z.string(),
requestId: z.string(),
direction: z.enum(["tail", "before", "after"]).optional(),
cursor: AgentTimelineCursorSchema.optional(),
// 0 means "all matching rows for this query window".
limit: z.number().int().nonnegative().optional(),
// Default should be projected for app timeline loading.
projection: z.enum(["projected", "canonical"]).optional(),
// Allow the client to merge this bounded page outside its contiguous loaded range.
mergeWindow: z.boolean().optional(),
});
export const AgentTimelineSearchRequestMessageSchema = z.object({
type: z.literal("agent.timeline.search.request"),
agentId: z.string(),
requestId: z.string(),
query: z.string(),
cursor: z.number().int().nonnegative().optional(),
});
export const AgentTimelineListPromptsRequestMessageSchema = z.object({
type: z.literal("agent.timeline.list_prompts.request"),
agentId: z.string(),
requestId: z.string(),
});
export const ProviderSubagentListRequestMessageSchema = z.object({
type: z.literal("agent.provider_subagents.list.request"),
parentAgentId: z.string(),
requestId: z.string(),
});
export const ProviderSubagentTimelineRequestMessageSchema = z.object({
type: z.literal("agent.provider_subagents.timeline.get.request"),
parentAgentId: z.string(),
subagentId: z.string(),
requestId: z.string(),
direction: z.enum(["tail", "before", "after"]).optional(),
cursor: AgentTimelineCursorSchema.optional(),
limit: z.number().int().nonnegative().optional(),
});
export const SetAgentTimelineSubscriptionRequestMessageSchema = z.object({
type: z.literal("agent.timeline.set_subscription.request"),
agentIds: z.array(z.string()),
requestId: z.string(),
});
export const AgentForkContextRequestMessageSchema = z.object({
type: z.literal("agent.fork_context.request"),
agentId: z.string(),
boundaryCursor: AgentTimelineCursorSchema.optional(),
boundaryMessageId: z.string().optional(),
requestId: z.string(),
});
export const SetAgentModeRequestMessageSchema = z.object({
type: z.literal("set_agent_mode_request"),
agentId: z.string(),
modeId: z.string(),
requestId: z.string(),
});
const AgentActionResponsePayloadSchema = z.object({
requestId: z.string(),
agentId: z.string(),
accepted: z.boolean(),
error: z.string().nullable(),
notice: AgentProviderNoticeSchema.nullable().optional(),
});
export const SetAgentModeResponseMessageSchema = z.object({
type: z.literal("set_agent_mode_response"),
payload: AgentActionResponsePayloadSchema,
});
export const SetAgentModelRequestMessageSchema = z.object({
type: z.literal("set_agent_model_request"),
agentId: z.string(),
modelId: z.string().nullable(),
requestId: z.string(),
});
export const SetAgentModelResponseMessageSchema = z.object({
type: z.literal("set_agent_model_response"),
payload: AgentActionResponsePayloadSchema,
});
export const SetAgentThinkingRequestMessageSchema = z.object({
type: z.literal("set_agent_thinking_request"),
agentId: z.string(),
thinkingOptionId: z.string().nullable(),
requestId: z.string(),
});
export const SetAgentThinkingResponseMessageSchema = z.object({
type: z.literal("set_agent_thinking_response"),
payload: AgentActionResponsePayloadSchema,
});
export const SetAgentFeatureRequestMessageSchema = z.object({
type: z.literal("set_agent_feature_request"),
agentId: z.string(),
featureId: z.string(),
value: z.unknown(),
requestId: z.string(),
});
export const SetAgentFeatureResponseMessageSchema = z.object({
type: z.literal("set_agent_feature_response"),
payload: AgentActionResponsePayloadSchema,
});
/**
* Every agent-config value a client can change in one shot. Each field is
* optional and an omitted field is left alone; `null` on model and thinking
* clears them, matching the single-field RPCs above.
*/
export const AgentConfigApplySchema = z.object({
modelId: z.string().nullable().optional(),
modeId: z.string().optional(),
thinkingOptionId: z.string().nullable().optional(),
featureValues: z.record(z.string(), z.unknown()).optional(),
});
export type AgentConfigApply = z.infer<typeof AgentConfigApplySchema>;
/**
* Applies a whole config bundle to one agent. The four single-field RPCs above
* stay for individual control edits. One request prevents client interruption
* and other mutations from interleaving between bundle steps; provider-level
* rejection can still leave earlier steps applied.
*/
export const AgentConfigApplyRequestMessageSchema = z.object({
type: z.literal("agent.config.apply.request"),
agentId: z.string(),
config: AgentConfigApplySchema,
requestId: z.string(),
});
export const AgentConfigApplyResponseMessageSchema = z.object({
type: z.literal("agent.config.apply.response"),
payload: AgentActionResponsePayloadSchema,
});
export const AgentDetachRequestMessageSchema = z.object({
type: z.literal("agent.detach.request"),
agentId: z.string(),
requestId: z.string(),
});
export const AgentDetachResponseMessageSchema = z.object({
type: z.literal("agent.detach.response"),
payload: AgentActionResponsePayloadSchema,
});
export const AgentRewindModeSchema = z.enum(["conversation", "files", "both"]);
export const AgentRewindRequestMessageSchema = z.object({
type: z.literal("agent.rewind.request"),
agentId: z.string(),
messageId: z.string(),
mode: AgentRewindModeSchema,
requestId: z.string(),
});
export const AgentRewindResponseMessageSchema = z.object({
type: z.literal("agent.rewind.response"),
payload: z.object({
requestId: z.string(),
agentId: z.string(),
ok: z.boolean(),
error: z.string().nullable(),
}),
});
export const UpdateAgentResponseMessageSchema = z.object({
type: z.literal("update_agent_response"),
payload: AgentActionResponsePayloadSchema,
});
export const ProjectRenameResponsePayloadSchema = z.object({
requestId: z.string(),
projectId: z.string(),
accepted: z.boolean(),
customName: z.string().nullable(),
error: z.string().nullable(),
});
export const ProjectRenameResponseSchema = z.object({
type: z.literal("project.rename.response"),
payload: ProjectRenameResponsePayloadSchema,
});
export const ProjectIconSetResponseSchema = z.object({
type: z.literal("project.icon.set.response"),
payload: z.object({
requestId: z.string(),
projectId: z.string(),
accepted: z.boolean(),
error: z.string().nullable(),
}),
});
export const ProjectRemoveResponsePayloadSchema = z.object({
requestId: z.string(),
projectId: z.string(),
accepted: z.boolean(),
removedWorkspaceIds: z.array(z.string()).default([]),
error: z.string().nullable(),
});
export const ProjectRemoveResponseSchema = z.object({
type: z.literal("project.remove.response"),
payload: ProjectRemoveResponsePayloadSchema,
});
export const WorkspaceTitleSetResponsePayloadSchema = z.object({
requestId: z.string(),
workspaceId: z.string(),
accepted: z.boolean(),
title: z.string().nullable(),
error: z.string().nullable(),
});
export const WorkspaceTitleSetResponseSchema = z.object({
type: z.literal("workspace.title.set.response"),
payload: WorkspaceTitleSetResponsePayloadSchema,
});
export const WorkspacePinSetResponsePayloadSchema = z.object({
requestId: z.string(),
workspaceId: z.string(),
accepted: z.boolean(),
pinnedAt: z.string().nullable(),
error: z.string().nullable(),
});
export const WorkspacePinSetResponseSchema = z.object({
type: z.literal("workspace.pin.set.response"),
payload: WorkspacePinSetResponsePayloadSchema,
});
export const WorkspaceRecoveryStateSchema = z.discriminatedUnion("kind", [
z.object({
kind: z.literal("recoverable"),
workspaceId: z.string(),
workspaceName: z.string(),
action: z.string(),
branch: z.string().nullable(),
}),
z.object({
kind: z.literal("unavailable"),
workspaceId: z.string(),
reason: z.string(),
message: z.string(),
}),
]);
export const WorkspaceRecoveryInspectResponseSchema = z.object({
type: z.literal("workspace.recovery.inspect.response"),
payload: z.object({
requestId: z.string(),
state: WorkspaceRecoveryStateSchema,
}),
});
export const WorkspaceRecoveryRestoreResponseSchema = z.object({
type: z.literal("workspace.recovery.restore.response"),
payload: z.object({
requestId: z.string(),
workspaceId: z.string(),
accepted: z.boolean(),
error: z.string().nullable(),
}),
});
export const SetVoiceModeResponseMessageSchema = z.object({
type: z.literal("set_voice_mode_response"),
payload: z.object({
requestId: z.string(),
enabled: z.boolean(),
agentId: z.string().nullable(),
accepted: z.boolean(),
error: z.string().nullable(),
reasonCode: z.string().optional(),
retryable: z.boolean().optional(),
missingModelIds: z.array(z.string()).optional(),
}),
});
export const AgentPermissionResponseMessageSchema = z.object({
type: z.literal("agent_permission_response"),
agentId: z.string(),
requestId: z.string(),
response: AgentPermissionResponseSchema,
});
const CheckoutErrorCodeSchema = z.enum([
"NOT_GIT_REPO",
"NOT_ALLOWED",
"MERGE_CONFLICT",
"UNKNOWN",
]);
const CheckoutErrorSchema = z.object({
code: CheckoutErrorCodeSchema,
message: z.string(),
});
const CheckoutDiffCompareSchema = z.object({
mode: z.enum(["uncommitted", "base"]),
baseRef: z.string().optional(),
ignoreWhitespace: z.boolean().optional(),
});
export const CheckoutStatusRequestSchema = z.object({
type: z.literal("checkout_status_request"),
cwd: z.string(),
requestId: z.string(),
});
export const SubscribeCheckoutDiffRequestSchema = z.object({
type: z.literal("subscribe_checkout_diff_request"),
subscriptionId: z.string().optional(),
cwd: z.string(),
compare: CheckoutDiffCompareSchema,
requestId: z.string(),
});
export const CheckoutDiffGetRequestSchema = z.object({
type: z.literal("checkout.diff.get.request"),
cwd: z.string(),
compare: CheckoutDiffCompareSchema,
requestId: z.string(),
});
export const UnsubscribeCheckoutDiffRequestSchema = z.object({
type: z.literal("unsubscribe_checkout_diff_request"),
subscriptionId: z.string(),
});
export const CheckoutCommitRequestSchema = z.object({
type: z.literal("checkout_commit_request"),
cwd: z.string(),
message: z.string().optional(),
addAll: z.boolean().optional(),
requestId: z.string(),
});
export const CheckoutMergeRequestSchema = z.object({
type: z.literal("checkout_merge_request"),
cwd: z.string(),
baseRef: z.string().optional(),
strategy: z.enum(["merge", "squash"]).optional(),
requireCleanTarget: z.boolean().optional(),
requestId: z.string(),
});
export const CheckoutMergeFromBaseRequestSchema = z.object({
type: z.literal("checkout_merge_from_base_request"),
cwd: z.string(),
baseRef: z.string().optional(),
requireCleanTarget: z.boolean().optional(),
requestId: z.string(),
});
export const CheckoutPullRequestSchema = z.object({
type: z.literal("checkout_pull_request"),
cwd: z.string(),
requestId: z.string(),
});
export const CheckoutPushRequestSchema = z.object({
type: z.literal("checkout_push_request"),
cwd: z.string(),
requestId: z.string(),
});
export const CheckoutRefreshRequestSchema = z.object({
type: z.literal("checkout.refresh.request"),
cwd: z.string(),
requestId: z.string(),
});
export const CheckoutDiscardChangesRequestSchema = z.object({
type: z.literal("checkout.discard_changes.request"),
cwd: z.string(),
paths: z.array(z.string()).min(1),
requestId: z.string(),
});
export const CheckoutPrCreateRequestSchema = z.object({
type: z.literal("checkout_pr_create_request"),
cwd: z.string(),
title: z.string().optional(),
body: z.string().optional(),
baseRef: z.string().optional(),
requestId: z.string(),
});
export const CheckoutPrMergeRequestSchema = z.object({
type: z.literal("checkout_pr_merge_request"),
cwd: z.string(),
mergeMethod: z.enum(["merge", "squash", "rebase"]),
requestId: z.string(),
});
export const CheckoutForgeSetAutoMergeRequestSchema = z.object({
type: z.literal("checkout.forge.set_auto_merge.request"),
cwd: z.string(),
enabled: z.boolean(),
mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(),
requestId: z.string(),
});
// COMPAT(githubAutoMergeRpc): legacy RPC retained when
// checkout.forge.set_auto_merge.* shipped in v0.2.0-beta.1. Stop serving and
// consuming it after 2027-01-17 once client and daemon floors are >= v0.2.0.
export const CheckoutGithubSetAutoMergeRequestSchema = z.object({
type: z.literal("checkout.github.set_auto_merge.request"),
cwd: z.string(),
enabled: z.boolean(),
mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(),
requestId: z.string(),
});
const CheckoutCommitFileSchema = z.object({
path: z.string(),
additions: z.number(),
deletions: z.number(),
status: z.enum(["added", "modified", "deleted", "renamed"]).optional(),
});
const CheckoutCommitSchema = z.object({
sha: z.string(),
shortSha: z.string(),
subject: z.string(),
authorName: z.string(),
authorDate: z.string(), // ISO 8601
isOnRemote: z.boolean(), // false = local-only (unpushed)
// COMPAT(commitBaseClassification): added in v0.2.0, remove optional after 2027-01-23.
isOnBase: z.boolean().optional(),
files: z.array(CheckoutCommitFileSchema),
});
export const CheckoutCommitsListRequestSchema = z.object({
type: z.literal("checkout.commits.list.request"),
cwd: z.string(),
requestId: z.string(),
});
export const CheckoutCommitFileDiffRequestSchema = z.object({
type: z.literal("checkout.commits.file_diff.request"),
cwd: z.string(),
sha: z.string(),
path: z.string(),
requestId: z.string(),
});
const GitHubRepoSegmentSchema = z.string().regex(/^[A-Za-z0-9._-]+$/);
const CheckoutCheckDetailsRequestPayloadSchema = z.object({
cwd: z.string(),
// GitHub addresses check runs by owner/name. GitLab resolves the project from
// cwd and omits these GitHub-only single-segment fields.
repoOwner: GitHubRepoSegmentSchema.optional(),
repoName: GitHubRepoSegmentSchema.optional(),
// Permanently optional: a check addressed only by workflowRunId (Gitea
// Actions runs carry no check-run id) is fetchable. Callers send at least one
// of checkRunId/workflowRunId; the gated forge RPC only reaches daemons that
// understand this.
checkRunId: z.number().int().positive().optional(),
workflowRunId: z.number().int().positive().optional(),
// Permanent forge-routing field, optional because only some forges need it:
// GitLab routes check details to the MR's head pipeline; Gitea-family adapters
// resolve the PR head SHA by number, including after merge/close. GitHub
// ignores it.
changeRequestNumber: z.number().int().positive().optional(),
requestId: z.string(),
});
export const CheckoutForgeGetCheckDetailsRequestSchema =
CheckoutCheckDetailsRequestPayloadSchema.extend({
type: z.literal("checkout.forge.get_check_details.request"),
});
// COMPAT(githubCheckDetailsRpc): legacy RPC retained when
// checkout.forge.get_check_details.* shipped in v0.2.0-beta.1. Stop serving
// and consuming it after 2027-01-17 once client and daemon floors are >= v0.2.0.
export const CheckoutGithubGetCheckDetailsRequestSchema =
CheckoutCheckDetailsRequestPayloadSchema.extend({
type: z.literal("checkout.github.get_check_details.request"),
});
export const CheckoutPrStatusRequestSchema = z.object({
type: z.literal("checkout_pr_status_request"),
cwd: z.string(),
requestId: z.string(),
});
export const PullRequestTimelineRequestSchema = z.object({
type: z.literal("pull_request_timeline_request"),
cwd: z.string(),
prNumber: z.number(),
repoOwner: z.string(),
repoName: z.string(),
requestId: z.string(),
});
export const ValidateBranchRequestSchema = z.object({
type: z.literal("validate_branch_request"),
cwd: z.string(),
branchName: z.string(),
requestId: z.string(),
});
export const CheckoutSwitchBranchRequestSchema = z.object({
type: z.literal("checkout_switch_branch_request"),
cwd: z.string(),
branch: z.string(),
requestId: z.string(),
});
export const CheckoutRenameBranchRequestSchema = z.object({
type: z.literal("checkout.rename_branch.request"),
cwd: z.string(),
branch: z.string(),
requestId: z.string(),
});
export const StashSaveRequestSchema = z.object({
type: z.literal("stash_save_request"),
cwd: z.string(),
/** Branch name to tag the stash with for later identification. */
branch: z.string().optional(),
requestId: z.string(),
});
export const StashPopRequestSchema = z.object({
type: z.literal("stash_pop_request"),
cwd: z.string(),
/** Zero-based index from stash_list_response. */
stashIndex: z.number().int().min(0),
requestId: z.string(),
});
export const StashListRequestSchema = z.object({
type: z.literal("stash_list_request"),
cwd: z.string(),
/** If true, only return paseo-created stashes. Default true. */
paseoOnly: z.boolean().optional(),
requestId: z.string(),
});
export const BranchSuggestionsRequestSchema = z.object({
type: z.literal("branch_suggestions_request"),
cwd: z.string(),
query: z.string().optional(),
limit: z.number().int().min(1).max(200).optional(),
requestId: z.string(),
});
export const GitHubSearchItemSchema = z.object({
kind: z.enum(["issue", "pr"]),
forge: z.string().optional(),
number: z.number(),
title: z.string(),
url: z.string(),
state: z.string(),
body: z.string().nullable(),
labels: z.array(z.string()),
projectPath: z.string().optional(),
baseRefName: z.string().nullable().optional(),
headRefName: z.string().nullable().optional(),
updatedAt: z.string().optional(),
});
export const ForgeSearchItemSchema = GitHubSearchItemSchema.extend({
kind: z.enum(["issue", "change_request"]),
});
// COMPAT(githubSearchKind): legacy GitHub kind aliases retained when neutral
// forge search shipped in v0.2.0-beta.1. Remove after 2027-01-17 together with
// the legacy github_search_request RPC.
export const ForgeSearchKindSchema = z.enum([
"issue",
"change_request",
"github-issue",
"github-pr",
"pr",
]);
export const GitHubSearchKindSchema = ForgeSearchKindSchema;
export const ForgeSearchRequestSchema = z.object({
type: z.literal("forge.search.request"),
cwd: z.string(),
query: z.string(),
limit: z.number().int().min(1).max(50).optional(),
kinds: z.array(ForgeSearchKindSchema).optional(),
requestId: z.string(),
});
// COMPAT(githubSearchRpc): legacy RPC retained when forge.search.* shipped in
// v0.2.0-beta.1. Stop serving and consuming it after 2027-01-17 once client
// and daemon floors are >= v0.2.0.
export const GitHubSearchRequestSchema = z.object({
type: z.literal("github_search_request"),
cwd: z.string(),
query: z.string(),
limit: z.number().int().min(1).max(50).optional(),
kinds: z.array(GitHubSearchKindSchema).optional(),
requestId: z.string(),
});
export const DirectorySuggestionsRequestSchema = z.object({
type: z.literal("directory_suggestions_request"),
query: z.string(),
cwd: z.string().optional(),
includeFiles: z.boolean().optional(),
includeDirectories: z.boolean().optional(),
matchMode: z.enum(["fuzzy", "suffix"]).optional(),
limit: z.number().int().min(1).max(100).optional(),
requestId: z.string(),
});
export const PaseoWorktreeListRequestSchema = z.object({
type: z.literal("paseo_worktree_list_request"),
cwd: z.string().optional(),
repoRoot: z.string().optional(),
requestId: z.string(),
});
export const PaseoWorktreeArchiveRequestSchema = z.object({
type: z.literal("paseo_worktree_archive_request"),
worktreePath: z.string().optional(),
repoRoot: z.string().optional(),
branchName: z.string().optional(),
// COMPAT(worktreeArchiveWorkspaceId): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
// Explicit workspace record to archive. A directory can back multiple workspaces
// (Model B), so resolving the target by cwd alone picks the wrong record. When
// present the daemon archives this exact workspace; when absent it falls back to
// resolving by worktreePath, preferring the worktree-kind record on a cwd tie.
workspaceId: z.string().optional(),
// COMPAT(worktreeArchiveScope): added in v0.1.97, drop the gate when floor >= v0.1.97.
// Scope of the archive operation. "workspace" archives a single workspace record
// (today's default UI behavior). "worktree" archives every active workspace whose
// cwd resolves to the target directory, then removes the directory if it is
// Paseo-owned. Omitted/unknown values default to "workspace" for old-client safety.
scope: z.enum(["workspace", "worktree"]).optional().default("workspace"),
// COMPAT(worktreeDiskDeletion): added in v0.1.97, ignored as of v0.1.97
// (disk removal derived from scope + last-reference + ownership); field
// retained for wire parse-compat, drop when floor >= v0.1.97.
deleteWorktreeFromDisk: z.boolean().optional().default(false),
requestId: z.string(),
});
export const FirstAgentContextSchema = z.object({
prompt: z.string().optional(),
attachments: AgentAttachmentsSchema,
});
export const CreatePaseoWorktreeRequestSchema = z.object({
type: z.literal("create_paseo_worktree_request"),
cwd: z.string(),
projectId: z.string().optional(),
worktreeSlug: z.string().optional(),
nameContext: z.string().optional(),
attachments: AgentAttachmentsSchema.optional(),
firstAgentContext: FirstAgentContextSchema.optional(),
refName: z.string().min(1).optional(),
action: z.enum(["branch-off", "checkout"]).optional(),
checkoutSource: ChangeRequestCheckoutSourceSchema.optional(),
// COMPAT(githubPrNumber): legacy GitHub checkout input retained when
// checkoutSource shipped in v0.2.0-beta.1. Remove after 2027-01-17 once the
// supported client floor is >= v0.2.0.
githubPrNumber: z.number().int().positive().optional(),
requestId: z.string(),
});
export const WorkspaceSetupStatusRequestSchema = z.object({
type: z.literal("workspace_setup_status_request"),
workspaceId: z.string(),
requestId: z.string(),
});
export const WorkspaceSetupRunRequestSchema = z.object({
type: z.literal("workspace.setup.run.request"),
workspaceId: z.string(),
requestId: z.string(),
});
// COMPAT(desktopEditorBridge): added in v0.1.88, remove after 2026-12-03 once old clients no longer call daemon editor RPCs.
export const LegacyListAvailableEditorsRequestSchema = z.object({
type: z.literal("list_available_editors_request"),
requestId: z.string(),
});
export const LegacyOpenInEditorRequestSchema = z.object({
type: z.literal("open_in_editor_request"),
path: z.string(),
editorId: z.string().trim().min(1),
mode: z.enum(["open", "reveal"]).optional(),
cwd: z.string().optional(),
requestId: z.string(),
});
export const OpenProjectRequestSchema = z.object({
type: z.literal("open_project_request"),
// Path used only for workspace lookup/creation. Use the returned workspace.id for all subsequent references.
cwd: z.string(),
requestId: z.string(),
});
// Smallest shorthand repo path is "a/b": owner, slash, repository.
const MIN_REPOSITORY_PATH_LENGTH = 3;
export const ProjectAddRequestSchema = z.object({
type: z.literal("project.add.request"),
cwd: z.string(),
requestId: z.string(),
});
export const ProjectCreateDirectoryRequestSchema = z.object({
type: z.literal("project.create_directory.request"),
parentPath: z.string(),
name: z.string(),
requestId: z.string(),
});
export const GithubRepositorySchema = z.object({
id: z.string().min(1),
name: z.string().min(1),
nameWithOwner: z.string().min(MIN_REPOSITORY_PATH_LENGTH),
description: z.string().nullable(),
visibility: z.enum(["public", "private", "internal"]),
updatedAt: z.string(),
cloneUrl: z.string().min(MIN_REPOSITORY_PATH_LENGTH),
});
export const WorkspaceGithubSearchRepositoriesRequestSchema = z.object({
type: z.literal("workspace.github.search_repositories.request"),
query: z.string(),
limit: z.number().int().min(1).max(50).optional(),
requestId: z.string(),
});
export const ProjectGithubCloneProtocolSchema = z.enum(["https", "ssh"]);
export const ProjectGithubCloneRequestSchema = z.object({
type: z.literal("project.github.clone.request"),
repo: z.string().trim().min(MIN_REPOSITORY_PATH_LENGTH),
cloneProtocol: ProjectGithubCloneProtocolSchema.optional(),
targetDirectory: z.string().trim().min(1),
requestId: z.string(),
});
export const ArchiveWorkspaceRequestSchema = z.object({
type: z.literal("archive_workspace_request"),
workspaceId: z.string(),
requestId: z.string(),
});
// Create a new workspace record. Unlike open_project, this never deduplicates by
// directory: it always produces a fresh workspace. The source discriminates
// between an existing local directory and a newly created paseo worktree.
export const WorkspaceCreateRequestSchema = z.object({
type: z.literal("workspace.create.request"),
workspaceId: z
.string()
.regex(/^wks_[a-f0-9]{16}$/)
.optional(),
agent: WorkspaceInitialAgentSchema.optional(),
subscribe: z.boolean().optional(),
requestId: z.string(),
idempotencyKey: z.string().min(1).max(512).optional(),
// Optional user-set title applied to the created workspace.
title: z.string().optional(),
// Optional prompt context for workspace-level name/branch generation.
firstAgentContext: FirstAgentContextSchema.optional(),
source: z.discriminatedUnion("kind", [
z.object({
kind: z.literal("directory"),
// Path of the existing checkout/directory to back the workspace.
path: z.string(),
projectId: z.string().optional(),
}),
z.object({
kind: z.literal("worktree"),
// The project whose repo the worktree is cut from.
cwd: z.string().optional(),
projectId: z.string().optional(),
action: z.enum(["branch-off", "checkout"]).optional(),
// Target branch for checkout, or base ref for branch-off.
refName: z.string().min(1).optional(),
baseBranch: z.string().optional(),
// New branch name for branch-off. The worktree path may use a different slug.
branchName: z.string().min(1).optional(),
checkoutSource: ChangeRequestCheckoutSourceSchema.optional(),
// COMPAT(githubPrNumber): legacy GitHub checkout input retained when
// checkoutSource shipped in v0.2.0-beta.1. Remove after 2027-01-17 once
// the supported client floor is >= v0.2.0.
githubPrNumber: z.number().int().positive().optional(),
worktreeSlug: z.string().optional(),
}),
]),
});
export const WorkspaceClearAttentionRequestSchema = z.object({
type: z.literal("workspace.clear_attention.request"),
workspaceId: z.union([z.string(), z.array(z.string())]),
requestId: z.string(),
});
export const WorkspaceMarkUnreadRequestSchema = z.object({
type: z.literal("workspace.mark_unread.request"),
workspaceId: z.string(),
requestId: z.string(),
});
// Highlighted diff token schema
// Note: style can be a compound class name (e.g., "heading meta") from the syntax highlighter
const HighlightTokenSchema = z.object({
text: z.string(),
style: z.string().nullable(),
});
const DiffLineSchema = z.object({
type: z.enum(["add", "remove", "context", "header"]),
content: z.string(),
tokens: z.array(HighlightTokenSchema).optional(),
});
const DiffHunkSchema = z.object({
oldStart: z.number(),
oldCount: z.number(),
newStart: z.number(),
newCount: z.number(),
lines: z.array(DiffLineSchema),
});
const ParsedDiffFileSchema = z.object({
path: z.string(),
// COMPAT(diffOldPath): added in v0.3.0, remove gate after 2027-02-09.
oldPath: z.string().optional(),
isNew: z.boolean(),
isDeleted: z.boolean(),
additions: z.number(),
deletions: z.number(),
hunks: z.array(DiffHunkSchema),
status: z.enum(["ok", "too_large", "binary"]).optional(),
});
const FileExplorerEntrySchema = z.object({
name: z.string(),
path: z.string(),
kind: z.enum(["file", "directory"]),
size: z.number(),
modifiedAt: z.string(),
});
const FileExplorerFileSchema = z.object({
path: z.string(),
kind: z.enum(["text", "image", "binary"]),
encoding: z.enum(["utf-8", "base64", "none"]),
content: z.string().optional(),
mimeType: z.string().optional(),
size: z.number(),
modifiedAt: z.string(),
revision: z.string().optional(),
});
const FileExplorerDirectorySchema = z.object({
path: z.string(),
entries: z.array(FileExplorerEntrySchema),
});
export const FileExplorerRequestSchema = z.object({
type: z.literal("file_explorer_request"),
cwd: z.string(),
path: z.string().optional(),
mode: z.enum(["list", "file"]),
requestId: z.string(),
acceptBinary: z.boolean().optional(),
maxBytes: z.number().int().positive().optional(),
});
export const FileVersionSchema = z.discriminatedUnion("status", [
z.object({
status: z.literal("ready"),
cwd: z.string(),
path: z.string(),
size: z.number().int().nonnegative(),
modifiedAt: z.string(),
revision: z.string().optional(),
}),
z.object({
status: z.literal("missing"),
cwd: z.string(),
path: z.string(),
}),
z.object({
status: z.literal("error"),
cwd: z.string(),
path: z.string(),
error: z.string(),
}),
]);
export const FileSubscribeRequestSchema = z.object({
type: z.literal("fs.file.subscribe.request"),
cwd: z.string(),
path: z.string(),
subscriptionId: z.string().optional(),
requestId: z.string(),
});
export const FileUnsubscribeRequestSchema = z.object({
type: z.literal("fs.file.unsubscribe.request"),
subscriptionId: z.string(),
requestId: z.string(),
});
export const FileWriteRequestSchema = z.object({
type: z.literal("fs.file.write.request"),
cwd: z.string(),
path: z.string(),
content: z.string(),
expectedModifiedAt: z.string(),
expectedRevision: z.string().optional(),
requestId: z.string(),
});
export const FileEntryCreateRequestSchema = z.object({
type: z.literal("fs.entry.create.request"),
cwd: z.string(),
parentPath: z.string(),
name: z.string(),
kind: z.enum(["file", "directory"]),
requestId: z.string(),
});
export const FileEntryRenameRequestSchema = z.object({
type: z.literal("fs.entry.rename.request"),
cwd: z.string(),
path: z.string(),
name: z.string(),
requestId: z.string(),
});
export const FileEntryDuplicateRequestSchema = z.object({
type: z.literal("fs.entry.duplicate.request"),
cwd: z.string(),
path: z.string(),
requestId: z.string(),
});
export const FileEntryDeleteRequestSchema = z.object({
type: z.literal("fs.entry.delete.request"),
cwd: z.string(),
path: z.string(),
requestId: z.string(),
});
export const ProjectIconRequestSchema = z.object({
type: z.literal("project_icon_request"),
cwd: z.string(),
requestId: z.string(),
});
export const ProjectIconGetRequestSchema = z.object({
type: z.literal("project.icon.get.request"),
projectId: z.string(),
requestId: z.string(),
});
export const FileDownloadTokenRequestSchema = z.object({
type: z.literal("file_download_token_request"),
cwd: z.string(),
path: z.string(),
requestId: z.string(),
});
export const FileUploadRequestSchema = z.object({
type: z.literal("file.upload.request"),
fileName: z.string().min(1),
mimeType: z.string().min(1),
size: z.number().int().nonnegative(),
modifiedAt: z.string(),
requestId: z.string(),
});
export const ClearAgentAttentionMessageSchema = z.object({
type: z.literal("clear_agent_attention"),
agentId: z.union([z.string(), z.array(z.string())]),
requestId: z.string().optional(),
});
export const ClientHeartbeatMessageSchema = z.object({
type: z.literal("client_heartbeat"),
deviceType: z.enum(["web", "mobile"]),
focusedAgentId: z.string().nullable(),
// COMPAT(terminalFocusHeartbeat): added in v0.1.97, remove optional default after 2026-12-13 once old clients no longer send heartbeats without terminal focus.
focusedTerminalId: z.string().nullable().optional().default(null),
lastActivityAt: z.string(),
appVisible: z.boolean(),
appVisibilityChangedAt: z.string().optional(),
});
export const PingMessageSchema = z.object({
type: z.literal("ping"),
requestId: z.string(),
clientSentAt: z.number().int().optional(),
});
const ListCommandsDraftConfigSchema = z.object({
provider: AgentProviderSchema,
cwd: z.string(),
modeId: z.string().optional(),
model: z.string().optional(),
thinkingOptionId: z.string().optional(),
featureValues: z.record(z.string(), z.unknown()).optional(),
});
export const ListProviderFeaturesRequestMessageSchema = z.object({
type: z.literal("list_provider_features_request"),
draftConfig: ListCommandsDraftConfigSchema,
requestId: z.string(),
});
export const ListCommandsRequestSchema = z.object({
type: z.literal("list_commands_request"),
agentId: z.string(),
draftConfig: ListCommandsDraftConfigSchema.optional(),
requestId: z.string(),
});
export const RegisterPushTokenMessageSchema = z.object({
type: z.literal("register_push_token"),
token: z.string(),
});
export const PushUnregisterRequestSchema = z.object({
type: z.literal("push.unregister.request"),
token: z.string(),
requestId: z.string(),
});
export const PushUnregisterResponseSchema = z.object({
type: z.literal("push.unregister.response"),
payload: z.object({
requestId: z.string(),
}),
});
// ============================================================================
// Terminal Messages
// ============================================================================
export const ListTerminalsRequestSchema = z.object({
type: z.literal("list_terminals_request"),
cwd: z.string().optional(),
workspaceId: z.string().optional(),
requestId: z.string(),
});
export const SubscribeTerminalsRequestSchema = z.object({
type: z.literal("subscribe_terminals_request"),
requestId: z.string().optional(),
cwd: z.string(),
workspaceId: z.string().optional(),
});
export const UnsubscribeTerminalsRequestSchema = z.object({
type: z.literal("unsubscribe_terminals_request"),
cwd: z.string(),
workspaceId: z.string().optional(),
});
export const CreateTerminalRequestSchema = z.object({
type: z.literal("create_terminal_request"),
cwd: z.string(),
workspaceId: z.string().optional(),
name: z.string().optional(),
agentId: z.string().optional(),
command: z.string().optional(),
args: z.array(z.string()).optional(),
// Initial PTY size. Added in v0.1.107; the app no longer sends it (the estimate cache that fed
// it was removed — the pane-focus resize claim sizes the PTY instead). Kept and honored
// permanently: released v0.1.107 clients still send it, and programmatic callers may pass an
// exact size. Daemons without it start at 80x24 and the first resize corrects that.
size: z
.object({
rows: z.number().int().positive(),
cols: z.number().int().positive(),
})
.optional(),
requestId: z.string(),
});
export const RenameTerminalRequestSchema = z.object({
type: z.literal("terminal.rename.request"),
terminalId: z.string(),
title: z.string(),
requestId: z.string(),
});
export const StartWorkspaceScriptRequestSchema = z.object({
type: z.literal("start_workspace_script_request"),
workspaceId: z.string(),
scriptName: z.string(),
requestId: z.string(),
});
export const WorkspaceScriptListRequestSchema = z.object({
type: z.literal("workspace.script.list.request"),
workspaceId: z.string(),
requestId: z.string(),
});
export const WorkspaceScriptStartRequestSchema = z.object({
type: z.literal("workspace.script.start.request"),
workspaceId: z.string(),
scriptName: z.string(),
requestId: z.string(),
});
export const WorkspaceScriptStopRequestSchema = z.object({
type: z.literal("workspace.script.stop.request"),
workspaceId: z.string(),
scriptName: z.string(),
requestId: z.string(),
});
export const SubscribeTerminalRequestSchema = z.object({
type: z.literal("subscribe_terminal_request"),
terminalId: z.string(),
requestId: z.string(),
restore: z
.object({
mode: z.enum(["live", "visible-snapshot", "full-snapshot"]),
scrollbackLines: z.number().int().nonnegative().optional(),
size: z
.object({
rows: z.number().int().positive(),
cols: z.number().int().positive(),
})
.optional(),
})
.optional(),
});
export const UnsubscribeTerminalRequestSchema = z.object({
type: z.literal("unsubscribe_terminal_request"),
terminalId: z.string(),
});
const TerminalClientMessageSchema = z.discriminatedUnion("type", [
z.object({ type: z.literal("input"), data: z.string() }),
z.object({
type: z.literal("resize"),
rows: z.number(),
cols: z.number(),
intent: z.enum(["claim", "update"]).optional(),
}),
z.object({
type: z.literal("mouse"),
row: z.number(),
col: z.number(),
button: z.number(),
action: z.enum(["down", "up", "move"]),
}),
]);
export const TerminalInputSchema = z.object({
type: z.literal("terminal_input"),
terminalId: z.string(),
message: TerminalClientMessageSchema,
});
export const KillTerminalRequestSchema = z.object({
type: z.literal("kill_terminal_request"),
terminalId: z.string(),
requestId: z.string(),
});
export const CaptureTerminalRequestSchema = z.object({
type: z.literal("capture_terminal_request"),
terminalId: z.string(),
start: z.number().int().optional(),
end: z.number().int().optional(),
stripAnsi: z.boolean().default(true),
requestId: z.string(),
});
export const HubExecutionAgentCreateRequestSchema = z.object({
type: z.literal("hub.execution.agent.create.request"),
requestId: z.string(),
executionId: z.string(),
provider: z.string(),
cwd: z.string(),
prompt: z.string(),
// COMPAT(hubExecutionWorkspaceSelection): semantics retired in v0.3.1; remove after 2027-08-08 once the Hub floor no longer sends it.
workspaceId: z.string().optional(),
model: z.string().optional(),
modeId: z.string().optional(),
thinkingOptionId: z.string().optional(),
featureValues: z.record(z.string(), z.unknown()).optional(),
providerOptions: ProviderOptionsSchema.optional(),
toolPolicy: ToolPolicySchema.optional(),
env: z.record(z.string(), z.string()).optional(),
mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
worktree: CreateAgentWorktreeTargetSchema.optional(),
});
export type HubExecutionAgentCreateRequest = z.infer<typeof HubExecutionAgentCreateRequestSchema>;
export const HubExecutionAgentValidateRequestSchema = z.object({
type: z.literal("hub.execution.agent.validate.request"),
requestId: z.string(),
provider: z.string(),
model: z.string().optional(),
modeId: z.string().optional(),
thinkingOptionId: z.string().optional(),
providerOptions: ProviderOptionsSchema.optional(),
});
export type HubExecutionAgentValidateRequest = z.infer<
typeof HubExecutionAgentValidateRequestSchema
>;
const HubExecutionAgentCreateErrorSchema = z.discriminatedUnion("code", [
z.object({
code: z.literal("provider_options_invalid"),
provider: z.string(),
issues: z.array(
z.object({
path: z.array(z.union([z.string(), z.number()])),
message: z.string(),
}),
),
message: z.string(),
}),
z.object({
code: z.literal("tool_policy_unsupported"),
provider: z.string(),
message: z.string(),
}),
z.object({
code: z.literal("create_failed"),
message: z.string(),
}),
]);
export type HubExecutionAgentCreateError = z.infer<typeof HubExecutionAgentCreateErrorSchema>;
export const HubExecutionControlActionSchema = z.enum(["interrupt", "archive"]);
export type HubExecutionControlAction = z.infer<typeof HubExecutionControlActionSchema>;
export const HubExecutionControlRequestSchema = z.object({
type: z.literal("hub.execution.control.request"),
requestId: z.string(),
executionId: z.string(),
action: HubExecutionControlActionSchema,
});
export type HubExecutionControlRequest = z.infer<typeof HubExecutionControlRequestSchema>;
// These connection event streams have no directory bootstrap or timeline membership.
export const SessionEventSubscriptionSchema = z.enum([
"project.update",
"providers_snapshot_update",
"agent_attention_required",
"agent_permission_request",
"agent_permission_resolved",
"checkout_status_update",
"script_status_update",
"workspace_setup_progress",
"agent.provider_subagents.update",
"terminal_attention_required",
"status.server_info",
"status.daemon_config_changed",
"status.plugin_catalog_changed",
"status.plugin_settings_changed",
"activity_log",
"hub.execution.agent.update",
"hub.execution.agent.stream",
]);
export type SessionEventSubscription = z.infer<typeof SessionEventSubscriptionSchema>;
export const SessionEventsSetSubscriptionRequestSchema = z.object({
type: z.literal("session.events.set_subscription.request"),
requestId: z.string(),
events: z.array(SessionEventSubscriptionSchema),
notifications: z.boolean().optional(),
});
export const SessionEventsSetSubscriptionResponseSchema = z.object({
type: z.literal("session.events.set_subscription.response"),
payload: z.object({ requestId: z.string(), subscriptionId: z.string().optional() }),
});
export const BrowserHostRegisterRequestSchema = z.object({
type: z.literal("browser.host.register.request"),
requestId: z.string(),
hostKind: z.string().min(1),
supportedCommands: z.array(z.enum(BROWSER_AUTOMATION_COMMAND_NAMES)).min(1),
});
export const BrowserHostRegisterResponseSchema = z.object({
type: z.literal("browser.host.register.response"),
payload: z.object({ requestId: z.string(), subscriptionId: z.string() }),
});
export const SubscriptionReleaseRequestSchema = z.object({
type: z.literal("subscription.release.request"),
requestId: z.string(),
subscriptionId: z.string(),
});
export const SubscriptionReleaseResponseSchema = z.object({
type: z.literal("subscription.release.response"),
payload: z.object({ requestId: z.string(), subscriptionId: z.string() }),
});
export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
BrowserHostRegisterRequestSchema,
SubscriptionReleaseRequestSchema,
SessionEventsSetSubscriptionRequestSchema,
HubExecutionAgentCreateRequestSchema,
HubExecutionAgentValidateRequestSchema,
HubExecutionControlRequestSchema,
BrowserAutomationExecuteResponseSchema,
VoiceAudioChunkMessageSchema,
AbortRequestMessageSchema,
AudioPlayedMessageSchema,
FetchAgentsRequestMessageSchema,
FetchAgentHistoryRequestMessageSchema,
FetchRecentProviderSessionsRequestMessageSchema,
FetchWorkspacesRequestMessageSchema,
ProjectListRequestMessageSchema,
FetchAgentRequestMessageSchema,
DeleteAgentRequestMessageSchema,
ArchiveAgentRequestMessageSchema,
CloseItemsRequestMessageSchema,
UpdateAgentRequestMessageSchema,
ProjectRenameRequestSchema,
ProjectIconSetRequestSchema,
ProjectRemoveRequestSchema,
WorkspaceTitleSetRequestSchema,
WorkspacePinSetRequestSchema,
WorkspaceLabelListRequestSchema,
WorkspaceLabelAssignmentSetRequestSchema,
WorkspaceLabelUpdateRequestSchema,
WorkspaceLabelDeleteRequestSchema,
WorkspaceLabelDeleteInspectRequestSchema,
WorkspaceRecoveryInspectRequestSchema,
WorkspaceRecoveryRestoreRequestSchema,
SetVoiceModeMessageSchema,
SendAgentMessageRequestSchema,
WaitForFinishRequestSchema,
DaemonGetStatusRequestSchema,
DaemonGetPairingOfferRequestSchema,
DaemonConfigReloadRequestSchema,
HubManagementDaemonConnectRequestSchema,
HubManagementDaemonGetStatusRequestSchema,
HubManagementDaemonDisconnectRequestSchema,
HubManagementDaemonPermissionsUpdateRequestSchema,
DiagnosticsRequestSchema,
PluginCatalogGetRequestSchema,
PluginListRequestSchema,
PluginLogsGetRequestSchema,
PluginDirectoryInstallRequestSchema,
PluginDirectoryInspectRequestSchema,
PluginSourceInstallRequestSchema,
PluginSourceStatusRequestSchema,
PluginSourceUpdateRequestSchema,
PluginUpdatePreviewRequestSchema,
PluginUpdateApplyRequestSchema,
PluginReloadRequestSchema,
PluginEnableRequestSchema,
PluginDisableRequestSchema,
PluginRemoveRequestSchema,
PluginRpcInvokeRequestSchema,
AgentTimelineAppendRequestSchema,
AgentSkillsGetStatusRequestSchema,
AgentSkillsReconcileRequestSchema,
AgentSkillsUninstallRequestSchema,
AgentSkillsSaveSelectionRequestSchema,
AgentSkillsImportLegacySelectionRequestSchema,
GetDaemonConfigRequestMessageSchema,
SetDaemonConfigRequestMessageSchema,
ReadProjectConfigRequestMessageSchema,
WriteProjectConfigRequestMessageSchema,
DictationStreamStartMessageSchema,
DictationStreamChunkMessageSchema,
DictationStreamFinishMessageSchema,
DictationStreamCancelMessageSchema,
CreateAgentRequestMessageSchema,
ListProviderModelsRequestMessageSchema,
ListProviderModesRequestMessageSchema,
ListProviderFeaturesRequestMessageSchema,
ListAvailableProvidersRequestMessageSchema,
GetProvidersSnapshotRequestMessageSchema,
RefreshProvidersSnapshotRequestMessageSchema,
ProviderDiagnosticRequestMessageSchema,
ProviderUsageListRequestMessageSchema,
UsageListReportsRequestMessageSchema,
ResumeAgentRequestMessageSchema,
ImportAgentRequestMessageSchema,
RefreshAgentRequestMessageSchema,
CancelAgentRequestMessageSchema,
ShutdownServerRequestMessageSchema,
RestartServerRequestMessageSchema,
DaemonUpdateRequestMessageSchema,
FetchAgentTimelineRequestMessageSchema,
AgentTimelineSearchRequestMessageSchema,
AgentTimelineListPromptsRequestMessageSchema,
ProviderSubagentListRequestMessageSchema,
ProviderSubagentTimelineRequestMessageSchema,
SetAgentTimelineSubscriptionRequestMessageSchema,
AgentForkContextRequestMessageSchema,
SetAgentModeRequestMessageSchema,
SetAgentModelRequestMessageSchema,
SetAgentThinkingRequestMessageSchema,
SetAgentFeatureRequestMessageSchema,
AgentConfigApplyRequestMessageSchema,
AgentDetachRequestMessageSchema,
AgentRewindRequestMessageSchema,
AgentPermissionResponseMessageSchema,
CheckoutStatusRequestSchema,
CheckoutDiffGetRequestSchema,
SubscribeCheckoutDiffRequestSchema,
UnsubscribeCheckoutDiffRequestSchema,
CheckoutCommitRequestSchema,
CheckoutMergeRequestSchema,
CheckoutMergeFromBaseRequestSchema,
CheckoutPullRequestSchema,
CheckoutPushRequestSchema,
CheckoutRefreshRequestSchema,
CheckoutDiscardChangesRequestSchema,
CheckoutPrCreateRequestSchema,
CheckoutPrMergeRequestSchema,
CheckoutForgeSetAutoMergeRequestSchema,
CheckoutGithubSetAutoMergeRequestSchema,
CheckoutCommitsListRequestSchema,
CheckoutCommitFileDiffRequestSchema,
CheckoutForgeGetCheckDetailsRequestSchema,
CheckoutGithubGetCheckDetailsRequestSchema,
CheckoutPrStatusRequestSchema,
PullRequestTimelineRequestSchema,
CheckoutSwitchBranchRequestSchema,
CheckoutRenameBranchRequestSchema,
StashSaveRequestSchema,
StashPopRequestSchema,
StashListRequestSchema,
ValidateBranchRequestSchema,
BranchSuggestionsRequestSchema,
ForgeSearchRequestSchema,
GitHubSearchRequestSchema,
DirectorySuggestionsRequestSchema,
PaseoWorktreeListRequestSchema,
PaseoWorktreeArchiveRequestSchema,
CreatePaseoWorktreeRequestSchema,
WorkspaceSetupStatusRequestSchema,
WorkspaceSetupRunRequestSchema,
LegacyListAvailableEditorsRequestSchema,
LegacyOpenInEditorRequestSchema,
OpenProjectRequestSchema,
ProjectAddRequestSchema,
ProjectCreateDirectoryRequestSchema,
WorkspaceGithubSearchRepositoriesRequestSchema,
ProjectGithubCloneRequestSchema,
ArchiveWorkspaceRequestSchema,
WorkspaceCreateRequestSchema,
AgentCreateRequestSchema,
CreationSubscribeRequestSchema,
WorkspaceClearAttentionRequestSchema,
WorkspaceMarkUnreadRequestSchema,
FileExplorerRequestSchema,
FileSubscribeRequestSchema,
FileUnsubscribeRequestSchema,
FileWriteRequestSchema,
FileEntryCreateRequestSchema,
FileEntryRenameRequestSchema,
FileEntryDuplicateRequestSchema,
FileEntryDeleteRequestSchema,
ProjectIconRequestSchema,
ProjectIconGetRequestSchema,
FileDownloadTokenRequestSchema,
FileUploadRequestSchema,
ClearAgentAttentionMessageSchema,
ClientHeartbeatMessageSchema,
PingMessageSchema,
ListCommandsRequestSchema,
RegisterPushTokenMessageSchema,
PushUnregisterRequestSchema,
ListTerminalsRequestSchema,
SubscribeTerminalsRequestSchema,
UnsubscribeTerminalsRequestSchema,
CreateTerminalRequestSchema,
RenameTerminalRequestSchema,
StartWorkspaceScriptRequestSchema,
WorkspaceScriptListRequestSchema,
WorkspaceScriptStartRequestSchema,
WorkspaceScriptStopRequestSchema,
SubscribeTerminalRequestSchema,
UnsubscribeTerminalRequestSchema,
TerminalInputSchema,
KillTerminalRequestSchema,
CaptureTerminalRequestSchema,
ChatCreateRequestSchema,
ChatListRequestSchema,
ChatInspectRequestSchema,
ChatDeleteRequestSchema,
ChatPostRequestSchema,
ChatReadRequestSchema,
ChatWaitRequestSchema,
ScheduleCreateRequestSchema,
ScheduleListRequestSchema,
ScheduleInspectRequestSchema,
ScheduleLogsRequestSchema,
SchedulePauseRequestSchema,
ScheduleResumeRequestSchema,
ScheduleDeleteRequestSchema,
ScheduleRunOnceRequestSchema,
ScheduleUpdateRequestSchema,
LoopRunRequestSchema,
LoopListRequestSchema,
LoopInspectRequestSchema,
LoopLogsRequestSchema,
LoopStopRequestSchema,
]);
export type SessionInboundMessage = z.infer<typeof SessionInboundMessageSchema>;
// ============================================================================
// Session Outbound Messages (Session emits these)
// ============================================================================
export const ActivityLogPayloadSchema = z.object({
subscriptionId: z.string().optional(),
id: z.string(),
timestamp: z.coerce.date(),
type: z.enum(["transcript", "assistant", "tool_call", "tool_result", "error", "system"]),
content: z.string(),
metadata: z.record(z.string(), z.unknown()).optional(),
});
export const ActivityLogMessageSchema = z.object({
type: z.literal("activity_log"),
payload: ActivityLogPayloadSchema,
});
export const AssistantChunkMessageSchema = z.object({
type: z.literal("assistant_chunk"),
payload: z.object({
chunk: z.string(),
}),
});
export const AudioOutputMessageSchema = z.object({
type: z.literal("audio_output"),
payload: z.object({
audio: z.string(), // base64 encoded
format: z.string(),
id: z.string(),
isVoiceMode: z.boolean(), // Mode when audio was generated (for drift protection)
groupId: z.string().optional(), // Logical utterance id
chunkIndex: z.number().int().nonnegative().optional(),
isLastChunk: z.boolean().optional(),
}),
});
export const TranscriptionResultMessageSchema = z.object({
type: z.literal("transcription_result"),
payload: z.object({
text: z.string(),
language: z.string().optional(),
duration: z.number().optional(),
requestId: z.string(), // Echoed back from request for tracking
avgLogprob: z.number().optional(),
isLowConfidence: z.boolean().optional(),
byteLength: z.number().optional(),
format: z.string().optional(),
debugRecordingPath: z.string().optional(),
}),
});
export const VoiceInputStateMessageSchema = z.object({
type: z.literal("voice_input_state"),
payload: z.object({
isSpeaking: z.boolean(),
}),
});
export const DictationStreamAckMessageSchema = z.object({
type: z.literal("dictation_stream_ack"),
payload: z.object({
dictationId: z.string(),
ackSeq: z.number().int(),
}),
});
export const DictationStreamFinishAcceptedMessageSchema = z.object({
type: z.literal("dictation_stream_finish_accepted"),
payload: z.object({
dictationId: z.string(),
timeoutMs: z.number().int().positive(),
}),
});
export const DictationStreamPartialMessageSchema = z.object({
type: z.literal("dictation_stream_partial"),
payload: z.object({
dictationId: z.string(),
text: z.string(),
}),
});
export const DictationStreamFinalMessageSchema = z.object({
type: z.literal("dictation_stream_final"),
payload: z.object({
dictationId: z.string(),
text: z.string(),
debugRecordingPath: z.string().optional(),
}),
});
export const DictationStreamErrorMessageSchema = z.object({
type: z.literal("dictation_stream_error"),
payload: z.object({
dictationId: z.string(),
error: z.string(),
retryable: z.boolean(),
reasonCode: z.string().optional(),
missingModelIds: z.array(z.string()).optional(),
debugRecordingPath: z.string().optional(),
}),
});
export const ServerCapabilityStateSchema = z.object({
enabled: z.boolean(),
reason: z.string(),
});
export const ServerVoiceCapabilitiesSchema = z.object({
dictation: ServerCapabilityStateSchema,
voice: ServerCapabilityStateSchema,
});
export const ServerCapabilitiesSchema = z
.object({
voice: ServerVoiceCapabilitiesSchema.optional(),
})
.passthrough();
const ServerInfoHostnameSchema = z.unknown().transform((value): string | null => {
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
});
const ServerInfoVersionSchema = z.unknown().transform((value): string | null => {
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
});
const ServerCapabilitiesFromUnknownSchema = z
.unknown()
.optional()
.transform((value): z.infer<typeof ServerCapabilitiesSchema> | undefined => {
if (value === undefined) {
return undefined;
}
const parsed = ServerCapabilitiesSchema.safeParse(value);
if (!parsed.success) {
return undefined;
}
return parsed.data;
});
export const ServerInfoStatusPayloadSchema = z
.object({
status: z.literal("server_info"),
protocolVersion: z.number().int().optional(),
serverId: z.string().trim().min(1),
hostname: ServerInfoHostnameSchema.optional(),
version: ServerInfoVersionSchema.optional(),
// COMPAT(sessionPermissions): optional while clients support older daemons.
permissions: z.array(DaemonPermissionSchema).optional(),
// COMPAT(desktopManaged): added in v0.1.X, remove optional parsing after 2027-01-16.
desktopManaged: z.boolean().optional(),
capabilities: ServerCapabilitiesFromUnknownSchema.optional(),
// COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
features: z
.object({
// COMPAT(agentRequestReceipts): added in v0.8.0; remove gate after 2027-03-05.
agentRequestReceipts: z.boolean().optional(),
// COMPAT(workspaceRequestReceipts): added in v0.8.0; remove gate after 2027-03-07.
workspaceRequestReceipts: z.boolean().optional(),
creationLifecycle: z.boolean().optional(),
// COMPAT(hubAgentRpc): added in v0.8.0; remove gate after 2027-03-05.
hubAgentRpc: z.boolean().optional(),
providersSnapshot: z.boolean().optional(),
usageSources: z.boolean().optional(),
// COMPAT(providersSnapshotCwd): added in v0.3.2, remove gate after 2027-02-10.
providersSnapshotCwd: z.boolean().optional(),
// COMPAT(directorySync): added in v0.3.x, remove gate after 2027-02-12.
directorySync: z.boolean().optional(),
// COMPAT(workspaceLabels): added in v0.5.0, remove after 2027-08-14.
workspaceLabels: z.boolean().optional(),
// COMPAT(workspaceSetupRun): added in v0.8.0, remove gate after 2027-09-02.
workspaceSetupRun: z.boolean().optional(),
// COMPAT(workspaceTerminals): added in v0.8.0, remove gate after 2027-09-05.
workspaceTerminals: z.boolean().optional(),
// COMPAT(checkoutForgeSetAutoMerge): added in v0.2.0-beta.1. Remove the
// feature gate and checkoutGithubSetAutoMerge fallback after 2027-01-17
// once the supported daemon floor is >= v0.2.0.
checkoutForgeSetAutoMerge: z.boolean().optional(),
// COMPAT(checkoutGithubSetAutoMerge): added in v0.1.75 and retained as
// the fallback for checkoutForgeSetAutoMerge. Stop advertising and
// consuming it after 2027-01-17 once supported floors are >= v0.2.0.
checkoutGithubSetAutoMerge: z.boolean().optional(),
// COMPAT(githubCheckDetails): added in v0.1.92 and retained as the
// fallback for forgeCheckDetails. Stop advertising and consuming it
// after 2027-01-17 once supported floors are >= v0.2.0.
githubCheckDetails: z.boolean().optional(),
// COMPAT(forgeCheckDetails): added in v0.2.0-beta.1. Remove the feature
// gate and githubCheckDetails fallback after 2027-01-17 once the
// supported daemon floor is >= v0.2.0.
forgeCheckDetails: z.boolean().optional(),
// COMPAT(forgeSearch): added in v0.2.0-beta.1. Remove the feature gate
// and github_search fallback after 2027-01-17 once the supported daemon
// floor is >= v0.2.0.
forgeSearch: z.boolean().optional(),
// COMPAT(daemonStatusRpc): added in v0.1.76, remove gate after 2026-11-18.
daemonStatusRpc: z.boolean().optional(),
// COMPAT(daemonConfigReload): added in v0.4.0, remove gate after 2027-02-14.
daemonConfigReload: z.boolean().optional(),
// COMPAT(relayConfig): added in v0.2.6, remove gate after 2027-01-31.
relayConfig: z.boolean().optional(),
// COMPAT(pushTokenRevocation): added in v0.3.2, remove gate after 2027-02-10.
pushTokenRevocation: z.boolean().optional(),
// COMPAT(plugins): added in v0.3.0, remove gate after 2027-08-07.
plugins: z.boolean().optional(),
// COMPAT(pluginManagement): added in v0.4.0, remove gate after 2027-08-14.
pluginManagement: z.boolean().optional(),
// COMPAT(pluginLogs): added in v0.4.0, remove gate after 2027-08-16.
pluginLogs: z.boolean().optional(),
// COMPAT(pluginGitManagement): added in v0.7.0, remove gate after 2027-08-26.
pluginGitManagement: z.boolean().optional(),
// COMPAT(pluginSourceInstallation): added in v0.8.0; remove gate after 2027-03-16 once daemon floor supports source identifiers.
pluginSourceInstallation: z.boolean().optional(),
// COMPAT(pluginSourceUpdates): added in v0.8.0; remove gate after 2027-03-16 once daemon floor supports reviewed updates.
pluginSourceUpdates: z.boolean().optional(),
// COMPAT(pluginThemes): added in v0.5.0, remove gate after 2027-08-20.
// A daemon that predates this flag keeps `addTheme` in the server bundle it compiles,
// so a theme plugin cannot start there at all.
pluginThemes: z.boolean().optional(),
pluginSettings: z.boolean().optional(),
pluginTimelineItems: z.boolean().optional(),
// COMPAT(skillManagement): added in v0.4.0, remove gate after 2027-08-16.
skillManagement: z.boolean().optional(),
// COMPAT(terminalRestoreModes): added in v0.1.81, remove gate after 2026-11-23.
"terminal-restore-modes": z.boolean().optional(),
// COMPAT(terminalInputModeReplay): added in v0.2.6, remove gate after 2027-02-02.
"terminal-input-mode-replay": z.boolean().optional(),
// COMPAT(terminalSizeOwnership): added in v0.2.6, remove gate after 2027-02-02.
"terminal-size-ownership": z.boolean().optional(),
// COMPAT(rewind): added in v0.1.X, drop the gate when floor >= v0.1.X.
rewind: z.boolean().optional(),
// COMPAT(agentTimelinePromptIndex): added in v0.2.X, drop the gate when floor >= v0.2.X.
agentTimelinePromptIndex: z.boolean().optional(),
// COMPAT(agentHistorySearch): added in v0.3.0, remove gate after 2027-02-07.
agentHistorySearch: z.boolean().optional(),
// COMPAT(checkoutRefresh): added in v0.1.86, remove gate after 2026-11-29.
checkoutRefresh: z.boolean().optional(),
// COMPAT(workspaceMultiplicity): added in v0.1.97, drop the gate when floor >= v0.1.97
workspaceMultiplicity: z.boolean().optional(),
// COMPAT(projectRemove): added in v0.1.97, drop the gate when floor >= v0.1.97.
projectRemove: z.boolean().optional(),
// COMPAT(projectAdd): added in v0.1.97, drop the gate when floor >= v0.1.97.
projectAdd: z.boolean().optional(),
// COMPAT(worktreeRestore): added in v0.1.97, drop the gate when floor >= v0.1.97
worktreeRestore: z.boolean().optional(),
// COMPAT(workspaceRecovery): added in v0.1.105, remove after 2027-01-11 once daemon floor >= v0.1.105.
workspaceRecovery: z.boolean().optional(),
// COMPAT(workspaceFileEditing): added in v0.2.0, remove after 2027-01-18 once daemon floor >= v0.2.0.
workspaceFileEditing: z.boolean().optional(),
// COMPAT(providerUsageList): added in v0.1.98, drop the gate when daemon floor >= v0.1.98.
providerUsageList: z.boolean().optional(),
// COMPAT(agentDetach): added in v0.1.98, remove gate after 2026-12-19 once daemon floor >= v0.1.98.
agentDetach: z.boolean().optional(),
// COMPAT(agentThinkingUpdate): added in v0.2.4, remove gate after 2027-01-28.
agentThinkingUpdate: z.boolean().optional(),
// COMPAT(daemonDiagnostics): added in v0.1.100, remove gate after 2026-12-25 once daemon floor >= v0.1.100.
daemonDiagnostics: z.boolean().optional(),
// COMPAT(daemonSelfUpdate): added in v0.1.93, remove gate after 2026-12-13.
daemonSelfUpdate: z.boolean().optional(),
// COMPAT(agentForkContext): added in v0.1.102, remove gate after 2026-12-28.
agentForkContext: z.boolean().optional(),
// COMPAT(agentForkContextCursor): added in v0.1.108, remove gate after 2027-01-14.
agentForkContextCursor: z.boolean().optional(),
// COMPAT(providerSubagents): added in v0.1.107, remove gate after 2027-01-12.
providerSubagents: z.boolean().optional(),
// COMPAT(projectedSubagentTimeline): added after v0.8.0, remove gates after 2027-03-14; retain wire field.
projectedSubagentTimeline: z.boolean().optional(),
// COMPAT(providerSubagentNesting): added in v0.7, remove gate after 2027-03-04.
providerSubagentNesting: z.boolean().optional(),
// COMPAT(workspacePinning): added in v0.1.107, remove gate after 2027-01-12.
workspacePinning: z.boolean().optional(),
// COMPAT(workspaceMarkUnread): added in v0.5.0, remove after 2027-08-20.
workspaceMarkUnread: z.boolean().optional(),
// COMPAT(hubRelationship): added in v0.1.X, drop the gate when floor >= v0.1.X.
hubRelationship: z.boolean().optional(),
// COMPAT(projectGithubClone): added in v0.1.108, remove gate after 2027-01-15.
projectGithubClone: z.boolean().optional(),
// COMPAT(workspaceGithubRepositorySearch): added in v0.1.108, remove gate after 2027-01-15.
workspaceGithubRepositorySearch: z.boolean().optional(),
// COMPAT(projectCreateDirectory): added in v0.1.108, remove gate after 2027-01-15.
projectCreateDirectory: z.boolean().optional(),
// COMPAT(projectList): added in v0.2.4, drop the gate when floor >= v0.2.4.
projectList: z.boolean().optional(),
// COMPAT(commitsList): added in v0.1.110, remove gate after 2027-01-16.
commitsList: z.boolean().optional(),
// COMPAT(commitBaseClassification): added in v0.2.0, remove gate after 2027-01-23.
commitBaseClassification: z.boolean().optional(),
// COMPAT(providerRemoval): added in v0.1.105, drop the gate when floor >= v0.1.105.
providerRemoval: z.boolean().optional(),
// COMPAT(importSessionWorkspaceTarget): added in v0.1.110, remove gate after 2027-01-16.
importSessionWorkspaceTarget: z.boolean().optional(),
// COMPAT(importSessionSearch): added in v0.8.0, remove gate after 2027-03-02.
importSessionSearch: z.boolean().optional(),
// COMPAT(forgeProviders): added in v0.2.0-beta.1. Drop the gate after
// 2027-01-17 once the supported daemon floor is >= v0.2.0.
// Daemon advertises pluggable non-GitHub forge support (the forge registry);
// the client gates non-GitHub setup UI on it.
forgeProviders: z.boolean().optional(),
// COMPAT(selectiveAgentTimeline): added in v0.1.106, remove after 2027-01-12.
selectiveAgentTimeline: z.boolean().optional(),
explicitEventSubscriptions: z.boolean().optional(),
ownedSubscriptions: z.boolean().optional(),
// COMPAT(canonicalSubmittedPrompts): added in v0.2.6, remove gate after 2027-01-30.
canonicalSubmittedPrompts: z.boolean().optional(),
agentMessageProvenance: z.boolean().optional(),
// COMPAT(agentTurnIdentity): accept peers that observed pre-release v0.2.6 through 2027-01-31.
agentTurnIdentity: z.boolean().optional(),
// COMPAT(stableProjectIdentity): added in v0.1.109, remove gate after 2027-01-15.
stableProjectIdentity: z.boolean().optional(),
// COMPAT(workspaceScriptManagement): added in v0.1.105, remove gate after 2027-01-10.
workspaceScriptManagement: z.boolean().optional(),
// COMPAT(projectCustomIcon): added in v0.2.0, remove after 2027-01-20.
projectCustomIcon: z.boolean().optional(),
// COMPAT(fsEntryOps): added in v0.3.0, remove gate after 2027-02-08.
fsEntryOps: z.boolean().optional(),
// COMPAT(fsEntryDuplicate): added in v0.3.0, remove gate after 2027-02-09.
fsEntryDuplicate: z.boolean().optional(),
// COMPAT(checkoutDiscardChanges): added in v0.3.0, remove gate after 2027-02-08.
checkoutDiscardChanges: z.boolean().optional(),
// COMPAT(agentProfiles): added in v0.3.2, remove gate after 2027-02-11.
// An older daemon parses its persisted config strictly, so writing
// agentProfiles to one is silently dropped. The client hides the feature
// rather than letting a save appear to succeed.
agentProfiles: z.boolean().optional(),
// COMPAT(agentConfigApply): added in v0.3.2, remove gate after 2027-02-11.
agentConfigApply: z.boolean().optional(),
})
.optional(),
})
.passthrough()
.transform((payload) => ({
...payload,
hostname: payload.hostname ?? null,
version: payload.version ?? null,
}));
export const StatusMessageSchema = z.object({
type: z.literal("status"),
payload: z
.object({
status: z.string(),
})
.passthrough(), // Allow additional fields
});
export const PongMessageSchema = z.object({
type: z.literal("pong"),
payload: z.object({
requestId: z.string(),
clientSentAt: z.number().int().optional(),
serverReceivedAt: z.number().int(),
serverSentAt: z.number().int(),
}),
});
export const RpcErrorMessageSchema = z.object({
type: z.literal("rpc_error"),
payload: z.object({
requestId: z.string(),
requestType: z.string().optional(),
error: z.string(),
code: z.string().optional(),
}),
});
const AgentStatusWithRequestSchema = z.object({
agentId: z.string(),
requestId: z.string(),
});
const AgentStatusWithTimelineSchema = AgentStatusWithRequestSchema.extend({
timelineSize: z.number().optional(),
});
export const AgentCreatedStatusPayloadSchema = z
.object({
status: z.literal("agent_created"),
agent: AgentSnapshotPayloadSchema,
})
.extend(AgentStatusWithRequestSchema.shape);
export const AgentCreateFailedStatusPayloadSchema = z.object({
status: z.literal("agent_create_failed"),
requestId: z.string(),
error: z.string(),
errorCode: z.string().optional(),
});
export const AgentResumedStatusPayloadSchema = z
.object({
status: z.literal("agent_resumed"),
agent: AgentSnapshotPayloadSchema,
})
.extend(AgentStatusWithTimelineSchema.shape);
export const AgentRefreshedStatusPayloadSchema = z
.object({
status: z.literal("agent_refreshed"),
})
.extend(AgentStatusWithTimelineSchema.shape);
export const RestartRequestedStatusPayloadSchema = z.object({
status: z.literal("restart_requested"),
clientId: z.string(),
reason: z.string().optional(),
requestId: z.string(),
});
export const ShutdownRequestedStatusPayloadSchema = z.object({
status: z.literal("shutdown_requested"),
clientId: z.string(),
requestId: z.string(),
});
export const DaemonConfigChangedStatusPayloadSchema = z
.object({
status: z.literal("daemon_config_changed"),
config: MutableDaemonConfigSchema,
})
.passthrough();
export const PluginCatalogChangedStatusPayloadSchema = z.object({
status: z.literal("plugin_catalog_changed"),
pluginId: PluginIdSchema,
});
export const PluginSettingsChangedStatusPayloadSchema = z.object({
status: z.literal("plugin_settings_changed"),
pluginId: PluginIdSchema,
settingsId: z.string(),
});
export const KnownStatusPayloadSchema = z.discriminatedUnion("status", [
AgentCreatedStatusPayloadSchema,
AgentCreateFailedStatusPayloadSchema,
AgentResumedStatusPayloadSchema,
AgentRefreshedStatusPayloadSchema,
ShutdownRequestedStatusPayloadSchema,
RestartRequestedStatusPayloadSchema,
DaemonConfigChangedStatusPayloadSchema,
PluginCatalogChangedStatusPayloadSchema,
PluginSettingsChangedStatusPayloadSchema,
]);
export type KnownStatusPayload = z.infer<typeof KnownStatusPayloadSchema>;
export const ArtifactMessageSchema = z.object({
type: z.literal("artifact"),
payload: z.object({
type: z.enum(["markdown", "diff", "image", "code"]),
id: z.string(),
title: z.string(),
content: z.string(),
isBase64: z.boolean(),
}),
});
export const ProjectCheckoutLiteNotGitPayloadSchema = z
.object({
cwd: z.string(),
isGit: z.literal(false),
currentBranch: z.null(),
remoteUrl: z.null(),
worktreeRoot: z.null().optional(),
isPaseoOwnedWorktree: z.literal(false),
mainRepoRoot: z.null(),
})
.transform((value) => ({
...value,
worktreeRoot: null,
}));
export const ProjectCheckoutLiteGitNonPaseoPayloadSchema = z
.object({
cwd: z.string(),
isGit: z.literal(true),
currentBranch: z.string().nullable(),
remoteUrl: z.string().nullable(),
worktreeRoot: z.string().optional(),
isPaseoOwnedWorktree: z.literal(false),
mainRepoRoot: z.string().nullable().optional().default(null),
})
.transform((value) => ({
...value,
worktreeRoot: value.worktreeRoot ?? value.cwd,
}));
export const ProjectCheckoutLiteGitPaseoPayloadSchema = z
.object({
cwd: z.string(),
isGit: z.literal(true),
currentBranch: z.string().nullable(),
remoteUrl: z.string().nullable(),
worktreeRoot: z.string().optional(),
isPaseoOwnedWorktree: z.literal(true),
mainRepoRoot: z.string(),
})
.transform((value) => ({
...value,
worktreeRoot: value.worktreeRoot ?? value.cwd,
}));
export const ProjectCheckoutLitePayloadSchema = z.union([
ProjectCheckoutLiteNotGitPayloadSchema,
ProjectCheckoutLiteGitNonPaseoPayloadSchema,
ProjectCheckoutLiteGitPaseoPayloadSchema,
]);
export const ProjectPlacementPayloadSchema = z.object({
projectKey: z.string(),
projectName: z.string(),
workspaceName: z.string().nullable().optional(),
checkout: ProjectCheckoutLitePayloadSchema,
});
export const WorkspaceScriptLifecycleSchema = z.enum(["running", "stopped"]);
export const WorkspaceScriptHealthSchema = z.enum(["healthy", "unhealthy"]);
export const WorkspaceScriptPayloadSchema = z.object({
scriptName: z.string(),
type: z.enum(["script", "service"]).optional().default("service"),
hostname: z.string(),
port: z.number().int().positive().nullable(),
localProxyUrl: z.string().nullable().optional(),
publicProxyUrl: z.string().nullable().optional(),
proxyUrl: z.string().nullable().optional().default(null),
lifecycle: WorkspaceScriptLifecycleSchema,
health: WorkspaceScriptHealthSchema.nullable(),
exitCode: z.number().nullable().optional().default(null),
terminalId: z.string().nullable().optional().default(null),
});
const WorkspaceGitRuntimePayloadSchema = z
.object({
currentBranch: z.string().nullable().optional(),
remoteUrl: z.string().nullable().optional(),
isPaseoOwnedWorktree: z.boolean().optional(),
isDirty: z.boolean().nullable().optional(),
aheadBehind: z
.object({
ahead: z.number(),
behind: z.number(),
})
.nullable()
.optional(),
aheadOfOrigin: z.number().nullable().optional(),
behindOfOrigin: z.number().nullable().optional(),
})
.optional()
.nullable();
export const WorkspaceGitHubRuntimePayloadSchema = z
.object({
featuresEnabled: z.boolean().optional(),
pullRequest: z
.object({
number: z.number().optional(),
url: z.string(),
title: z.string(),
state: z.string(),
baseRefName: z.string(),
headRefName: z.string(),
isMerged: z.boolean(),
isDraft: z.boolean().optional(),
mergeable: z.enum(["MERGEABLE", "CONFLICTING", "UNKNOWN"]).catch("UNKNOWN").optional(),
checks: z
.array(
z.object({
name: z.string(),
status: z.enum(["success", "failure", "pending", "skipped", "cancelled"]),
url: z.string().nullable(),
workflow: z.string().optional(),
duration: z.string().optional(),
// Open so future forge-neutral refinements remain parse-compatible.
traits: z.array(z.string()).optional(),
}),
)
.optional(),
checksStatus: z.enum(["none", "pending", "success", "failure"]).optional(),
reviewDecision: z.enum(["approved", "changes_requested", "pending"]).nullable().optional(),
repoOwner: z.string().optional(),
repoName: z.string().optional(),
github: z.unknown().optional(),
})
.nullable()
.optional(),
error: z
.object({
message: z.string(),
})
.nullable()
.optional(),
refreshedAt: z.string().nullable().optional(),
})
.optional()
.nullable();
export const WorkspaceDescriptorPayloadSchema = z
.object({
id: z.string(),
projectId: z.string(),
projectDisplayName: z.string(),
// COMPAT(projectCustomName): added in v0.1.76, drop the optional gate when floor >= v0.1.76.
// When the user has renamed a project, projectDisplayName carries the resolved
// value (customName) and projectCustomName mirrors the raw override so the
// settings UI can prefill its input and offer a "reset" action.
projectCustomName: z.string().nullable().optional(),
// Identifies the project's stored custom icon; null means automatic.
// COMPAT(projectCustomIcon): added in v0.2.0, remove after 2027-01-20.
projectCustomIconRevision: z.string().nullable().optional(),
projectRootPath: z.string(),
workspaceDirectory: z.string().optional(),
// COMPAT(worktreeSlug): added in v0.2.6, remove optional after 2027-01-31.
// Present only for Paseo-owned worktrees; this is the basename of their root directory.
worktreeSlug: z.string().optional(),
projectKind: z.enum(["git", "non_git", "directory"]),
// COMPAT(workspaces): keep legacy directory workspace kind parseable.
workspaceKind: z.enum(["directory", "local_checkout", "checkout", "worktree"]),
name: z.string(),
// COMPAT(workspaceTitles): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
// When the user has titled a workspace, `name` carries the resolved value
// (title) and `title` mirrors the raw override so the rename UI can prefill
// its input and offer a "reset to branch name" action. Null means the name
// is derived from the branch/directory.
title: z.string().nullable().optional(),
// COMPAT(workspacePinning): added in v0.1.107, remove optional after 2027-01-12.
pinnedAt: z.string().nullable().optional(),
// COMPAT(workspaceLabels): added in v0.5.0, remove optional after 2027-08-14.
labels: z.array(z.string()).optional(),
archivingAt: z.string().nullable().optional().default(null),
status: WorkspaceStateBucketSchema,
// Best-effort workspace status entry timestamp. Old daemons omit the
// field; old clients treat missing and null equivalently. The transform
// coerces a missing field to `null` so downstream code never has to
// handle `undefined`.
statusEnteredAt: z
.string()
.nullish()
.transform((value) => value ?? null),
activityAt: z.string().nullable(),
diffStat: z
.object({
additions: z.number(),
deletions: z.number(),
})
.nullable()
.optional(),
scripts: z.array(WorkspaceScriptPayloadSchema).default([]),
gitRuntime: WorkspaceGitRuntimePayloadSchema,
// COMPAT(githubRuntimeName): legacy wire-field name now carries
// forge-neutral runtime data. Introduce and migrate to a neutral
// forgeRuntime field before consumers stop using this name. Target cleanup
// after 2027-01-17 once the supported client floor is >= v0.2.0.
githubRuntime: WorkspaceGitHubRuntimePayloadSchema,
// COMPAT(forge): added in v0.2.0-beta.1. Treat an absent forge as GitHub
// until 2027-01-17; remove the consumer fallback once the supported daemon
// floor is >= v0.2.0.
forge: z.string().optional(),
project: ProjectPlacementPayloadSchema.optional(),
// COMPAT(directorySync): sequence of this latest directory projection.
syncSeq: z.number().int().positive().optional(),
})
.transform((workspace) => ({
...workspace,
workspaceDirectory: workspace.workspaceDirectory ?? workspace.projectRootPath,
}));
export const AgentUpdateMessageSchema = z.object({
type: z.literal("agent_update"),
payload: z.discriminatedUnion("kind", [
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("upsert"),
agent: AgentSnapshotPayloadSchema,
project: ProjectPlacementPayloadSchema.nullable().optional(),
generation: z.string().optional(),
seq: z.number().int().positive().optional(),
}),
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("remove"),
agentId: z.string(),
generation: z.string().optional(),
seq: z.number().int().positive().optional(),
}),
]),
});
export const AgentStreamMessageSchema = z.object({
type: z.literal("agent_stream"),
payload: z.object({
subscriptionId: z.string().optional(),
agentId: z.string(),
event: AgentStreamEventPayloadSchema,
timestamp: z.string(),
// Present for timeline events. Maps 1:1 to canonical in-memory timeline rows.
seq: z.number().int().nonnegative().optional(),
epoch: z.string().optional(),
}),
});
export const AgentStatusMessageSchema = z.object({
type: z.literal("agent_status"),
payload: z.object({
agentId: z.string(),
status: z.string(),
info: AgentSnapshotPayloadSchema,
}),
});
export const AgentListMessageSchema = z.object({
type: z.literal("agent_list"),
payload: z.object({
agents: z.array(AgentSnapshotPayloadSchema),
}),
});
export const AgentSearchMatchFieldSchema = z.enum(["workspace", "title", "branch", "project"]);
export const AgentSearchMatchSchema = z.object({
field: AgentSearchMatchFieldSchema,
ranges: z.array(
z.object({
start: z.number().int().nonnegative(),
length: z.number().int().positive(),
}),
),
});
export type AgentSearchMatch = z.infer<typeof AgentSearchMatchSchema>;
const AgentDirectoryResponseEntrySchema = z.object({
agent: AgentSnapshotPayloadSchema,
project: ProjectPlacementPayloadSchema,
// Legacy relevance metadata remains accepted from older daemons.
// Current history responses use the requested chronological sort.
searchScore: z.number().optional(),
// Legacy server-generated highlights. Current clients highlight displayed text locally.
searchMatches: z.array(AgentSearchMatchSchema).optional(),
// COMPAT(directorySync): sequence of this latest directory projection.
syncSeq: z.number().int().positive().optional(),
});
const AgentDirectoryPageInfoSchema = z.object({
nextCursor: z.string().nullable(),
prevCursor: z.string().nullable(),
hasMore: z.boolean(),
});
export const FetchAgentsResponseMessageSchema = z.object({
type: z.literal("fetch_agents_response"),
payload: z.object({
requestId: z.string(),
subscriptionId: z.string().nullable().optional(),
entries: z.array(AgentDirectoryResponseEntrySchema),
pageInfo: AgentDirectoryPageInfoSchema,
sync: DirectorySyncMetadataSchema.optional(),
}),
});
export const FetchAgentHistoryResponseMessageSchema = z.object({
type: z.literal("fetch_agent_history_response"),
payload: z.object({
requestId: z.string(),
entries: z.array(AgentDirectoryResponseEntrySchema),
pageInfo: AgentDirectoryPageInfoSchema,
// Older daemons truncate relevance-ranked searches instead of returning a cursor.
searchTruncated: z.boolean().optional(),
}),
});
export const FetchRecentProviderSessionsResponseMessageSchema = z.object({
type: z.literal("fetch_recent_provider_sessions_response"),
payload: z.object({
requestId: z.string(),
entries: z.array(RecentProviderSessionDescriptorPayloadSchema),
filteredAlreadyImportedCount: z.number().int().nonnegative().optional(),
providerErrors: z
.array(
z.object({
provider: z.string(),
message: z.string(),
}),
)
.optional(),
}),
});
// COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
// A project parent that has zero active workspaces. The sidebar renders the
// project row with a new-workspace child so projects persist after their last
// workspace is archived.
export const WorkspaceProjectDescriptorPayloadSchema = z.object({
projectId: z.string(),
// COMPAT(projectKey): added in v0.2.4 on 2026-07-28; remove optional after 2027-01-28.
projectKey: z.string().optional(),
projectDisplayName: z.string(),
projectCustomName: z.string().nullable().optional(),
// COMPAT(projectCustomIcon): added in v0.2.0, remove after 2027-01-20.
projectCustomIconRevision: z.string().nullable().optional(),
// Fingerprints the effective icon, including automatic discovery and the
// absence of an icon. Clients may persist icon results against this value.
// COMPAT(projectIconCache): added in v0.2.7, remove optional after 2027-02-12.
projectIconRevision: z.string().optional(),
projectRootPath: z.string(),
projectKind: z.enum(["git", "non_git", "directory"]),
// COMPAT(directorySync): sequence of this latest directory projection.
syncSeq: z.number().int().positive().optional(),
});
export const FetchWorkspacesResponseMessageSchema = z.object({
type: z.literal("fetch_workspaces_response"),
payload: z.object({
requestId: z.string(),
subscriptionId: z.string().nullable().optional(),
entries: z.array(WorkspaceDescriptorPayloadSchema),
// COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
// Project parents with no active workspaces. Old daemons omit it; old clients
// ignore it. Only populated on the first page (no cursor).
emptyProjects: z.array(WorkspaceProjectDescriptorPayloadSchema).optional().default([]),
pageInfo: z.object({
nextCursor: z.string().nullable(),
prevCursor: z.string().nullable(),
hasMore: z.boolean(),
}),
sync: DirectorySyncMetadataSchema.optional(),
}),
});
export const WorkspaceUpdateMessageSchema = z.object({
type: z.literal("workspace_update"),
payload: z.discriminatedUnion("kind", [
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("upsert"),
workspace: WorkspaceDescriptorPayloadSchema,
generation: z.string().optional(),
seq: z.number().int().positive().optional(),
}),
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("remove"),
id: z.string(),
// COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
// When archiving this workspace leaves its project with no active
// workspaces, the daemon includes the project parent so the sidebar keeps
// rendering it without waiting for a full re-hydration. Old daemons omit
// it; old clients ignore it and surface the project on their next
// workspace fetch instead.
emptyProject: WorkspaceProjectDescriptorPayloadSchema.optional(),
// Project removal is represented on the existing workspace update channel
// so old clients can still parse the message and ignore the extra field.
removedProjectId: z.string().optional(),
generation: z.string().optional(),
seq: z.number().int().positive().optional(),
}),
]),
});
const WorkspaceLabelSyncMetadataSchema = z.object({
mode: z.enum(["snapshot", "changes"]),
generation: z.string(),
headSeq: z.number().int().nonnegative(),
removals: z.array(z.object({ name: z.string(), seq: z.number().int().positive() })),
});
export const WorkspaceLabelListResponseSchema = z.object({
type: z.literal("workspace.label.list.response"),
payload: z.object({
requestId: z.string(),
subscriptionId: z.string().optional(),
labels: z.array(WorkspaceLabelDefinitionSchema),
sync: WorkspaceLabelSyncMetadataSchema,
}),
});
export const WorkspaceLabelUpdateSchema = z.object({
type: z.literal("workspace.label.update"),
payload: z.discriminatedUnion("kind", [
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("upsert"),
label: WorkspaceLabelDefinitionSchema,
previousName: z.string().optional(),
generation: z.string(),
seq: z.number().int().positive(),
}),
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("remove"),
name: z.string(),
generation: z.string(),
seq: z.number().int().positive(),
}),
]),
});
export const WorkspaceLabelAssignmentSetResponseSchema = z.object({
type: z.literal("workspace.label.assignment.set.response"),
payload: z.object({
requestId: z.string(),
label: WorkspaceLabelDefinitionSchema,
workspaceLabels: z.array(z.string()),
}),
});
export const WorkspaceLabelUpdateResponseSchema = z.object({
type: z.literal("workspace.label.update.response"),
payload: z.object({
requestId: z.string(),
label: WorkspaceLabelDefinitionSchema,
affectedWorkspaceCount: z.number().int().nonnegative(),
}),
});
export const WorkspaceLabelDeleteResponseSchema = z.object({
type: z.literal("workspace.label.delete.response"),
payload: z.object({
requestId: z.string(),
affectedWorkspaceCount: z.number().int().nonnegative(),
}),
});
export const WorkspaceLabelDeleteInspectResponseSchema = z.object({
type: z.literal("workspace.label.delete.inspect.response"),
payload: z.object({
requestId: z.string(),
affectedWorkspaceCount: z.number().int().nonnegative(),
}),
});
export const ProjectUpdateMessageSchema = z.object({
type: z.literal("project.update"),
payload: z.discriminatedUnion("kind", [
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("upsert"),
project: WorkspaceProjectDescriptorPayloadSchema,
generation: z.string().optional(),
seq: z.number().int().positive().optional(),
}),
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("remove"),
projectId: z.string(),
generation: z.string().optional(),
seq: z.number().int().positive().optional(),
}),
]),
});
export const ProjectListResponseMessageSchema = z.object({
type: z.literal("project.list.response"),
payload: z.object({
requestId: z.string(),
projects: z.array(WorkspaceProjectDescriptorPayloadSchema),
sync: DirectorySyncMetadataSchema.optional(),
}),
});
export const ScriptStatusUpdateMessageSchema = z.object({
type: z.literal("script_status_update"),
payload: z.object({
subscriptionId: z.string().optional(),
workspaceId: z.string(),
scripts: z.array(WorkspaceScriptPayloadSchema),
}),
});
export const WorkspaceSetupProgressMessageSchema = z.object({
type: z.literal("workspace_setup_progress"),
payload: z.object({
subscriptionId: z.string().optional(),
workspaceId: z.string(),
status: z.enum(["running", "completed", "failed", "blocked"]),
detail: WorktreeSetupDetailPayloadSchema,
error: z.string().nullable(),
blockedSource: z
.object({
kind: z.literal("change_request"),
forge: z.string(),
number: z.number().int().positive(),
headRepository: z.string(),
})
.optional(),
}),
});
export const WorkspaceSetupSnapshotSchema = z.object({
status: z.enum(["running", "completed", "failed", "blocked"]),
detail: WorktreeSetupDetailPayloadSchema,
error: z.string().nullable(),
blockedSource: z
.object({
kind: z.literal("change_request"),
forge: z.string(),
number: z.number().int().positive(),
headRepository: z.string(),
})
.optional(),
});
export const WorkspaceSetupRunResponseMessageSchema = z.object({
type: z.literal("workspace.setup.run.response"),
payload: z.object({
requestId: z.string(),
workspaceId: z.string(),
started: z.boolean(),
error: z.string().nullable(),
}),
});
export const WorkspaceSetupStatusResponseMessageSchema = z.object({
type: z.literal("workspace_setup_status_response"),
payload: z.object({
requestId: z.string(),
workspaceId: z.string(),
snapshot: WorkspaceSetupSnapshotSchema.nullable(),
}),
});
export const OpenProjectResponseMessageSchema = z.object({
type: z.literal("open_project_response"),
payload: z.object({
requestId: z.string(),
workspace: WorkspaceDescriptorPayloadSchema.nullable(),
error: z.string().nullable(),
// Unknown codes from newer daemons degrade to null; clients fall back to `error`.
errorCode: z.enum(["directory_not_found"]).nullish().catch(null),
}),
});
export const ProjectAddResponseSchema = z.object({
type: z.literal("project.add.response"),
payload: z.object({
requestId: z.string(),
project: WorkspaceProjectDescriptorPayloadSchema.nullable(),
error: z.string().nullable(),
errorCode: z.enum(["directory_not_found"]).nullish().catch(null),
}),
});
export const ProjectCreateDirectoryErrorCodeSchema = z.enum([
"invalid_name",
"parent_directory_not_found",
"directory_exists",
"permission_denied",
"registration_failed",
"filesystem_error",
]);
export const ProjectCreateDirectoryResponseSchema = z.object({
type: z.literal("project.create_directory.response"),
payload: z.object({
requestId: z.string(),
directoryPath: z.string().nullable(),
project: WorkspaceProjectDescriptorPayloadSchema.nullable(),
error: z.string().nullable(),
// Error codes are open-ended on the wire so older clients can still parse
// responses after a newer daemon learns another failure reason.
errorCode: z.string().nullable(),
}),
});
export const WorkspaceGithubSearchRepositoriesResponseSchema = z.object({
type: z.literal("workspace.github.search_repositories.response"),
payload: z.discriminatedUnion("status", [
z.object({
status: z.literal("success"),
requestId: z.string(),
repositories: z.array(GithubRepositorySchema),
available: z.literal(true),
error: z.null(),
}),
z.object({
status: z.literal("unavailable"),
requestId: z.string(),
repositories: z.array(GithubRepositorySchema),
reason: z.literal("gh_missing"),
available: z.literal(false),
error: z.string(),
}),
z.object({
status: z.literal("unauthenticated"),
requestId: z.string(),
repositories: z.array(GithubRepositorySchema),
available: z.literal(false),
error: z.string(),
}),
z.object({
status: z.literal("error"),
requestId: z.string(),
repositories: z.array(GithubRepositorySchema),
available: z.literal(true),
error: z.string(),
}),
]),
});
export const ProjectGithubCloneResponseSchema = z.object({
type: z.literal("project.github.clone.response"),
payload: z.object({
requestId: z.string(),
repo: z.string().trim().min(MIN_REPOSITORY_PATH_LENGTH),
checkoutPath: z.string().nullable(),
project: WorkspaceProjectDescriptorPayloadSchema.nullable(),
error: z.string().nullable(),
}),
});
export const StartWorkspaceScriptResponseMessageSchema = z.object({
type: z.literal("start_workspace_script_response"),
payload: z.object({
requestId: z.string(),
workspaceId: z.string(),
scriptName: z.string(),
terminalId: z.string().nullable(),
error: z.string().nullable(),
}),
});
const WorkspaceScriptOperationPayloadSchema = z.object({
requestId: z.string(),
workspaceId: z.string(),
scriptName: z.string().optional(),
script: WorkspaceScriptPayloadSchema.nullable().optional(),
scripts: z.array(WorkspaceScriptPayloadSchema).optional(),
error: z.string().nullable(),
});
export const WorkspaceScriptListResponseMessageSchema = z.object({
type: z.literal("workspace.script.list.response"),
payload: WorkspaceScriptOperationPayloadSchema,
});
export const WorkspaceScriptStartResponseMessageSchema = z.object({
type: z.literal("workspace.script.start.response"),
payload: WorkspaceScriptOperationPayloadSchema,
});
export const WorkspaceScriptStopResponseMessageSchema = z.object({
type: z.literal("workspace.script.stop.response"),
payload: WorkspaceScriptOperationPayloadSchema,
});
// COMPAT(desktopEditorBridge): added in v0.1.88, remove after 2026-12-03 once old clients no longer parse daemon editor RPC responses.
export const LegacyListAvailableEditorsResponseMessageSchema = z.object({
type: z.literal("list_available_editors_response"),
payload: z.object({
requestId: z.string(),
editors: z.array(
z.object({
id: z.string().trim().min(1),
label: z.string(),
}),
),
error: z.string().nullable(),
}),
});
export const LegacyOpenInEditorResponseMessageSchema = z.object({
type: z.literal("open_in_editor_response"),
payload: z.object({
requestId: z.string(),
error: z.string().nullable(),
}),
});
export const ArchiveWorkspaceResponseMessageSchema = z.object({
type: z.literal("archive_workspace_response"),
payload: z.object({
requestId: z.string(),
workspaceId: z.string(),
archivedAt: z.string().nullable(),
error: z.string().nullable(),
}),
});
export const FetchAgentResponseMessageSchema = z.object({
type: z.literal("fetch_agent_response"),
payload: z.object({
requestId: z.string(),
agent: AgentSnapshotPayloadSchema.nullable(),
project: ProjectPlacementPayloadSchema.nullable().optional(),
error: z.string().nullable(),
}),
});
const AgentTimelineSeqRangeSchema = z.object({
startSeq: z.number().int().nonnegative(),
endSeq: z.number().int().nonnegative(),
});
export const AgentTimelineEntryPayloadSchema = z.object({
provider: AgentProviderSchema,
item: AgentTimelineItemPayloadSchema,
turnId: z.string().optional(),
timestamp: z.string(),
seqStart: z.number().int().nonnegative(),
seqEnd: z.number().int().nonnegative(),
sourceSeqRanges: z.array(AgentTimelineSeqRangeSchema),
collapsed: z.array(z.enum(["assistant_merge", "reasoning_merge", "tool_lifecycle", "identity"])),
});
export const FetchAgentTimelineResponseMessageSchema = z.object({
type: z.literal("fetch_agent_timeline_response"),
payload: z.object({
requestId: z.string(),
agentId: z.string(),
agent: AgentSnapshotPayloadSchema.nullable(),
direction: z.enum(["tail", "before", "after"]),
projection: z.enum(["projected", "canonical"]),
epoch: z.string(),
reset: z.boolean(),
staleCursor: z.boolean(),
gap: z.boolean(),
window: z.object({
minSeq: z.number().int().nonnegative(),
maxSeq: z.number().int().nonnegative(),
nextSeq: z.number().int().nonnegative(),
}),
startCursor: AgentTimelineCursorSchema.nullable(),
endCursor: AgentTimelineCursorSchema.nullable(),
hasOlder: z.boolean(),
hasNewer: z.boolean(),
mergeWindow: z.boolean().optional(),
entries: z.array(AgentTimelineEntryPayloadSchema),
error: z.string().nullable(),
}),
});
export const AgentTimelineReplacementMessageSchema = z.object({
type: z.literal("agent.timeline.replacement"),
payload: z.object({
subscriptionId: z.string().optional(),
agentId: z.string(),
epoch: z.string(),
}),
});
export const AgentTimelineSearchResponseMessageSchema = z.object({
type: z.literal("agent.timeline.search.response"),
payload: z.object({
requestId: z.string(),
agentId: z.string(),
epoch: z.string(),
locations: z.array(
z.object({
seq: z.number().int().nonnegative(),
role: z.enum(["user", "assistant"]),
// Estimated occurrences in the message; the client verifies against rendered text.
// COMPAT(timelineSearchCount): added in v0.9.0, remove optional after 2027-09-22.
count: z.number().int().positive().optional(),
}),
),
nextCursor: z.number().int().nonnegative().nullable(),
error: z.string().nullable(),
}),
});
export const AgentTimelineListPromptsResponseMessageSchema = z.object({
type: z.literal("agent.timeline.list_prompts.response"),
payload: z.object({
requestId: z.string(),
agentId: z.string(),
epoch: z.string(),
prompts: z.array(
z.object({
seq: z.number().int().nonnegative(),
timestamp: z.string(),
preview: z.string(),
}),
),
error: z.string().nullable(),
}),
});
export const ProviderSubagentDescriptorPayloadSchema = z.object({
id: z.string(),
parentAgentId: z.string(),
// COMPAT(providerSubagentNesting): added in v0.7, remove optional after 2027-03-04.
parentSubagentId: z.string().nullable().optional(),
provider: AgentProviderSchema,
title: z.string().nullable(),
description: z.string().nullable(),
status: z.enum(["running", "completed", "failed", "canceled"]),
createdAt: z.string(),
updatedAt: z.string(),
toolCallId: z.string().nullable(),
cwd: z.string().nullable().optional(),
// Compact provider-owned context for the shared track. Providers choose what belongs here and
// format it for display; clients must not parse provider-specific facts out of this string.
subtitle: z.string().nullable().optional(),
});
export type ProviderSubagentDescriptorPayload = z.infer<
typeof ProviderSubagentDescriptorPayloadSchema
>;
export const ProviderSubagentListResponseMessageSchema = z.object({
type: z.literal("agent.provider_subagents.list.response"),
payload: z.object({
requestId: z.string(),
parentAgentId: z.string(),
subagents: z.array(ProviderSubagentDescriptorPayloadSchema),
error: z.string().nullable(),
}),
});
export const ProviderSubagentTimelineResponseMessageSchema = z.object({
type: z.literal("agent.provider_subagents.timeline.get.response"),
payload: z.object({
requestId: z.string(),
parentAgentId: z.string(),
subagentId: z.string(),
provider: AgentProviderSchema.nullable(),
direction: z.enum(["tail", "before", "after"]),
epoch: z.string(),
projection: z.literal("projected").optional(),
startCursor: AgentTimelineCursorSchema.nullable().optional(),
endCursor: AgentTimelineCursorSchema.nullable().optional(),
reset: z.boolean(),
staleCursor: z.boolean(),
gap: z.boolean(),
window: z.object({
minSeq: z.number().int().nonnegative(),
maxSeq: z.number().int().nonnegative(),
nextSeq: z.number().int().nonnegative(),
}),
hasOlder: z.boolean(),
hasNewer: z.boolean(),
rows: z.array(
z.object({
item: AgentTimelineItemPayloadSchema,
timestamp: z.string(),
seq: z.number().int().nonnegative(),
seqStart: z.number().int().nonnegative().optional(),
seqEnd: z.number().int().nonnegative().optional(),
sourceSeqRanges: z
.array(
z.object({
startSeq: z.number().int().nonnegative(),
endSeq: z.number().int().nonnegative(),
}),
)
.optional(),
}),
),
error: z.string().nullable(),
}),
});
export const ProviderSubagentUpdateMessageSchema = z.object({
type: z.literal("agent.provider_subagents.update"),
payload: z.discriminatedUnion("kind", [
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("upsert"),
subagent: ProviderSubagentDescriptorPayloadSchema,
}),
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("timeline"),
parentAgentId: z.string(),
subagentId: z.string(),
provider: AgentProviderSchema,
item: AgentTimelineItemPayloadSchema,
timestamp: z.string(),
seq: z.number().int().nonnegative(),
epoch: z.string(),
}),
z.object({
subscriptionId: z.string().optional(),
kind: z.literal("remove"),
parentAgentId: z.string(),
subagentId: z.string(),
}),
]),
});
export const SetAgentTimelineSubscriptionResponseMessageSchema = z.object({
type: z.literal("agent.timeline.set_subscription.response"),
payload: z.object({
subscriptionId: z.string().optional(),
agentIds: z.array(z.string()),
requestId: z.string(),
}),
});
export const AgentAttentionRequiredMessageSchema = z.object({
type: z.literal("agent_attention_required"),
payload: z.object({
subscriptionId: z.string().optional(),
agentId: z.string(),
reason: z.enum(["finished", "error", "permission"]),
timestamp: z.string(),
shouldNotify: z.boolean(),
notification: z
.object({
title: z.string(),
body: z.string(),
data: z.object({
serverId: z.string(),
workspaceId: z.string().optional(),
agentId: z.string(),
reason: z.enum(["finished", "error", "permission"]),
}),
})
.optional(),
}),
});
export const AgentForkContextResponseMessageSchema = z.object({
type: z.literal("agent.fork_context.response"),
payload: z.object({
requestId: z.string(),
agentId: z.string(),
attachment: TextAttachmentSchema.nullable(),
itemCount: z.number().int().nonnegative(),
boundaryMessageId: z.string().nullable(),
boundaryCursor: AgentTimelineCursorSchema.nullable().optional(),
error: z.string().nullable(),
}),
});
export const CancelAgentResponseMessageSchema = z.object({
type: z.literal("cancel_agent_response"),
payload: z.object({
requestId: z.string(),
agentId: z.string(),
agent: AgentSnapshotPayloadSchema.nullable(),
error: z.string().nullable().optional(),
}),
});
export const ClearAgentAttentionResponseMessageSchema = z.object({
type: z.literal("clear_agent_attention_response"),
payload: z.object({
requestId: z.string(),
agentId: z.string().or(z.array(z.string())),
agents: z.array(AgentSnapshotPayloadSchema),
}),
});
export const CreationSnapshotSchema = z.object({
kind: z.enum(["workspace", "agent"]),
idempotencyKey: z.string(),
revision: z.number().int().nonnegative(),
phase: z.enum([
"accepted",
"workspace_ready",
"agent_ready",
"prompt_started",
"completed",
"failed",
]),
workspaceId: z.string().nullable(),
agentId: z.string().nullable(),
workspace: WorkspaceDescriptorPayloadSchema.optional(),
agent: AgentSnapshotPayloadSchema.optional(),
error: z.string().nullable(),
setupSkippedReason: z.string().optional(),
errorCode: z.string().optional(),
failedStage: z.enum(["workspace", "agent", "prompt"]).optional(),
outcomeUnknown: z.boolean().optional(),
});
export type CreationSnapshot = z.infer<typeof CreationSnapshotSchema>;
export type AgentCreateRequest = z.infer<typeof AgentCreateRequestSchema>;
export type WorkspaceInitialAgent = z.infer<typeof WorkspaceInitialAgentSchema>;
export const WorkspaceCreationUpdateSchema = z.object({
type: z.literal("workspace.create.update"),
payload: CreationSnapshotSchema.extend({ subscriptionId: z.string().optional() }),
});
export const AgentCreationUpdateSchema = WorkspaceCreationUpdateSchema.extend({
type: z.literal("agent.create.update"),
});
export const CreationSubscribeResponseSchema = z.object({
type: z.literal("creation.subscribe.response"),
payload: z.object({
requestId: z.string(),
subscriptionId: z.string().optional(),
snapshot: CreationSnapshotSchema.nullable(),
error: z.string().nullable(),
}),
});
export const AgentCreateResponseSchema = z.object({
type: z.literal("agent.create.response"),
payload: z.object({
requestId: z.string(),
agent: AgentSnapshotPayloadSchema.nullable(),
error: z.string().nullable(),
creation: CreationSnapshotSchema.optional(),
}),
});
export const WorkspaceCreateResponseSchema = z.object({
type: z.literal("workspace.create.response"),
payload: z.object({
agent: AgentSnapshotPayloadSchema.optional(),
creation: CreationSnapshotSchema.optional(),
workspace: WorkspaceDescriptorPayloadSchema.nullable(),
setupTerminalId: z.string().nullable(),
setupSkippedReason: z.string().optional(),
error: z.string().nullable(),
errorCode: z.string().optional(),
requestId: z.string(),
}),
});
export const WorkspaceClearAttentionResponseSchema = z.object({
type: z.literal("workspace.clear_attention.response"),
payload: z.object({
requestId: z.string(),
workspaceId: z.union([z.string(), z.array(z.string())]),
clearedAgentIds: z.array(z.string()),
results: z.array(
z.object({
workspaceId: z.string(),
clearedAgentIds: z.array(z.string()),
success: z.boolean(),
error: z.string().nullable(),
}),
),
success: z.boolean(),
error: z.string().nullable(),
}),
});
export const WorkspaceMarkUnreadResponseSchema = z.object({
type: z.literal("workspace.mark_unread.response"),
payload: z.object({
requestId: z.string(),
workspaceId: z.string(),
markedAgentId: z.string().nullable(),
success: z.boolean(),
error: z.string().nullable(),
}),
});
export const SendAgentMessageResponseMessageSchema = z.object({
type: z.literal("send_agent_message_response"),
payload: z.object({
requestId: z.string(),
agentId: z.string(),
accepted: z.boolean(),
error: z.string().nullable(),
}),
});
export const WaitForFinishResponseMessageSchema = z.object({
type: z.literal("wait_for_finish_response"),
payload: z.object({
requestId: z.string(),
status: z.enum(["idle", "error", "permission", "timeout"]),
final: AgentSnapshotPayloadSchema.nullable(),
error: z.string().nullable(),
lastMessage: z.string().nullable(),
}),
});
export const GetDaemonConfigResponseMessageSchema = z.object({
type: z.literal("get_daemon_config_response"),
payload: z
.object({
requestId: z.string(),
config: MutableDaemonConfigSchema,
})
.passthrough(),
});
export const DaemonGetStatusResponseSchema = z.object({
type: z.literal("daemon.get_status.response"),
payload: z
.object({
requestId: z.string(),
serverId: z.string(),
version: z.string().nullable().optional(),
pid: z.number(),
nodePath: z.string(),
startedAt: z.string().nullable().optional(),
listen: z.string().nullable(),
relay: z
.object({
enabled: z.boolean(),
endpoint: z.string(),
publicEndpoint: z.string(),
useTls: z.boolean(),
publicUseTls: z.boolean(),
})
.nullable()
.optional(),
providers: z.array(
z.object({
provider: z.string(),
available: z.boolean(),
error: z.string().nullable().optional(),
}),
),
})
.passthrough(),
});
export const HubRelationshipStatusSchema = z.object({
state: z.enum([
"not_connected",
"connecting",
"connected",
"reconnecting",
"disconnecting",
"revoked",
]),
daemonId: z.string().nullable(),
hubOrigin: z.string().nullable(),
permissions: z.array(DaemonPermissionSchema),
connectedAt: z.string().nullable(),
lastError: z.string().nullable(),
});
export const HubManagementDaemonConnectResponseSchema = z.object({
type: z.literal("hub.management.daemon.connect.response"),
payload: z.object({ requestId: z.string(), status: HubRelationshipStatusSchema }),
});
export const HubManagementDaemonGetStatusResponseSchema = z.object({
type: z.literal("hub.management.daemon.get_status.response"),
payload: z.object({ requestId: z.string(), status: HubRelationshipStatusSchema }),
});
export const HubManagementDaemonDisconnectResponseSchema = z.object({
type: z.literal("hub.management.daemon.disconnect.response"),
payload: z.object({
requestId: z.string(),
status: HubRelationshipStatusSchema,
warning: z.string().optional(),
}),
});
export const HubManagementDaemonPermissionsUpdateResponseSchema = z.object({
type: z.literal("hub.management.daemon.permissions.update.response"),
payload: z.object({ requestId: z.string(), status: HubRelationshipStatusSchema }),
});
export const DaemonGetPairingOfferResponseSchema = z.object({
type: z.literal("daemon.get_pairing_offer.response"),
payload: z
.object({
requestId: z.string(),
url: z.string(),
qr: z.string().nullable().optional(),
relayEnabled: z.boolean(),
})
.passthrough(),
});
export const DaemonConfigReloadResponseSchema = z.object({
type: z.literal("daemon.config.reload.response"),
payload: z
.object({
requestId: z.string(),
appliedPaths: z.array(z.string()),
restartRequiredPaths: z.array(z.string()),
overrideControlledPaths: z.array(z.string()),
})
.passthrough(),
});
export const DiagnosticsResponseSchema = z.object({
type: z.literal("diagnostics.response"),
payload: z
.object({
requestId: z.string(),
diagnostic: z.string(),
})
.passthrough(),
});
export const SetDaemonConfigResponseMessageSchema = z.object({
type: z.literal("set_daemon_config_response"),
payload: z
.object({
requestId: z.string(),
config: MutableDaemonConfigSchema,
})
.passthrough(),
});
export const ReadProjectConfigResponseMessageSchema = z.object({
type: z.literal("read_project_config_response"),
// zod-aot 0.2.0 miscompiles boolean discriminators as string options
// (`"true"`/`"false"`), so keep this sequential until upstream fixes it.
payload: z.union([
z.object({
requestId: z.string(),
repoRoot: z.string(),
ok: z.literal(true),
config: PaseoConfigRawSchema.nullable(),
revision: PaseoConfigRevisionSchema.nullable(),
hasUncommittedWorktreeSetupChanges: z.boolean().optional(),
}),
z.object({
requestId: z.string(),
repoRoot: z.string(),
ok: z.literal(false),
error: ProjectConfigRpcErrorSchema,
}),
]),
});
export const WriteProjectConfigResponseMessageSchema = z.object({
type: z.literal("write_project_config_response"),
// zod-aot 0.2.0 miscompiles boolean discriminators as string options
// (`"true"`/`"false"`), so keep this sequential until upstream fixes it.
payload: z.union([
z.object({
requestId: z.string(),
repoRoot: z.string(),
ok: z.literal(true),
config: PaseoConfigRawSchema,
revision: PaseoConfigRevisionSchema,
hasUncommittedWorktreeSetupChanges: z.boolean().optional(),
}),
z.object({
requestId: z.string(),
repoRoot: z.string(),
ok: z.literal(false),
error: ProjectConfigRpcErrorSchema,
}),
]),
});
export const AgentPermissionRequestMessageSchema = z.object({
type: z.literal("agent_permission_request"),
payload: z.object({
subscriptionId: z.string().optional(),
agentId: z.string(),
request: AgentPermissionRequestPayloadSchema,
}),
});
export const AgentPermissionResolvedMessageSchema = z.object({
type: z.literal("agent_permission_resolved"),
payload: z.object({
subscriptionId: z.string().optional(),
agentId: z.string(),
requestId: z.string(),
resolution: AgentPermissionResponseSchema,
}),
});
export const AgentDeletedMessageSchema = z.object({
type: z.literal("agent_deleted"),
payload: z.object({
agentId: z.string(),
requestId: z.string(),
}),
});
export const AgentArchivedMessageSchema = z.object({
type: z.literal("agent_archived"),
payload: z.object({
agentId: z.string(),
archivedAt: z.string(),
requestId: z.string(),
}),
});
const CloseItemsAgentResultSchema = z.object({
agentId: z.string(),
archivedAt: z.string(),
});
const CloseItemsTerminalResultSchema = z.object({
terminalId: z.string(),
success: z.boolean(),
});
export const CloseItemsResponseSchema = z.object({
type: z.literal("close_items_response"),
payload: z.object({
agents: z.array(CloseItemsAgentResultSchema),
terminals: z.array(CloseItemsTerminalResultSchema),
requestId: z.string(),
}),
});
const AheadBehindSchema = z.object({
ahead: z.number(),
behind: z.number(),
});
const CheckoutStatusCommonSchema = z.object({
cwd: z.string(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
// The full ref currentBranch tracks, as git resolves `<branch>@{upstream}`:
// "refs/remotes/origin/main", "refs/remotes/upstream/main" on a fork, or a
// "refs/heads/..." ref for a branch tracking a local branch. Null when there is no
// upstream. Clients use it verbatim — the remote is not necessarily origin and the
// upstream branch name is not necessarily currentBranch, so composing one is wrong.
// aheadOfOrigin/behindOfOrigin are measured against exactly this ref.
upstreamRef: z.string().nullable().optional(),
});
const CheckoutStatusNotGitSchema = CheckoutStatusCommonSchema.extend({
isGit: z.literal(false),
isPaseoOwnedWorktree: z.literal(false),
repoRoot: z.null(),
currentBranch: z.null(),
isDirty: z.null(),
baseRef: z.null(),
aheadBehind: z.null(),
aheadOfOrigin: z.null(),
behindOfOrigin: z.null(),
hasRemote: z.boolean(),
remoteUrl: z.null(),
});
const CheckoutStatusGitNonPaseoSchema = CheckoutStatusCommonSchema.extend({
isGit: z.literal(true),
isPaseoOwnedWorktree: z.literal(false),
repoRoot: z.string(),
mainRepoRoot: z.string().nullable().optional().default(null),
currentBranch: z.string().nullable(),
isDirty: z.boolean(),
baseRef: z.string().nullable(),
aheadBehind: AheadBehindSchema.nullable(),
aheadOfOrigin: z.number().nullable(),
behindOfOrigin: z.number().nullable(),
hasRemote: z.boolean(),
remoteUrl: z.string().nullable(),
});
const CheckoutStatusGitPaseoSchema = CheckoutStatusCommonSchema.extend({
isGit: z.literal(true),
isPaseoOwnedWorktree: z.literal(true),
repoRoot: z.string(),
mainRepoRoot: z.string(),
currentBranch: z.string().nullable(),
isDirty: z.boolean(),
baseRef: z.string(),
aheadBehind: AheadBehindSchema.nullable(),
aheadOfOrigin: z.number().nullable(),
behindOfOrigin: z.number().nullable(),
hasRemote: z.boolean(),
remoteUrl: z.string().nullable(),
});
export const CheckoutStatusResponseSchema = z.object({
type: z.literal("checkout_status_response"),
payload: z.union([
CheckoutStatusNotGitSchema,
CheckoutStatusGitNonPaseoSchema,
CheckoutStatusGitPaseoSchema,
]),
});
const CheckoutPrGithubAutoMergeRequestSchema = z
.object({
enabledAt: z.string().nullable().optional().default(null),
mergeMethod: z.string().nullable().optional().default(null),
enabledBy: z.string().nullable().optional().default(null),
})
.nullable()
.optional()
.default(null);
const CheckoutPrGithubRepositoryPolicySchema = z
.object({
autoMergeAllowed: z.boolean().optional().default(false),
mergeCommitAllowed: z.boolean().optional().default(false),
squashMergeAllowed: z.boolean().optional().default(false),
rebaseMergeAllowed: z.boolean().optional().default(false),
viewerDefaultMergeMethod: z.string().nullable().optional().default(null),
})
.optional()
.default({
autoMergeAllowed: false,
mergeCommitAllowed: false,
squashMergeAllowed: false,
rebaseMergeAllowed: false,
viewerDefaultMergeMethod: null,
});
const CheckoutPrGithubStatusObjectSchema = z.object({
mergeStateStatus: z.string().nullable().optional().default(null),
autoMergeRequest: CheckoutPrGithubAutoMergeRequestSchema,
viewerCanEnableAutoMerge: z.boolean().optional().default(false),
viewerCanDisableAutoMerge: z.boolean().optional().default(false),
viewerCanMergeAsAdmin: z.boolean().optional().default(false),
viewerCanUpdateBranch: z.boolean().optional().default(false),
repository: CheckoutPrGithubRepositoryPolicySchema,
isMergeQueueEnabled: z.boolean().optional().default(false),
isInMergeQueue: z.boolean().optional().default(false),
});
const CheckoutPrGithubStatusSchema = CheckoutPrGithubStatusObjectSchema.optional();
// The open facts envelope for forge-specific PR facts. Permanent — non-GitHub
// forges deliver their native facts through it. The transitional piece is the
// `github` mirror above, which stays populated for clients predating this
// envelope; see COMPAT(forgeSpecific) in status-projection.ts for the shim.
//
// NOTE: `forgeSpecific.forge` is a FACTS-FAMILY tag, not the workspace brand id.
// The whole Gitea family (gitea, forgejo, codeberg) emits `forge: "gitea"` here
// because they share one facts shape, while the top-level `forge` above carries
// the specific brand. Validation of family-specific payloads happens at runtime
// in the consumer that knows that forge family.
const CheckoutPrForgeSpecificSchema = z.unknown().optional();
export const CheckoutPrStatusSchema = z.object({
// COMPAT(forge): added in v0.2.0-beta.1. Remove the GitHub default after
// 2027-01-17 once the supported daemon floor is >= v0.2.0.
forge: z.string().optional().default("github"),
projectPath: z.string().optional(),
number: z.number().optional(),
url: z.string(),
title: z.string(),
state: z.string(),
baseRefName: z.string(),
headRefName: z.string(),
isMerged: z.boolean(),
isDraft: z.boolean().optional().default(false),
mergeable: z
.enum(["MERGEABLE", "CONFLICTING", "UNKNOWN"])
.catch("UNKNOWN")
.optional()
.default("UNKNOWN"),
checks: z
.array(
z.object({
name: z.string(),
status: z.string(),
url: z.string().nullable(),
workflow: z.string().optional(),
/**
* Formatted by the forge adapter: how long a finished check took, or how long a
* running one has been going. Raw timestamps stay off the wire.
*/
duration: z.string().optional(),
checkRunId: z.number().optional(),
workflowRunId: z.number().optional(),
// Open so future forge-neutral refinements remain parse-compatible.
traits: z.array(z.string()).optional(),
}),
)
.optional()
.default([]),
checksStatus: z.string().optional(),
reviewDecision: z.string().nullable().optional(),
repoOwner: z.string().optional(),
repoName: z.string().optional(),
github: CheckoutPrGithubStatusSchema,
forgeSpecific: CheckoutPrForgeSpecificSchema,
});
// Why a forge's PR/MR features are (un)available, so the client can offer the
// precise next step instead of a generic dead-end. Kept open on the wire so
// feature consumers can ignore values introduced by newer daemons.
export type ForgeAuthState =
| "authenticated"
| "unauthenticated"
| "cli_missing"
| "no_remote"
| "error";
export const ForgeAuthStateSchema = z.unknown().optional();
const CheckoutPrStatusPayloadSchema = z.object({
cwd: z.string(),
status: CheckoutPrStatusSchema.nullable(),
githubFeaturesEnabled: z.boolean(),
// COMPAT(forgeAuthState): added in v0.2.0-beta.1. Remove the legacy
// githubFeaturesEnabled normalization after 2027-01-17 once the supported
// daemon floor is >= v0.2.0.
authState: ForgeAuthStateSchema,
// COMPAT(forge): added in v0.2.0-beta.1. Remove the GitHub default after
// 2027-01-17 once the supported daemon floor is >= v0.2.0.
forge: z.string().optional().default("github"),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
});
const CheckoutStatusUpdateMetadataSchema = z.object({
subscriptionId: z.string().optional(),
prStatus: CheckoutPrStatusPayloadSchema.optional(),
});
export const CheckoutStatusUpdateSchema = z.object({
type: z.literal("checkout_status_update"),
payload: z
.union([
CheckoutStatusNotGitSchema,
CheckoutStatusGitNonPaseoSchema,
CheckoutStatusGitPaseoSchema,
])
.and(CheckoutStatusUpdateMetadataSchema),
});
const CheckoutDiffSubscriptionPayloadSchema = z.object({
subscriptionId: z.string(),
cwd: z.string(),
files: z.array(ParsedDiffFileSchema),
error: CheckoutErrorSchema.nullable(),
// COMPAT(diffTooLarge): added in v0.2.4, keep optional until the daemon floor is v0.2.4.
diffTooLarge: z.boolean().optional(),
});
export const SubscribeCheckoutDiffResponseSchema = z.object({
type: z.literal("subscribe_checkout_diff_response"),
payload: CheckoutDiffSubscriptionPayloadSchema.extend({
requestId: z.string(),
}),
});
export const CheckoutDiffGetResponseSchema = z.object({
type: z.literal("checkout.diff.get.response"),
payload: CheckoutDiffSubscriptionPayloadSchema.omit({ subscriptionId: true }).extend({
requestId: z.string(),
}),
});
export const CheckoutDiffUpdateSchema = z.object({
type: z.literal("checkout_diff_update"),
payload: CheckoutDiffSubscriptionPayloadSchema,
});
export const CheckoutCommitResponseSchema = z.object({
type: z.literal("checkout_commit_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutMergeResponseSchema = z.object({
type: z.literal("checkout_merge_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutMergeFromBaseResponseSchema = z.object({
type: z.literal("checkout_merge_from_base_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutPullResponseSchema = z.object({
type: z.literal("checkout_pull_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutPushResponseSchema = z.object({
type: z.literal("checkout_push_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutRefreshResponseSchema = z.object({
type: z.literal("checkout.refresh.response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutPrCreateResponseSchema = z.object({
type: z.literal("checkout_pr_create_response"),
payload: z.object({
cwd: z.string(),
url: z.string().nullable(),
number: z.number().nullable(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutPrMergeResponseSchema = z.object({
type: z.literal("checkout_pr_merge_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutForgeSetAutoMergeResponseSchema = z.object({
type: z.literal("checkout.forge.set_auto_merge.response"),
payload: z.object({
cwd: z.string(),
enabled: z.boolean(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
// COMPAT(githubAutoMergeRpc): legacy RPC retained when
// checkout.forge.set_auto_merge.* shipped in v0.2.0-beta.1. Stop serving and
// consuming it after 2027-01-17 once client and daemon floors are >= v0.2.0.
export const CheckoutGithubSetAutoMergeResponseSchema = z.object({
type: z.literal("checkout.github.set_auto_merge.response"),
payload: z.object({
cwd: z.string(),
enabled: z.boolean(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutDiscardChangesResponseSchema = z.object({
type: z.literal("checkout.discard_changes.response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutCommitsListResponseSchema = z.object({
type: z.literal("checkout.commits.list.response"),
payload: z.object({
cwd: z.string(),
baseRef: z.string().nullable(),
commits: z.array(CheckoutCommitSchema),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutCommitFileDiffResponseSchema = z.object({
type: z.literal("checkout.commits.file_diff.response"),
payload: z.object({
cwd: z.string(),
sha: z.string(),
path: z.string(),
// null when the file is absent from the commit or carries no textual diff
// (e.g. binary-only changes).
file: ParsedDiffFileSchema.nullable(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
const CheckoutGithubCheckAnnotationSchema = z.object({
path: z.string().optional(),
startLine: z.number().optional(),
endLine: z.number().optional(),
annotationLevel: z.string().optional(),
message: z.string().optional(),
title: z.string().optional(),
rawDetails: z.string().optional(),
});
const CheckoutGithubCheckJobSchema = z.object({
jobId: z.number(),
name: z.string(),
status: z.string().nullable().optional(),
conclusion: z.string().nullable().optional(),
url: z.string().nullable().optional(),
logTail: z.string().optional(),
logTruncated: z.boolean().optional(),
});
// Statuses stay open strings so future forge values cannot break parsing.
const CheckoutPipelineJobSchema = z.object({
id: z.number(),
name: z.string(),
stage: z.string(),
status: z.string(),
// COMPAT(pipelineRawStatus): no client reads this, but peers <= v0.2.0-rc.1
// validate it as required, so daemons must keep emitting it. Optional since
// this schema so future daemons may omit it; delete the field and its
// emission after 2027-01-17 once the supported client floor is >= v0.2.0.
rawStatus: z.string().optional(),
url: z.string().nullable().optional().default(null),
allowFailure: z.boolean().optional().default(false),
durationSeconds: z.number().nullable().optional().default(null),
});
const CheckoutPipelineStageSchema = z.object({
name: z.string(),
status: z.string(),
jobs: z.array(CheckoutPipelineJobSchema).optional().default([]),
});
const CheckoutPipelineSchema = z.object({
id: z.number(),
status: z.string(),
// COMPAT(pipelineRawStatus): see CheckoutPipelineJobSchema.rawStatus.
rawStatus: z.string().optional(),
url: z.string().nullable().optional().default(null),
ref: z.string().nullable().optional().default(null),
sha: z.string().nullable().optional().default(null),
stages: z.array(CheckoutPipelineStageSchema).optional().default([]),
});
export const CheckoutGithubCheckDetailsSchema = z.object({
checkRunId: z.number(),
workflowRunId: z.number().nullable().optional(),
name: z.string(),
status: z.string().nullable().optional(),
conclusion: z.string().nullable().optional(),
url: z.string().nullable().optional(),
detailsUrl: z.string().nullable().optional(),
output: z
.object({
title: z.string().nullable().optional(),
summary: z.string().nullable().optional(),
text: z.string().nullable().optional(),
})
.nullable()
.optional(),
annotations: z.array(CheckoutGithubCheckAnnotationSchema).optional().default([]),
failedJobs: z.array(CheckoutGithubCheckJobSchema).optional().default([]),
truncated: z.boolean().optional().default(false),
// No default: server CheckDetails keeps this optional and GitHub leaves it absent.
pipeline: CheckoutPipelineSchema.nullable().optional(),
});
export const CheckoutCheckDetailsSchema = CheckoutGithubCheckDetailsSchema;
export const CheckoutForgeGetCheckDetailsResponseSchema = z.object({
type: z.literal("checkout.forge.get_check_details.response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
details: CheckoutCheckDetailsSchema.nullable().optional().default(null),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
// COMPAT(githubCheckDetailsRpc): legacy RPC retained when
// checkout.forge.get_check_details.* shipped in v0.2.0-beta.1. Stop serving
// and consuming it after 2027-01-17 once client and daemon floors are >= v0.2.0.
export const CheckoutGithubGetCheckDetailsResponseSchema = z.object({
type: z.literal("checkout.github.get_check_details.response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
details: CheckoutCheckDetailsSchema.nullable().optional().default(null),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutPrStatusResponseSchema = z.object({
type: z.literal("checkout_pr_status_response"),
payload: CheckoutPrStatusPayloadSchema,
});
const PullRequestTimelineKnownErrorSchema = z.discriminatedUnion("kind", [
z.object({
kind: z.literal("not_found"),
message: z.string().optional().default(""),
}),
z.object({
kind: z.literal("forbidden"),
message: z.string().optional().default(""),
}),
z.object({
kind: z.literal("unknown"),
message: z.string().optional().default(""),
}),
]);
const PullRequestTimelineErrorSchema = z.preprocess((value) => {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return { kind: "unknown", message: "" };
}
const error = value as Record<string, unknown>;
if (error.kind === "not_found" || error.kind === "forbidden" || error.kind === "unknown") {
return error;
}
return { ...error, kind: "unknown" };
}, PullRequestTimelineKnownErrorSchema);
const PullRequestTimelineReviewItemSchema = z.object({
id: z.string().optional().default(""),
kind: z.literal("review"),
author: z.string().optional().default("unknown"),
authorUrl: z.string().nullable().optional(),
avatarUrl: z.string().nullable().optional(),
body: z.string().optional().default(""),
createdAt: z.number().optional().default(0),
url: z.string().optional().default(""),
reviewState: z
.enum(["approved", "changes_requested", "commented"])
.optional()
.default("commented"),
});
const PullRequestTimelineCommentItemSchema = z.object({
id: z.string().optional().default(""),
kind: z.literal("comment"),
author: z.string().optional().default("unknown"),
authorUrl: z.string().nullable().optional(),
avatarUrl: z.string().nullable().optional(),
body: z.string().optional().default(""),
createdAt: z.number().optional().default(0),
url: z.string().optional().default(""),
// GitHub review id this inline comment belongs to; lets clients nest review
// threads under their parent review. Absent on issue comments and on
// timelines from daemons that predate the field.
reviewId: z.string().optional(),
// Forge-neutral discussion/thread id this comment belongs to, independent of a
// file position. GitLab maps its discussion id here so general (non-file)
// reply chains group into one thread; file-position threads also carry it.
// Absent on standalone comments and on timelines from daemons that predate it.
threadId: z.string().optional(),
// Forge-neutral resolution state for a thread that has no file position, e.g. a
// GitLab general (non-file) discussion that is resolvable. File-position threads
// carry their resolution under `location.isResolved` instead. Absent on ordinary
// comments, on forges that expose no thread resolution, and on older timelines.
threadIsResolved: z.boolean().optional(),
location: z
.object({
path: z.string(),
line: z.number().optional(),
startLine: z.number().optional(),
threadId: z.string().optional(),
isResolved: z.boolean().optional(),
isOutdated: z.boolean().optional(),
})
.optional(),
});
export const PullRequestTimelineItemSchema = z.preprocess(
(value) => {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return value;
}
const item = value as Record<string, unknown>;
if (item.kind === "review" || item.kind === "comment") {
return item;
}
return { ...item, kind: "comment" };
},
z.discriminatedUnion("kind", [
PullRequestTimelineReviewItemSchema,
PullRequestTimelineCommentItemSchema,
]),
);
export const PullRequestTimelineResponseSchema = z.object({
type: z.literal("pull_request_timeline_response"),
payload: z
.object({
cwd: z.string().optional().default(""),
prNumber: z.number().nullable().optional().default(null),
items: z.array(PullRequestTimelineItemSchema).optional().default([]),
truncated: z.boolean().optional().default(false),
error: PullRequestTimelineErrorSchema.nullable().optional().default(null),
requestId: z.string().optional().default(""),
githubFeaturesEnabled: z.boolean().optional().default(true),
// COMPAT(forgeAuthState): added in v0.2.0-beta.1. Remove the legacy
// githubFeaturesEnabled normalization after 2027-01-17 once the supported
// daemon floor is >= v0.2.0.
authState: ForgeAuthStateSchema,
})
.optional()
.prefault({}),
});
export const CheckoutSwitchBranchResponseSchema = z.object({
type: z.literal("checkout_switch_branch_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
branch: z.string(),
source: z.enum(["local", "remote"]).optional(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CheckoutRenameBranchResponseSchema = z.object({
type: z.literal("checkout.rename_branch.response"),
payload: z.object({
requestId: z.string(),
success: z.boolean(),
cwd: z.string(),
currentBranch: z.string().nullable(),
error: CheckoutErrorSchema.nullable(),
}),
});
const StashEntrySchema = z.object({
index: z.number().int().min(0),
message: z.string(),
branch: z.string().nullable(),
isPaseo: z.boolean(),
});
export const StashSaveResponseSchema = z.object({
type: z.literal("stash_save_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const StashPopResponseSchema = z.object({
type: z.literal("stash_pop_response"),
payload: z.object({
cwd: z.string(),
success: z.boolean(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const StashListResponseSchema = z.object({
type: z.literal("stash_list_response"),
payload: z.object({
cwd: z.string(),
entries: z.array(StashEntrySchema),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const ValidateBranchResponseSchema = z.object({
type: z.literal("validate_branch_response"),
payload: z.object({
exists: z.boolean(),
resolvedRef: z.string().nullable(),
isRemote: z.boolean(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const BranchSuggestionsResponseSchema = z.object({
type: z.literal("branch_suggestions_response"),
payload: z.object({
branches: z.array(z.string()),
branchDetails: z
.array(
z.object({
name: z.string(),
committerDate: z.number(),
hasLocal: z.boolean().optional(),
hasRemote: z.boolean().optional(),
localAhead: z.number().int().nonnegative().optional(),
localBehind: z.number().int().nonnegative().optional(),
}),
)
.optional(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
const ForgeSearchResponsePayloadSchema = z.object({
items: z.array(z.unknown()),
authState: z.unknown().optional(),
error: z.string().nullable(),
requestId: z.string(),
});
const GitHubSearchResponsePayloadSchema = z.object({
items: z.array(z.unknown()),
featuresEnabled: z.boolean().optional(),
authState: z.unknown().optional(),
githubFeaturesEnabled: z.boolean().optional(),
error: z.string().nullable(),
requestId: z.string(),
});
export const ForgeSearchResponseSchema = z.object({
type: z.literal("forge.search.response"),
payload: ForgeSearchResponsePayloadSchema,
});
// COMPAT(githubSearchRpc): legacy RPC retained when forge.search.* shipped in
// v0.2.0-beta.1. Stop serving and consuming it after 2027-01-17 once client
// and daemon floors are >= v0.2.0.
export const GitHubSearchResponseSchema = z.object({
type: z.literal("github_search_response"),
payload: GitHubSearchResponsePayloadSchema,
});
export const DirectorySuggestionsResponseSchema = z.object({
type: z.literal("directory_suggestions_response"),
payload: z.object({
directories: z.array(z.string()),
entries: z
.array(
z.object({
path: z.string(),
kind: z.enum(["file", "directory"]),
}),
)
.optional()
.default([]),
error: z.string().nullable(),
requestId: z.string(),
}),
});
const PaseoWorktreeSchema = z.object({
worktreePath: z.string(),
createdAt: z.string(),
branchName: z.string().nullable().optional(),
head: z.string().nullable().optional(),
});
export const PaseoWorktreeListResponseSchema = z.object({
type: z.literal("paseo_worktree_list_response"),
payload: z.object({
worktrees: z.array(PaseoWorktreeSchema),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const PaseoWorktreeArchiveResponseSchema = z.object({
type: z.literal("paseo_worktree_archive_response"),
payload: z.object({
success: z.boolean(),
removedAgents: z.array(z.string()).optional(),
error: CheckoutErrorSchema.nullable(),
requestId: z.string(),
}),
});
export const CreatePaseoWorktreeResponseSchema = z.object({
type: z.literal("create_paseo_worktree_response"),
payload: z.object({
workspace: WorkspaceDescriptorPayloadSchema.nullable(),
error: z.string().nullable(),
errorCode: z.string().optional(),
setupTerminalId: z.string().nullable(),
setupSkippedReason: z.string().optional(),
requestId: z.string(),
}),
});
export const FileExplorerResponseSchema = z.object({
type: z.literal("file_explorer_response"),
payload: z.object({
cwd: z.string(),
path: z.string(),
mode: z.enum(["list", "file"]),
directory: FileExplorerDirectorySchema.nullable(),
file: FileExplorerFileSchema.nullable(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const FileSubscribeResponseSchema = z.object({
type: z.literal("fs.file.subscribe.response"),
payload: z.object({
subscriptionId: z.string(),
initial: FileVersionSchema,
requestId: z.string(),
}),
});
export const FileUnsubscribeResponseSchema = z.object({
type: z.literal("fs.file.unsubscribe.response"),
payload: z.object({
subscriptionId: z.string(),
requestId: z.string(),
}),
});
export const FileWriteResultSchema = z.discriminatedUnion("status", [
z.object({
status: z.literal("written"),
modifiedAt: z.string(),
size: z.number(),
revision: z.string().optional(),
}),
z.object({ status: z.literal("conflict"), version: FileVersionSchema }),
z.object({ status: z.literal("error"), error: z.string() }),
]);
export const FileWriteResponseSchema = z.object({
type: z.literal("fs.file.write.response"),
payload: z.object({
result: FileWriteResultSchema,
requestId: z.string(),
}),
});
export const FileEntryCreateResponseSchema = z.object({
type: z.literal("fs.entry.create.response"),
payload: z.object({
cwd: z.string(),
parentPath: z.string(),
path: z.string().nullable(),
success: z.boolean(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const FileEntryRenameResponseSchema = z.object({
type: z.literal("fs.entry.rename.response"),
payload: z.object({
cwd: z.string(),
path: z.string(),
renamedPath: z.string().nullable(),
success: z.boolean(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const FileEntryDuplicateResponseSchema = z.object({
type: z.literal("fs.entry.duplicate.response"),
payload: z.object({
cwd: z.string(),
path: z.string(),
duplicatedPath: z.string().nullable(),
success: z.boolean(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const FileEntryDeleteResponseSchema = z.object({
type: z.literal("fs.entry.delete.response"),
payload: z.object({
cwd: z.string(),
path: z.string(),
success: z.boolean(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const FileUpdateSchema = z.object({
type: z.literal("fs.file.update"),
payload: z.object({
subscriptionId: z.string(),
version: FileVersionSchema,
}),
});
const ProjectIconSchema = z.object({
data: z.string(),
mimeType: z.string(),
});
export const ProjectIconResponseSchema = z.object({
type: z.literal("project_icon_response"),
payload: z.object({
cwd: z.string(),
icon: ProjectIconSchema.nullable(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const ProjectIconGetResponseSchema = z.object({
type: z.literal("project.icon.get.response"),
payload: z.object({
projectId: z.string(),
icon: ProjectIconSchema.nullable(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const FileDownloadTokenResponseSchema = z.object({
type: z.literal("file_download_token_response"),
payload: z.object({
cwd: z.string(),
path: z.string(),
token: z.string().nullable(),
fileName: z.string().nullable(),
mimeType: z.string().nullable(),
size: z.number().nullable(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const FileUploadResponseSchema = z.object({
type: z.literal("file.upload.response"),
payload: z.object({
requestId: z.string(),
file: UploadedFileAttachmentSchema.nullable(),
error: z.string().nullable(),
}),
});
export const ListProviderModelsResponseMessageSchema = z.object({
type: z.literal("list_provider_models_response"),
payload: z.object({
provider: AgentProviderSchema,
models: z.array(AgentModelDefinitionSchema).optional(),
error: z.string().nullable().optional(),
fetchedAt: z.string(),
requestId: z.string(),
}),
});
export const ListProviderModesResponseMessageSchema = z.object({
type: z.literal("list_provider_modes_response"),
payload: z.object({
provider: AgentProviderSchema,
modes: z.array(AgentModeSchema).optional(),
error: z.string().nullable().optional(),
fetchedAt: z.string(),
requestId: z.string(),
}),
});
export const ListProviderFeaturesResponseMessageSchema = z.object({
type: z.literal("list_provider_features_response"),
payload: z.object({
provider: AgentProviderSchema,
features: z.array(AgentFeatureSchema).optional(),
error: z.string().nullable().optional(),
fetchedAt: z.string(),
requestId: z.string(),
}),
});
const ProviderAvailabilitySchema = z.object({
provider: AgentProviderSchema,
available: z.boolean(),
error: z.string().nullable().optional(),
});
export const ListAvailableProvidersResponseSchema = z.object({
type: z.literal("list_available_providers_response"),
payload: z.object({
providers: z.array(ProviderAvailabilitySchema),
error: z.string().nullable().optional(),
fetchedAt: z.string(),
requestId: z.string(),
}),
});
// COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
export const GetProvidersSnapshotResponseMessageSchema = z.object({
type: z.literal("get_providers_snapshot_response"),
payload: z.object({
cwd: z.string().optional(),
entries: z.array(ProviderSnapshotEntrySchema),
compactSnapshot: CompactProviderSnapshotSchema.optional(),
snapshotHash: z.string().optional(),
fetchedAt: z.record(z.string(), z.string()).optional(),
notModified: z.boolean().optional(),
generatedAt: z.string(),
requestId: z.string(),
}),
});
// COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
export const ProvidersSnapshotUpdateMessageSchema = z.object({
type: z.literal("providers_snapshot_update"),
payload: z.object({
subscriptionId: z.string().optional(),
cwd: z.string().optional(),
entries: z.array(ProviderSnapshotEntrySchema),
compactSnapshot: CompactProviderSnapshotSchema.optional(),
snapshotHash: z.string().optional(),
fetchedAt: z.record(z.string(), z.string()).optional(),
generatedAt: z.string(),
}),
});
// COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
export const RefreshProvidersSnapshotResponseMessageSchema = z.object({
type: z.literal("refresh_providers_snapshot_response"),
payload: z.object({
requestId: z.string(),
acknowledged: z.boolean(),
}),
});
// COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
export const ProviderDiagnosticResponseMessageSchema = z.object({
type: z.literal("provider_diagnostic_response"),
payload: z.object({
provider: AgentProviderSchema,
diagnostic: z.string(),
requestId: z.string(),
}),
});
export const ProviderUsageToneSchema = z.enum(["default", "ok", "warning", "danger"]);
export const ProviderUsageStatusSchema = z.enum(["available", "unavailable", "error"]);
export const ProviderUsageWindowSchema = z.object({
id: z.string(),
label: z.string(),
/**
* A few characters naming the window where space is tight, e.g. "5h" or "wk". An empty string
* shows the percent alone; leaving it out shows `label`.
*/
shortLabel: z.string().optional(),
/** Shown in the usage summary until the user pins windows of their own. */
summary: z.boolean().optional(),
usedPct: z.number().nullable().optional(),
remainingPct: z.number().nullable().optional(),
resetsAt: z.string().nullable().optional(),
runsOutAt: z.string().nullable().optional(),
shortfallPct: z.number().nullable().optional(),
tone: ProviderUsageToneSchema.optional(),
});
export const ProviderUsageBalanceSchema = z.object({
id: z.string(),
label: z.string(),
used: z.number().nullable().optional(),
remaining: z.number().nullable().optional(),
limit: z.number().nullable().optional(),
unit: z.enum(["usd", "credits", "requests", "tokens"]),
resetsAt: z.string().nullable().optional(),
tone: ProviderUsageToneSchema.optional(),
});
export const ProviderUsageDetailSchema = z.object({
id: z.string(),
label: z.string(),
value: z.string(),
tone: ProviderUsageToneSchema.optional(),
});
export const ProviderUsageSchema = z.object({
providerId: z.string(),
displayName: z.string(),
status: ProviderUsageStatusSchema,
planLabel: z.string().nullable(),
sourceLabel: z.string().nullable().optional(),
fetchedAt: z.string().nullable().optional(),
nextRefreshAt: z.string().nullable().optional(),
windows: z.array(ProviderUsageWindowSchema),
balances: z.array(ProviderUsageBalanceSchema).optional(),
details: z.array(ProviderUsageDetailSchema).optional(),
error: z.string().nullable().optional(),
});
export const ProviderUsageListResponseMessageSchema = z.object({
type: z.literal("provider.usage.list.response"),
payload: z.object({
requestId: z.string(),
fetchedAt: z.string(),
providers: z.array(ProviderUsageSchema),
}),
});
export const UsageProblemSchema = z.discriminatedUnion("kind", [
z.object({
kind: z.literal("expired"),
expiresAt: z.iso.datetime(),
refreshedBy: z.string().optional(),
}),
z.object({
kind: z.literal("rejected"),
status: z.number().int(),
refreshedBy: z.string().optional(),
}),
z.object({ kind: z.literal("no_quota"), detail: z.string() }),
]);
export const UsageReportSchema = z.discriminatedUnion("status", [
z.object({
status: z.literal("available"),
planLabel: z.string().optional(),
windows: z.array(ProviderUsageWindowSchema),
balances: z.array(ProviderUsageBalanceSchema).optional(),
details: z.array(ProviderUsageDetailSchema).optional(),
}),
z.object({ status: z.literal("unavailable"), problem: UsageProblemSchema }),
z.object({ status: z.literal("error"), error: z.string() }),
]);
export const UsageReportEntrySchema = z.object({
id: z.string(),
account: z.object({ label: z.string().optional() }),
fetchedAt: z.string(),
sourceId: z.string(),
sourceLabel: z.string(),
icon: z.string().optional(),
report: UsageReportSchema,
loginErrors: z
.array(
z.object({
harness: z.string(),
report: z.discriminatedUnion("status", [
z.object({ status: z.literal("unavailable"), problem: UsageProblemSchema }),
z.object({ status: z.literal("error"), error: z.string() }),
]),
}),
)
.optional(),
});
export const UsageListReportsUpdateMessageSchema = z.object({
type: z.literal("usage.list_reports.update"),
payload: z.object({ requestId: z.string(), report: UsageReportEntrySchema }),
});
export const UsageListReportsResponseMessageSchema = z.object({
type: z.literal("usage.list_reports.response"),
payload: z.object({ requestId: z.string(), error: z.string().nullable() }),
});
const AgentSlashCommandSchema = z.object({
name: z.string(),
description: z.string(),
argumentHint: z.string(),
kind: z.enum(["command", "skill"]).optional().catch("command"),
});
export const ListCommandsResponseSchema = z.object({
type: z.literal("list_commands_response"),
payload: z.object({
agentId: z.string(),
commands: z.array(AgentSlashCommandSchema),
error: z.string().nullable(),
requestId: z.string(),
}),
});
// ============================================================================
// Terminal Outbound Messages
// ============================================================================
const TerminalInfoSchema = z.object({
id: z.string(),
name: z.string(),
cwd: z.string(),
workspaceId: z.string().optional(),
title: z.string().optional(),
activity: TerminalActivitySchema.nullable().optional(),
});
export const TerminalCellSchema = z.object({
char: z.string(),
fg: z.number().optional(),
bg: z.number().optional(),
fgMode: z.number().optional(),
bgMode: z.number().optional(),
bold: z.boolean().optional(),
italic: z.boolean().optional(),
underline: z.boolean().optional(),
dim: z.boolean().optional(),
inverse: z.boolean().optional(),
strikethrough: z.boolean().optional(),
});
export const TerminalCursorStyleSchema = z.enum(["block", "underline", "bar"]);
export const TerminalCursorSchema = z.object({
row: z.number(),
col: z.number(),
hidden: z.boolean().optional(),
style: TerminalCursorStyleSchema.optional(),
blink: z.boolean().optional(),
});
export const TerminalStateSchema = z.object({
rows: z.number(),
cols: z.number(),
grid: z.array(z.array(TerminalCellSchema)),
scrollback: z.array(z.array(TerminalCellSchema)),
cursor: TerminalCursorSchema,
title: z.string().optional(),
// Per-row soft-wrap flags aligned 1:1 with `grid` / `scrollback`. `true` means
// the row continued onto the next row (xterm's GRID_LINE_WRAPPED equivalent),
// so the client can re-wrap the logical line on resize instead of freezing it
// at the snapshot width. Optional: only sent to clients that advertise the
// `terminalReflowableSnapshot` capability, so old daemons/clients are unaffected.
gridWrapped: z.array(z.boolean()).optional(),
scrollbackWrapped: z.array(z.boolean()).optional(),
});
export const ListTerminalsResponseSchema = z.object({
type: z.literal("list_terminals_response"),
payload: z.object({
cwd: z.string().optional(),
terminals: z.array(TerminalInfoSchema.partial({ cwd: true })),
requestId: z.string(),
}),
});
export const TerminalsChangedSchema = z.object({
type: z.literal("terminals_changed"),
payload: z.object({
subscriptionId: z.string().optional(),
requestId: z.string().optional(),
workspaceId: z.string().optional(),
cwd: z.string(),
terminals: z.array(TerminalInfoSchema.omit({ cwd: true })),
}),
});
export const CreateTerminalResponseSchema = z.object({
type: z.literal("create_terminal_response"),
payload: z.object({
terminal: TerminalInfoSchema.nullable(),
error: z.string().nullable(),
requestId: z.string(),
}),
});
export const RenameTerminalResponseSchema = z.object({
type: z.literal("terminal.rename.response"),
payload: z.object({
requestId: z.string(),
success: z.boolean(),
error: z.string().nullable(),
}),
});
export const SubscribeTerminalResponseSchema = z.object({
type: z.literal("subscribe_terminal_response"),
payload: z.union([
z.object({
terminalId: z.string(),
slot: z.number().int().min(0).max(255),
subscriptionId: z.string().optional(),
error: z.null(),
requestId: z.string(),
}),
z.object({
terminalId: z.string(),
error: z.string(),
requestId: z.string(),
}),
]),
});
export const KillTerminalResponseSchema = z.object({
type: z.literal("kill_terminal_response"),
payload: z.object({
terminalId: z.string(),
success: z.boolean(),
requestId: z.string(),
}),
});
export const CaptureTerminalResponseSchema = z.object({
type: z.literal("capture_terminal_response"),
payload: z.object({
terminalId: z.string(),
lines: z.array(z.string()),
totalLines: z.number().int().nonnegative(),
requestId: z.string(),
}),
});
export const TerminalStreamExitSchema = z.object({
type: z.literal("terminal_stream_exit"),
payload: z.object({
subscriptionId: z.string().optional(),
terminalId: z.string(),
// Observation failure; the underlying terminal process may still be running.
error: z.string().optional(),
}),
});
export const TerminalAttentionRequiredSchema = z.object({
type: z.literal("terminal_attention_required"),
payload: z.object({
subscriptionId: z.string().optional(),
serverId: z.string().optional(),
terminalId: z.string(),
cwd: z.string(),
workspaceId: z.string().optional(),
reason: z.enum(["finished", "needs_input"]),
title: z.string(),
body: z.string(),
shouldNotify: z.boolean(),
}),
});
export const DaemonUpdateResponseSchema = z.object({
type: z.literal("daemon.update.response"),
payload: z.object({
requestId: z.string(),
success: z.boolean(),
error: z.string().nullable(),
previousVersion: z.string().nullable(),
newVersion: z.string().nullable(),
}),
});
export type DaemonUpdateResponse = z.infer<typeof DaemonUpdateResponseSchema>;
export const DaemonUpdateProgressMessageSchema = z.object({
type: z.literal("daemon.update.progress"),
payload: z.object({
requestId: z.string(),
phase: z.enum(["starting", "downloading", "installing", "complete"]),
}),
});
export const HubExecutionAgentCreateResponseSchema = z.object({
type: z.literal("hub.execution.agent.create.response"),
payload: z.object({
requestId: z.string(),
executionId: z.string(),
agentId: z.string().nullable(),
agent: AgentSnapshotPayloadSchema.nullable(),
success: z.boolean(),
toolPolicyApplied: z.literal(true).optional(),
error: HubExecutionAgentCreateErrorSchema.nullable(),
}),
});
export const HubExecutionAgentValidationIssueSchema = z.object({
path: z.array(z.union([z.string(), z.number()])),
message: z.string(),
});
export type HubExecutionAgentValidationIssue = z.infer<
typeof HubExecutionAgentValidationIssueSchema
>;
export const HubExecutionAgentValidateResponseSchema = z.object({
type: z.literal("hub.execution.agent.validate.response"),
payload: z.object({
requestId: z.string(),
valid: z.boolean(),
issues: z.array(HubExecutionAgentValidationIssueSchema),
error: z.string().nullable(),
}),
});
export const HubExecutionControlResponseSchema = z.object({
type: z.literal("hub.execution.control.response"),
payload: z.object({
requestId: z.string(),
executionId: z.string(),
action: HubExecutionControlActionSchema,
success: z.boolean(),
error: z.string().nullable(),
}),
});
export const HubExecutionAgentUpdateSchema = z.object({
type: z.literal("hub.execution.agent.update"),
payload: z.object({
subscriptionId: z.string().optional(),
executionId: z.string(),
agentId: z.string(),
agent: AgentSnapshotPayloadSchema,
}),
});
export const HubExecutionAgentStreamSchema = z.object({
type: z.literal("hub.execution.agent.stream"),
payload: z.object({
subscriptionId: z.string().optional(),
executionId: z.string(),
agentId: z.string(),
event: AgentStreamEventPayloadSchema,
}),
});
export type HubExecutionAgentCreateResponse = z.infer<typeof HubExecutionAgentCreateResponseSchema>;
export type HubExecutionAgentValidateResponse = z.infer<
typeof HubExecutionAgentValidateResponseSchema
>;
export type HubExecutionControlResponse = z.infer<typeof HubExecutionControlResponseSchema>;
export type HubExecutionAgentUpdate = z.infer<typeof HubExecutionAgentUpdateSchema>;
export type HubExecutionAgentStream = z.infer<typeof HubExecutionAgentStreamSchema>;
export const HubExecutionOutboundMessageSchema = z.discriminatedUnion("type", [
HubExecutionAgentCreateResponseSchema,
HubExecutionAgentValidateResponseSchema,
HubExecutionControlResponseSchema,
HubExecutionAgentUpdateSchema,
HubExecutionAgentStreamSchema,
]);
export type HubExecutionOutboundMessage = z.infer<typeof HubExecutionOutboundMessageSchema>;
export class HubMessageCorrelationError extends Error {
constructor(messageType: HubExecutionOutboundMessage["type"]) {
super(`Hub message ${messageType} has mismatched agent correlation`);
this.name = "HubMessageCorrelationError";
}
}
export function parseHubExecutionOutboundMessage(value: unknown): HubExecutionOutboundMessage {
const message = HubExecutionOutboundMessageSchema.parse(value);
const payload = message.payload;
if (
"agent" in payload &&
payload.agent !== null &&
"agentId" in payload &&
payload.agentId !== null &&
payload.agent.id !== payload.agentId
) {
throw new HubMessageCorrelationError(message.type);
}
return message;
}
export type DaemonUpdateProgressMessage = z.infer<typeof DaemonUpdateProgressMessageSchema>;
export const PluginCatalogGetResponseSchema = z.object({
type: z.literal("plugin.catalog.get.response"),
payload: z.object({
requestId: z.string(),
plugins: z.array(
z.object({
id: PluginIdSchema,
clientBundle: z.string(),
requirements: PluginRequirementsSchema.optional(),
}),
),
}),
});
export const PluginStatusSchema = z.enum(["running", "disabled", "failed"]);
export type PluginStatus = z.infer<typeof PluginStatusSchema>;
export const PluginNpmInstallationSchema = z.object({
packageName: z.string(),
requestedSpec: z.string(),
version: z.string(),
integrity: z.string(),
});
export const PluginListItemSchema = z.object({
id: PluginIdSchema,
name: z.string().optional(),
icon: z.string().optional(),
media: z.array(z.string()).optional(),
description: z.string().optional(),
path: z.string(),
enabled: z.boolean(),
status: PluginStatusSchema,
source: z.enum(["directory", "git"]).optional(),
npm: PluginNpmInstallationSchema.optional(),
installation: PluginInstallationSchema.optional(),
remote: z.string().optional(),
ref: z.string().optional(),
commit: z.string().optional(),
error: z.string().optional(),
});
export type PluginListItem = z.infer<typeof PluginListItemSchema>;
export const PluginLogEntrySchema = z.object({
sequence: z.number().int().nonnegative(),
timestamp: z.string().datetime(),
stream: z.enum(["stdout", "stderr"]),
message: z.string(),
});
export type PluginLogEntry = z.infer<typeof PluginLogEntrySchema>;
export const PluginListResponseSchema = z.object({
type: z.literal("plugin.list.response"),
payload: z.object({ requestId: z.string(), plugins: z.array(PluginListItemSchema) }),
});
export const PluginLogsGetResponseSchema = z.object({
type: z.literal("plugin.logs.get.response"),
payload: z.object({
requestId: z.string(),
pluginId: PluginIdSchema,
entries: z.array(PluginLogEntrySchema),
}),
});
export const PluginDirectoryInstallResponseSchema = z.object({
type: z.literal("plugin.directory.install.response"),
payload: z.object({ requestId: z.string(), plugin: PluginListItemSchema }),
});
export const PluginDirectoryInspectResponseSchema = z.object({
type: z.literal("plugin.directory.inspect.response"),
payload: z.object({ requestId: z.string(), id: PluginIdSchema }),
});
export const PluginSourceInstallResponseSchema = z.object({
type: z.literal("plugin.source.install.response"),
payload: z.object({ requestId: z.string(), plugin: PluginListItemSchema }),
});
export const PluginSourceStatusItemSchema = z.object({
id: PluginIdSchema,
source: z.enum(["directory", "git"]),
npm: PluginNpmInstallationSchema.optional(),
installation: PluginInstallationSchema.optional(),
path: z.string(),
remote: z.string().optional(),
ref: z.string().optional(),
currentCommit: z.string().optional(),
latestCommit: z.string().optional(),
commitsBehind: z.number().int().nonnegative().optional(),
updateAvailable: z.boolean().optional(),
});
export type PluginSourceStatusItem = z.infer<typeof PluginSourceStatusItemSchema>;
export const PluginSourceStatusResponseSchema = z.object({
type: z.literal("plugin.source.status.response"),
payload: z.object({ requestId: z.string(), plugins: z.array(PluginSourceStatusItemSchema) }),
});
export const PluginSourceUpdateItemSchema = z.object({
id: PluginIdSchema,
previousCommit: z.string(),
currentCommit: z.string(),
commits: z.number().int().nonnegative(),
updated: z.boolean(),
});
export type PluginSourceUpdateItem = z.infer<typeof PluginSourceUpdateItemSchema>;
export const PluginSourceUpdateResponseSchema = z.object({
type: z.literal("plugin.source.update.response"),
payload: z.object({ requestId: z.string(), plugins: z.array(PluginSourceUpdateItemSchema) }),
});
export const PluginUpdatePreviewSchema = z.object({
id: PluginIdSchema,
outcome: z.enum(["update", "current", "installed-newer", "local", "error"]),
current: PluginInstallationSchema.optional(),
target: PluginUpdateTargetSchema.optional(),
links: z.array(z.string()),
proposal: PluginUpdateProposalSchema.optional(),
error: z.string().optional(),
});
export type PluginUpdatePreview = z.infer<typeof PluginUpdatePreviewSchema>;
export const PluginUpdateResultSchema = z.object({
id: PluginIdSchema,
outcome: z.enum(["updated", "error"]),
plugin: PluginListItemSchema.optional(),
error: z.string().optional(),
warning: z.string().optional(),
});
export type PluginUpdateResult = z.infer<typeof PluginUpdateResultSchema>;
export const PluginUpdatePreviewResponseSchema = z.object({
type: z.literal("plugin.source.update.preview.response"),
payload: z.object({ requestId: z.string(), plugins: z.array(PluginUpdatePreviewSchema) }),
});
export const PluginUpdateApplyResponseSchema = z.object({
type: z.literal("plugin.source.update.apply.response"),
payload: z.object({ requestId: z.string(), plugins: z.array(PluginUpdateResultSchema) }),
});
function pluginActionResponse<const Type extends string>(type: Type) {
return z.object({
type: z.literal(type),
payload: z.object({ requestId: z.string(), plugin: PluginListItemSchema }),
});
}
export const PluginReloadResponseSchema = pluginActionResponse("plugin.reload.response");
export const PluginEnableResponseSchema = pluginActionResponse("plugin.enable.response");
export const PluginDisableResponseSchema = pluginActionResponse("plugin.disable.response");
export const PluginRemoveResponseSchema = z.object({
type: z.literal("plugin.remove.response"),
payload: z.object({ requestId: z.string() }).strict(),
});
export const PluginRpcInvokeResponseSchema = z.object({
type: z.literal("plugin.rpc.invoke.response"),
payload: z.object({
requestId: z.string(),
output: z.unknown(),
}),
});
export const AgentTimelineAppendResponseSchema = z.object({
type: z.literal("agent.timeline.append.response"),
payload: z.object({
requestId: z.string(),
seq: z.number().int().nonnegative(),
epoch: z.string(),
}),
});
function agentSkillsStatusResponse<const Type extends string>(type: Type) {
return z.object({
type: z.literal(type),
payload: AgentSkillsStatusSchema.extend({ requestId: z.string() }),
});
}
export const AgentSkillsGetStatusResponseSchema = agentSkillsStatusResponse(
"agent.skills.get_status.response",
);
export const AgentSkillsReconcileResponseSchema = agentSkillsStatusResponse(
"agent.skills.reconcile.response",
);
export const AgentSkillsUninstallResponseSchema = agentSkillsStatusResponse(
"agent.skills.uninstall.response",
);
export const AgentSkillsSaveSelectionResponseSchema = z.object({
type: z.literal("agent.skills.save_selection.response"),
payload: AgentSkillsSaveResultSchema.extend({ requestId: z.string() }),
});
export const AgentSkillsImportLegacySelectionResponseSchema = z.object({
type: z.literal("agent.skills.import_legacy_selection.response"),
payload: z.object({
requestId: z.string(),
imported: z.boolean(),
selection: AgentSkillSelectionSchema,
}),
});
export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
BrowserHostRegisterResponseSchema,
SubscriptionReleaseResponseSchema,
SessionEventsSetSubscriptionResponseSchema,
HubExecutionAgentCreateResponseSchema,
HubExecutionAgentValidateResponseSchema,
HubExecutionControlResponseSchema,
HubExecutionAgentUpdateSchema,
HubExecutionAgentStreamSchema,
BrowserAutomationExecuteRequestSchema,
PluginCatalogGetResponseSchema,
PluginListResponseSchema,
PluginLogsGetResponseSchema,
PluginDirectoryInstallResponseSchema,
PluginDirectoryInspectResponseSchema,
PluginSourceInstallResponseSchema,
PluginSourceStatusResponseSchema,
PluginSourceUpdateResponseSchema,
PluginUpdatePreviewResponseSchema,
PluginUpdateApplyResponseSchema,
PluginReloadResponseSchema,
PluginEnableResponseSchema,
PluginDisableResponseSchema,
PluginRemoveResponseSchema,
PluginRpcInvokeResponseSchema,
AgentTimelineAppendResponseSchema,
AgentSkillsGetStatusResponseSchema,
AgentSkillsReconcileResponseSchema,
AgentSkillsUninstallResponseSchema,
AgentSkillsSaveSelectionResponseSchema,
AgentSkillsImportLegacySelectionResponseSchema,
ActivityLogMessageSchema,
AssistantChunkMessageSchema,
AudioOutputMessageSchema,
TranscriptionResultMessageSchema,
VoiceInputStateMessageSchema,
DictationStreamAckMessageSchema,
DictationStreamFinishAcceptedMessageSchema,
DictationStreamPartialMessageSchema,
DictationStreamFinalMessageSchema,
DictationStreamErrorMessageSchema,
StatusMessageSchema,
PongMessageSchema,
PushUnregisterResponseSchema,
RpcErrorMessageSchema,
ArtifactMessageSchema,
AgentUpdateMessageSchema,
WorkspaceUpdateMessageSchema,
WorkspaceLabelListResponseSchema,
WorkspaceLabelUpdateSchema,
WorkspaceLabelAssignmentSetResponseSchema,
WorkspaceLabelUpdateResponseSchema,
WorkspaceLabelDeleteResponseSchema,
WorkspaceLabelDeleteInspectResponseSchema,
ProjectUpdateMessageSchema,
ProjectListResponseMessageSchema,
ScriptStatusUpdateMessageSchema,
WorkspaceSetupProgressMessageSchema,
WorkspaceSetupStatusResponseMessageSchema,
WorkspaceSetupRunResponseMessageSchema,
AgentStreamMessageSchema,
AgentStatusMessageSchema,
FetchAgentsResponseMessageSchema,
FetchAgentHistoryResponseMessageSchema,
FetchRecentProviderSessionsResponseMessageSchema,
FetchWorkspacesResponseMessageSchema,
ProjectAddResponseSchema,
ProjectCreateDirectoryResponseSchema,
OpenProjectResponseMessageSchema,
WorkspaceGithubSearchRepositoriesResponseSchema,
ProjectGithubCloneResponseSchema,
StartWorkspaceScriptResponseMessageSchema,
WorkspaceScriptListResponseMessageSchema,
WorkspaceScriptStartResponseMessageSchema,
WorkspaceScriptStopResponseMessageSchema,
LegacyListAvailableEditorsResponseMessageSchema,
LegacyOpenInEditorResponseMessageSchema,
ArchiveWorkspaceResponseMessageSchema,
FetchAgentResponseMessageSchema,
FetchAgentTimelineResponseMessageSchema,
AgentTimelineReplacementMessageSchema,
AgentTimelineSearchResponseMessageSchema,
AgentTimelineListPromptsResponseMessageSchema,
ProviderSubagentListResponseMessageSchema,
ProviderSubagentTimelineResponseMessageSchema,
ProviderSubagentUpdateMessageSchema,
SetAgentTimelineSubscriptionResponseMessageSchema,
AgentAttentionRequiredMessageSchema,
AgentForkContextResponseMessageSchema,
CancelAgentResponseMessageSchema,
ClearAgentAttentionResponseMessageSchema,
WorkspaceCreateResponseSchema,
AgentCreateResponseSchema,
WorkspaceCreationUpdateSchema,
AgentCreationUpdateSchema,
CreationSubscribeResponseSchema,
WorkspaceClearAttentionResponseSchema,
WorkspaceMarkUnreadResponseSchema,
SendAgentMessageResponseMessageSchema,
SetVoiceModeResponseMessageSchema,
DaemonGetStatusResponseSchema,
DaemonGetPairingOfferResponseSchema,
DaemonConfigReloadResponseSchema,
HubManagementDaemonConnectResponseSchema,
HubManagementDaemonGetStatusResponseSchema,
HubManagementDaemonDisconnectResponseSchema,
HubManagementDaemonPermissionsUpdateResponseSchema,
DiagnosticsResponseSchema,
GetDaemonConfigResponseMessageSchema,
SetDaemonConfigResponseMessageSchema,
ReadProjectConfigResponseMessageSchema,
WriteProjectConfigResponseMessageSchema,
SetAgentModeResponseMessageSchema,
SetAgentModelResponseMessageSchema,
SetAgentThinkingResponseMessageSchema,
SetAgentFeatureResponseMessageSchema,
AgentConfigApplyResponseMessageSchema,
AgentDetachResponseMessageSchema,
AgentRewindResponseMessageSchema,
UpdateAgentResponseMessageSchema,
ProjectRenameResponseSchema,
ProjectIconSetResponseSchema,
ProjectRemoveResponseSchema,
WorkspaceTitleSetResponseSchema,
WorkspacePinSetResponseSchema,
WorkspaceRecoveryInspectResponseSchema,
WorkspaceRecoveryRestoreResponseSchema,
WaitForFinishResponseMessageSchema,
AgentPermissionRequestMessageSchema,
AgentPermissionResolvedMessageSchema,
AgentDeletedMessageSchema,
AgentArchivedMessageSchema,
CloseItemsResponseSchema,
CheckoutStatusResponseSchema,
CheckoutStatusUpdateSchema,
CheckoutDiffGetResponseSchema,
SubscribeCheckoutDiffResponseSchema,
CheckoutDiffUpdateSchema,
CheckoutCommitResponseSchema,
CheckoutMergeResponseSchema,
CheckoutMergeFromBaseResponseSchema,
CheckoutPullResponseSchema,
CheckoutPushResponseSchema,
CheckoutRefreshResponseSchema,
CheckoutDiscardChangesResponseSchema,
CheckoutPrCreateResponseSchema,
CheckoutPrMergeResponseSchema,
CheckoutForgeSetAutoMergeResponseSchema,
CheckoutGithubSetAutoMergeResponseSchema,
CheckoutCommitsListResponseSchema,
CheckoutCommitFileDiffResponseSchema,
CheckoutForgeGetCheckDetailsResponseSchema,
CheckoutGithubGetCheckDetailsResponseSchema,
CheckoutPrStatusResponseSchema,
PullRequestTimelineResponseSchema,
CheckoutSwitchBranchResponseSchema,
CheckoutRenameBranchResponseSchema,
StashSaveResponseSchema,
StashPopResponseSchema,
StashListResponseSchema,
ValidateBranchResponseSchema,
BranchSuggestionsResponseSchema,
ForgeSearchResponseSchema,
GitHubSearchResponseSchema,
DirectorySuggestionsResponseSchema,
PaseoWorktreeListResponseSchema,
PaseoWorktreeArchiveResponseSchema,
CreatePaseoWorktreeResponseSchema,
FileExplorerResponseSchema,
FileSubscribeResponseSchema,
FileUnsubscribeResponseSchema,
FileWriteResponseSchema,
FileEntryCreateResponseSchema,
FileEntryRenameResponseSchema,
FileEntryDuplicateResponseSchema,
FileEntryDeleteResponseSchema,
FileUpdateSchema,
ProjectIconResponseSchema,
ProjectIconGetResponseSchema,
FileDownloadTokenResponseSchema,
FileUploadResponseSchema,
ListProviderModelsResponseMessageSchema,
ListProviderModesResponseMessageSchema,
ListProviderFeaturesResponseMessageSchema,
ListAvailableProvidersResponseSchema,
GetProvidersSnapshotResponseMessageSchema,
ProvidersSnapshotUpdateMessageSchema,
RefreshProvidersSnapshotResponseMessageSchema,
ProviderDiagnosticResponseMessageSchema,
ProviderUsageListResponseMessageSchema,
UsageListReportsUpdateMessageSchema,
UsageListReportsResponseMessageSchema,
ListCommandsResponseSchema,
ListTerminalsResponseSchema,
TerminalsChangedSchema,
CreateTerminalResponseSchema,
RenameTerminalResponseSchema,
SubscribeTerminalResponseSchema,
KillTerminalResponseSchema,
CaptureTerminalResponseSchema,
TerminalStreamExitSchema,
TerminalAttentionRequiredSchema,
ChatCreateResponseSchema,
ChatListResponseSchema,
ChatInspectResponseSchema,
ChatDeleteResponseSchema,
ChatPostResponseSchema,
ChatReadResponseSchema,
ChatWaitResponseSchema,
ScheduleCreateResponseSchema,
ScheduleListResponseSchema,
ScheduleInspectResponseSchema,
ScheduleLogsResponseSchema,
SchedulePauseResponseSchema,
ScheduleResumeResponseSchema,
ScheduleDeleteResponseSchema,
ScheduleRunOnceResponseSchema,
ScheduleUpdateResponseSchema,
LoopRunResponseSchema,
LoopListResponseSchema,
LoopInspectResponseSchema,
LoopLogsResponseSchema,
LoopStopResponseSchema,
DaemonUpdateProgressMessageSchema,
DaemonUpdateResponseSchema,
]);
export type SessionOutboundMessage = z.infer<typeof SessionOutboundMessageSchema>;
// Type exports for individual message types
export type ActivityLogMessage = z.infer<typeof ActivityLogMessageSchema>;
export type AssistantChunkMessage = z.infer<typeof AssistantChunkMessageSchema>;
export type AudioOutputMessage = z.infer<typeof AudioOutputMessageSchema>;
export type TranscriptionResultMessage = z.infer<typeof TranscriptionResultMessageSchema>;
export type StatusMessage = z.infer<typeof StatusMessageSchema>;
export type ServerCapabilityState = z.infer<typeof ServerCapabilityStateSchema>;
export type ServerVoiceCapabilities = z.infer<typeof ServerVoiceCapabilitiesSchema>;
export type ServerCapabilities = z.infer<typeof ServerCapabilitiesSchema>;
export type ServerInfoStatusPayload = z.infer<typeof ServerInfoStatusPayloadSchema>;
export type RpcErrorMessage = z.infer<typeof RpcErrorMessageSchema>;
export type ArtifactMessage = z.infer<typeof ArtifactMessageSchema>;
export type AgentUpdateMessage = z.infer<typeof AgentUpdateMessageSchema>;
export type WorkspaceSetupProgressMessage = z.infer<typeof WorkspaceSetupProgressMessageSchema>;
export type WorkspaceSetupSnapshot = z.infer<typeof WorkspaceSetupSnapshotSchema>;
export type WorkspaceSetupStatusResponseMessage = z.infer<
typeof WorkspaceSetupStatusResponseMessageSchema
>;
export type WorkspaceSetupRunResponseMessage = z.infer<
typeof WorkspaceSetupRunResponseMessageSchema
>;
export type AgentStreamMessage = z.infer<typeof AgentStreamMessageSchema>;
export type AgentStatusMessage = z.infer<typeof AgentStatusMessageSchema>;
export type ProjectCheckoutLitePayload = z.infer<typeof ProjectCheckoutLitePayloadSchema>;
export type ProjectPlacementPayload = z.infer<typeof ProjectPlacementPayloadSchema>;
export type WorkspaceStateBucket = z.infer<typeof WorkspaceStateBucketSchema>;
export type WorkspaceDescriptorPayload = z.infer<typeof WorkspaceDescriptorPayloadSchema>;
export type WorkspaceProjectDescriptorPayload = z.infer<
typeof WorkspaceProjectDescriptorPayloadSchema
>;
export type ProjectListResponseMessage = z.infer<typeof ProjectListResponseMessageSchema>;
export type WorkspaceScriptLifecycle = z.infer<typeof WorkspaceScriptLifecycleSchema>;
export type WorkspaceScriptHealth = z.infer<typeof WorkspaceScriptHealthSchema>;
export type WorkspaceScriptPayload = z.infer<typeof WorkspaceScriptPayloadSchema>;
export type FetchAgentsResponseMessage = z.infer<typeof FetchAgentsResponseMessageSchema>;
export type FetchAgentHistoryResponseMessage = z.infer<
typeof FetchAgentHistoryResponseMessageSchema
>;
export type FetchRecentProviderSessionsResponseMessage = z.infer<
typeof FetchRecentProviderSessionsResponseMessageSchema
>;
export type FetchWorkspacesResponseMessage = z.infer<typeof FetchWorkspacesResponseMessageSchema>;
export type ProjectAddResponse = z.infer<typeof ProjectAddResponseSchema>;
export type ProjectCreateDirectoryResponse = z.infer<typeof ProjectCreateDirectoryResponseSchema>;
export type ScriptStatusUpdateMessage = z.infer<typeof ScriptStatusUpdateMessageSchema>;
export type OpenProjectResponseMessage = z.infer<typeof OpenProjectResponseMessageSchema>;
export type WorkspaceGithubSearchRepositoriesResponse = z.infer<
typeof WorkspaceGithubSearchRepositoriesResponseSchema
>;
export type GithubRepository = z.infer<typeof GithubRepositorySchema>;
export type ProjectGithubCloneResponse = z.infer<typeof ProjectGithubCloneResponseSchema>;
export type StartWorkspaceScriptResponseMessage = z.infer<
typeof StartWorkspaceScriptResponseMessageSchema
>;
export type WorkspaceScriptListRequest = z.infer<typeof WorkspaceScriptListRequestSchema>;
export type WorkspaceScriptStartRequest = z.infer<typeof WorkspaceScriptStartRequestSchema>;
export type WorkspaceScriptStopRequest = z.infer<typeof WorkspaceScriptStopRequestSchema>;
export type WorkspaceScriptListResponseMessage = z.infer<
typeof WorkspaceScriptListResponseMessageSchema
>;
export type WorkspaceScriptStartResponseMessage = z.infer<
typeof WorkspaceScriptStartResponseMessageSchema
>;
export type WorkspaceScriptStopResponseMessage = z.infer<
typeof WorkspaceScriptStopResponseMessageSchema
>;
export type LegacyListAvailableEditorsResponseMessage = z.infer<
typeof LegacyListAvailableEditorsResponseMessageSchema
>;
export type LegacyOpenInEditorResponseMessage = z.infer<
typeof LegacyOpenInEditorResponseMessageSchema
>;
export type ArchiveWorkspaceResponseMessage = z.infer<typeof ArchiveWorkspaceResponseMessageSchema>;
export type FetchAgentResponseMessage = z.infer<typeof FetchAgentResponseMessageSchema>;
export type FetchAgentTimelineResponseMessage = z.infer<
typeof FetchAgentTimelineResponseMessageSchema
>;
export type AgentTimelineListPromptsResponseMessage = z.infer<
typeof AgentTimelineListPromptsResponseMessageSchema
>;
export type AgentForkContextResponseMessage = z.infer<typeof AgentForkContextResponseMessageSchema>;
export type CancelAgentResponseMessage = z.infer<typeof CancelAgentResponseMessageSchema>;
export type SendAgentMessageResponseMessage = z.infer<typeof SendAgentMessageResponseMessageSchema>;
export type SetVoiceModeResponseMessage = z.infer<typeof SetVoiceModeResponseMessageSchema>;
export type SetAgentModeResponseMessage = z.infer<typeof SetAgentModeResponseMessageSchema>;
export type SetAgentModelResponseMessage = z.infer<typeof SetAgentModelResponseMessageSchema>;
export type SetAgentThinkingResponseMessage = z.infer<typeof SetAgentThinkingResponseMessageSchema>;
export type SetAgentFeatureResponseMessage = z.infer<typeof SetAgentFeatureResponseMessageSchema>;
export type AgentConfigApplyResponseMessage = z.infer<typeof AgentConfigApplyResponseMessageSchema>;
export type AgentDetachResponseMessage = z.infer<typeof AgentDetachResponseMessageSchema>;
export type AgentRewindResponseMessage = z.infer<typeof AgentRewindResponseMessageSchema>;
export type UpdateAgentResponseMessage = z.infer<typeof UpdateAgentResponseMessageSchema>;
export type ProjectRenameResponse = z.infer<typeof ProjectRenameResponseSchema>;
export type ProjectIconSetResponse = z.infer<typeof ProjectIconSetResponseSchema>;
export type ProjectRemoveResponse = z.infer<typeof ProjectRemoveResponseSchema>;
export type WorkspaceTitleSetResponse = z.infer<typeof WorkspaceTitleSetResponseSchema>;
export type WorkspaceTitleSetResponsePayload = z.infer<
typeof WorkspaceTitleSetResponsePayloadSchema
>;
export type WorkspacePinSetResponse = z.infer<typeof WorkspacePinSetResponseSchema>;
export type WorkspacePinSetResponsePayload = z.infer<typeof WorkspacePinSetResponsePayloadSchema>;
export type WorkspaceRecoveryState = z.infer<typeof WorkspaceRecoveryStateSchema>;
export type WorkspaceRecoveryInspectResponse = z.infer<
typeof WorkspaceRecoveryInspectResponseSchema
>;
export type WorkspaceRecoveryRestoreResponse = z.infer<
typeof WorkspaceRecoveryRestoreResponseSchema
>;
export type WorkspaceCreateRequest = z.infer<typeof WorkspaceCreateRequestSchema>;
export type WorkspaceCreateResponse = z.infer<typeof WorkspaceCreateResponseSchema>;
export type ProjectRenameResponsePayload = z.infer<typeof ProjectRenameResponsePayloadSchema>;
export type ProjectRemoveResponsePayload = z.infer<typeof ProjectRemoveResponsePayloadSchema>;
export type WaitForFinishResponseMessage = z.infer<typeof WaitForFinishResponseMessageSchema>;
export type AgentPermissionRequestMessage = z.infer<typeof AgentPermissionRequestMessageSchema>;
export type AgentPermissionResolvedMessage = z.infer<typeof AgentPermissionResolvedMessageSchema>;
export type AgentDeletedMessage = z.infer<typeof AgentDeletedMessageSchema>;
export type ListProviderModelsResponseMessage = z.infer<
typeof ListProviderModelsResponseMessageSchema
>;
export type ListProviderModesResponseMessage = z.infer<
typeof ListProviderModesResponseMessageSchema
>;
export type ListProviderFeaturesResponseMessage = z.infer<
typeof ListProviderFeaturesResponseMessageSchema
>;
export type ListAvailableProvidersResponse = z.infer<typeof ListAvailableProvidersResponseSchema>;
export type DaemonGetStatusResponse = z.infer<typeof DaemonGetStatusResponseSchema>;
export type DaemonGetPairingOfferResponse = z.infer<typeof DaemonGetPairingOfferResponseSchema>;
export type DaemonConfigReloadResponse = z.infer<typeof DaemonConfigReloadResponseSchema>;
export type DiagnosticsResponse = z.infer<typeof DiagnosticsResponseSchema>;
export type GetProvidersSnapshotResponseMessage = z.infer<
typeof GetProvidersSnapshotResponseMessageSchema
>;
export type ProvidersSnapshotUpdateMessage = z.infer<typeof ProvidersSnapshotUpdateMessageSchema>;
export type RefreshProvidersSnapshotResponseMessage = z.infer<
typeof RefreshProvidersSnapshotResponseMessageSchema
>;
export type ProviderDiagnosticResponseMessage = z.infer<
typeof ProviderDiagnosticResponseMessageSchema
>;
export type ProviderUsageTone = z.infer<typeof ProviderUsageToneSchema>;
export type UsageProblem = z.infer<typeof UsageProblemSchema>;
export type UsageReport = z.infer<typeof UsageReportSchema>;
export type UsageReportEntry = z.infer<typeof UsageReportEntrySchema>;
export type UsageListReportsResponseMessage = z.infer<typeof UsageListReportsResponseMessageSchema>;
export type ProviderUsageStatus = z.infer<typeof ProviderUsageStatusSchema>;
export type ProviderUsage = z.infer<typeof ProviderUsageSchema>;
export type ProviderUsageWindow = z.infer<typeof ProviderUsageWindowSchema>;
export type ProviderUsageBalance = z.infer<typeof ProviderUsageBalanceSchema>;
export type ProviderUsageDetail = z.infer<typeof ProviderUsageDetailSchema>;
export type ProviderUsageListResponseMessage = z.infer<
typeof ProviderUsageListResponseMessageSchema
>;
export type ChatCreateResponse = z.infer<typeof ChatCreateResponseSchema>;
export type ChatListResponse = z.infer<typeof ChatListResponseSchema>;
export type ChatInspectResponse = z.infer<typeof ChatInspectResponseSchema>;
export type ChatDeleteResponse = z.infer<typeof ChatDeleteResponseSchema>;
export type ChatPostResponse = z.infer<typeof ChatPostResponseSchema>;
export type ChatReadResponse = z.infer<typeof ChatReadResponseSchema>;
export type ChatWaitResponse = z.infer<typeof ChatWaitResponseSchema>;
export type ScheduleCreateResponse = z.infer<typeof ScheduleCreateResponseSchema>;
export type ScheduleListResponse = z.infer<typeof ScheduleListResponseSchema>;
export type ScheduleInspectResponse = z.infer<typeof ScheduleInspectResponseSchema>;
export type ScheduleLogsResponse = z.infer<typeof ScheduleLogsResponseSchema>;
export type SchedulePauseResponse = z.infer<typeof SchedulePauseResponseSchema>;
export type ScheduleResumeResponse = z.infer<typeof ScheduleResumeResponseSchema>;
export type ScheduleDeleteResponse = z.infer<typeof ScheduleDeleteResponseSchema>;
export type ScheduleRunOnceResponse = z.infer<typeof ScheduleRunOnceResponseSchema>;
export type ScheduleUpdateResponse = z.infer<typeof ScheduleUpdateResponseSchema>;
export type LoopRunResponse = z.infer<typeof LoopRunResponseSchema>;
export type LoopListResponse = z.infer<typeof LoopListResponseSchema>;
export type LoopInspectResponse = z.infer<typeof LoopInspectResponseSchema>;
export type LoopLogsResponse = z.infer<typeof LoopLogsResponseSchema>;
export type LoopStopResponse = z.infer<typeof LoopStopResponseSchema>;
// Type exports for payload types
export type ActivityLogPayload = z.infer<typeof ActivityLogPayloadSchema>;
// Type exports for inbound message types
export type VoiceAudioChunkMessage = z.infer<typeof VoiceAudioChunkMessageSchema>;
export type FetchAgentsRequestMessage = z.infer<typeof FetchAgentsRequestMessageSchema>;
export type FetchAgentHistoryRequestMessage = z.infer<typeof FetchAgentHistoryRequestMessageSchema>;
export type FetchRecentProviderSessionsRequestMessage = z.infer<
typeof FetchRecentProviderSessionsRequestMessageSchema
>;
export type FetchWorkspacesRequestMessage = z.infer<typeof FetchWorkspacesRequestMessageSchema>;
export type ProjectListRequestMessage = z.infer<typeof ProjectListRequestMessageSchema>;
export type FetchAgentRequestMessage = z.infer<typeof FetchAgentRequestMessageSchema>;
export type AgentForkContextRequestMessage = z.infer<typeof AgentForkContextRequestMessageSchema>;
export type SendAgentMessageRequest = z.infer<typeof SendAgentMessageRequestSchema>;
export type WaitForFinishRequest = z.infer<typeof WaitForFinishRequestSchema>;
export type DictationStreamStartMessage = z.infer<typeof DictationStreamStartMessageSchema>;
export type DictationStreamChunkMessage = z.infer<typeof DictationStreamChunkMessageSchema>;
export type DictationStreamFinishMessage = z.infer<typeof DictationStreamFinishMessageSchema>;
export type DictationStreamCancelMessage = z.infer<typeof DictationStreamCancelMessageSchema>;
export type CreateAgentRequestMessage = z.infer<typeof CreateAgentRequestMessageSchema>;
export type AgentAttachment = z.infer<typeof AgentAttachmentSchema>;
export type ForgeChangeRequestAttachment = z.infer<typeof ForgeChangeRequestAttachmentSchema>;
export type ForgeIssueAttachment = z.infer<typeof ForgeIssueAttachmentSchema>;
export type UploadedFileAttachment = z.infer<typeof UploadedFileAttachmentSchema>;
export type FirstAgentContext = z.infer<typeof FirstAgentContextSchema>;
export type ReviewAttachment = z.infer<typeof ReviewAttachmentSchema>;
export type ListProviderModelsRequestMessage = z.infer<
typeof ListProviderModelsRequestMessageSchema
>;
export type ListProviderModesRequestMessage = z.infer<typeof ListProviderModesRequestMessageSchema>;
export type ListProviderFeaturesRequestMessage = z.infer<
typeof ListProviderFeaturesRequestMessageSchema
>;
export type ListAvailableProvidersRequestMessage = z.infer<
typeof ListAvailableProvidersRequestMessageSchema
>;
export type GetProvidersSnapshotRequestMessage = z.infer<
typeof GetProvidersSnapshotRequestMessageSchema
>;
export type RefreshProvidersSnapshotRequestMessage = z.infer<
typeof RefreshProvidersSnapshotRequestMessageSchema
>;
export type ProviderDiagnosticRequestMessage = z.infer<
typeof ProviderDiagnosticRequestMessageSchema
>;
export type ChatCreateRequest = z.infer<typeof ChatCreateRequestSchema>;
export type ChatListRequest = z.infer<typeof ChatListRequestSchema>;
export type ChatInspectRequest = z.infer<typeof ChatInspectRequestSchema>;
export type ChatDeleteRequest = z.infer<typeof ChatDeleteRequestSchema>;
export type ChatPostRequest = z.infer<typeof ChatPostRequestSchema>;
export type ChatReadRequest = z.infer<typeof ChatReadRequestSchema>;
export type ChatWaitRequest = z.infer<typeof ChatWaitRequestSchema>;
export type ScheduleCreateRequest = z.infer<typeof ScheduleCreateRequestSchema>;
export type ScheduleListRequest = z.infer<typeof ScheduleListRequestSchema>;
export type ScheduleInspectRequest = z.infer<typeof ScheduleInspectRequestSchema>;
export type ScheduleLogsRequest = z.infer<typeof ScheduleLogsRequestSchema>;
export type SchedulePauseRequest = z.infer<typeof SchedulePauseRequestSchema>;
export type ScheduleResumeRequest = z.infer<typeof ScheduleResumeRequestSchema>;
export type ScheduleDeleteRequest = z.infer<typeof ScheduleDeleteRequestSchema>;
export type ScheduleRunOnceRequest = z.infer<typeof ScheduleRunOnceRequestSchema>;
export type ScheduleUpdateRequest = z.infer<typeof ScheduleUpdateRequestSchema>;
export type LoopRunRequest = z.infer<typeof LoopRunRequestSchema>;
export type LoopListRequest = z.infer<typeof LoopListRequestSchema>;
export type LoopInspectRequest = z.infer<typeof LoopInspectRequestSchema>;
export type LoopLogsRequest = z.infer<typeof LoopLogsRequestSchema>;
export type LoopStopRequest = z.infer<typeof LoopStopRequestSchema>;
export type ResumeAgentRequestMessage = z.infer<typeof ResumeAgentRequestMessageSchema>;
export type DeleteAgentRequestMessage = z.infer<typeof DeleteAgentRequestMessageSchema>;
export type UpdateAgentRequestMessage = z.infer<typeof UpdateAgentRequestMessageSchema>;
export type ProjectIconSource = z.infer<typeof ProjectIconSourceSchema>;
export type ProjectRenameRequest = z.infer<typeof ProjectRenameRequestSchema>;
export type ProjectIconSetRequest = z.infer<typeof ProjectIconSetRequestSchema>;
export type ProjectRemoveRequest = z.infer<typeof ProjectRemoveRequestSchema>;
export type WorkspaceTitleSetRequest = z.infer<typeof WorkspaceTitleSetRequestSchema>;
export type WorkspacePinSetRequest = z.infer<typeof WorkspacePinSetRequestSchema>;
export type WorkspaceRecoveryInspectRequest = z.infer<typeof WorkspaceRecoveryInspectRequestSchema>;
export type WorkspaceRecoveryRestoreRequest = z.infer<typeof WorkspaceRecoveryRestoreRequestSchema>;
export type SetAgentModeRequestMessage = z.infer<typeof SetAgentModeRequestMessageSchema>;
export type SetAgentModelRequestMessage = z.infer<typeof SetAgentModelRequestMessageSchema>;
export type SetAgentThinkingRequestMessage = z.infer<typeof SetAgentThinkingRequestMessageSchema>;
export type SetAgentFeatureRequestMessage = z.infer<typeof SetAgentFeatureRequestMessageSchema>;
export type AgentConfigApplyRequestMessage = z.infer<typeof AgentConfigApplyRequestMessageSchema>;
export type AgentDetachRequestMessage = z.infer<typeof AgentDetachRequestMessageSchema>;
export type AgentPermissionResponseMessage = z.infer<typeof AgentPermissionResponseMessageSchema>;
export type CheckoutStatusRequest = z.infer<typeof CheckoutStatusRequestSchema>;
export type CheckoutStatusResponse = z.infer<typeof CheckoutStatusResponseSchema>;
export type CheckoutStatusUpdate = z.infer<typeof CheckoutStatusUpdateSchema>;
export type SubscribeCheckoutDiffRequest = z.infer<typeof SubscribeCheckoutDiffRequestSchema>;
export type UnsubscribeCheckoutDiffRequest = z.infer<typeof UnsubscribeCheckoutDiffRequestSchema>;
export type SubscribeCheckoutDiffResponse = z.infer<typeof SubscribeCheckoutDiffResponseSchema>;
export type CheckoutDiffUpdate = z.infer<typeof CheckoutDiffUpdateSchema>;
export type CheckoutCommitRequest = z.infer<typeof CheckoutCommitRequestSchema>;
export type CheckoutCommitResponse = z.infer<typeof CheckoutCommitResponseSchema>;
export type CheckoutMergeRequest = z.infer<typeof CheckoutMergeRequestSchema>;
export type CheckoutMergeResponse = z.infer<typeof CheckoutMergeResponseSchema>;
export type CheckoutMergeFromBaseRequest = z.infer<typeof CheckoutMergeFromBaseRequestSchema>;
export type CheckoutMergeFromBaseResponse = z.infer<typeof CheckoutMergeFromBaseResponseSchema>;
export type CheckoutPullRequest = z.infer<typeof CheckoutPullRequestSchema>;
export type CheckoutPullResponse = z.infer<typeof CheckoutPullResponseSchema>;
export type CheckoutPushRequest = z.infer<typeof CheckoutPushRequestSchema>;
export type CheckoutPushResponse = z.infer<typeof CheckoutPushResponseSchema>;
export type CheckoutRefreshRequest = z.infer<typeof CheckoutRefreshRequestSchema>;
export type CheckoutRefreshResponse = z.infer<typeof CheckoutRefreshResponseSchema>;
export type CheckoutDiscardChangesRequest = z.infer<typeof CheckoutDiscardChangesRequestSchema>;
export type CheckoutDiscardChangesResponse = z.infer<typeof CheckoutDiscardChangesResponseSchema>;
export type CheckoutCommitFile = z.infer<typeof CheckoutCommitFileSchema>;
export type CheckoutCommit = z.infer<typeof CheckoutCommitSchema>;
export type CheckoutCommitsListRequest = z.infer<typeof CheckoutCommitsListRequestSchema>;
export type CheckoutCommitsListResponse = z.infer<typeof CheckoutCommitsListResponseSchema>;
export type CheckoutCommitFileDiffRequest = z.infer<typeof CheckoutCommitFileDiffRequestSchema>;
export type CheckoutCommitFileDiffResponse = z.infer<typeof CheckoutCommitFileDiffResponseSchema>;
export type ParsedDiffFile = z.infer<typeof ParsedDiffFileSchema>;
export type CheckoutPrCreateRequest = z.infer<typeof CheckoutPrCreateRequestSchema>;
export type CheckoutPrCreateResponse = z.infer<typeof CheckoutPrCreateResponseSchema>;
export type CheckoutPrMergeRequest = z.infer<typeof CheckoutPrMergeRequestSchema>;
export type CheckoutPrMergeResponse = z.infer<typeof CheckoutPrMergeResponseSchema>;
export type CheckoutPrMergeMethod = z.infer<typeof CheckoutPrMergeRequestSchema>["mergeMethod"];
export type CheckoutForgeSetAutoMergeRequest = z.infer<
typeof CheckoutForgeSetAutoMergeRequestSchema
>;
export type CheckoutForgeSetAutoMergeResponse = z.infer<
typeof CheckoutForgeSetAutoMergeResponseSchema
>;
export type CheckoutGithubSetAutoMergeRequest = z.infer<
typeof CheckoutGithubSetAutoMergeRequestSchema
>;
export type CheckoutGithubSetAutoMergeResponse = z.infer<
typeof CheckoutGithubSetAutoMergeResponseSchema
>;
export type CheckoutForgeGetCheckDetailsRequest = z.infer<
typeof CheckoutForgeGetCheckDetailsRequestSchema
>;
export type CheckoutGithubGetCheckDetailsRequest = z.infer<
typeof CheckoutGithubGetCheckDetailsRequestSchema
>;
export type CheckoutCheckDetails = z.infer<typeof CheckoutCheckDetailsSchema>;
export type CheckoutGithubCheckDetails = z.infer<typeof CheckoutGithubCheckDetailsSchema>;
export type CheckoutPipeline = z.infer<typeof CheckoutPipelineSchema>;
export type CheckoutPipelineStage = z.infer<typeof CheckoutPipelineStageSchema>;
export type CheckoutPipelineJob = z.infer<typeof CheckoutPipelineJobSchema>;
export type CheckoutForgeGetCheckDetailsResponse = z.infer<
typeof CheckoutForgeGetCheckDetailsResponseSchema
>;
export type CheckoutGithubGetCheckDetailsResponse = z.infer<
typeof CheckoutGithubGetCheckDetailsResponseSchema
>;
export type PullRequestMergeable = z.infer<typeof CheckoutPrStatusSchema>["mergeable"];
export type CheckoutPrStatusRequest = z.infer<typeof CheckoutPrStatusRequestSchema>;
export type CheckoutPrStatusResponse = z.infer<typeof CheckoutPrStatusResponseSchema>;
export type PullRequestTimelineRequest = z.infer<typeof PullRequestTimelineRequestSchema>;
export type PullRequestTimelineItem = z.infer<typeof PullRequestTimelineItemSchema>;
export type PullRequestTimelineResponse = z.infer<typeof PullRequestTimelineResponseSchema>;
export type CheckoutSwitchBranchRequest = z.infer<typeof CheckoutSwitchBranchRequestSchema>;
export type CheckoutSwitchBranchResponse = z.infer<typeof CheckoutSwitchBranchResponseSchema>;
export type CheckoutRenameBranchRequest = z.infer<typeof CheckoutRenameBranchRequestSchema>;
export type CheckoutRenameBranchResponse = z.infer<typeof CheckoutRenameBranchResponseSchema>;
export type StashSaveRequest = z.infer<typeof StashSaveRequestSchema>;
export type StashSaveResponse = z.infer<typeof StashSaveResponseSchema>;
export type StashPopRequest = z.infer<typeof StashPopRequestSchema>;
export type StashPopResponse = z.infer<typeof StashPopResponseSchema>;
export type StashListRequest = z.infer<typeof StashListRequestSchema>;
export type StashListResponse = z.infer<typeof StashListResponseSchema>;
export type StashEntry = z.infer<typeof StashEntrySchema>;
export type ValidateBranchRequest = z.infer<typeof ValidateBranchRequestSchema>;
export type ValidateBranchResponse = z.infer<typeof ValidateBranchResponseSchema>;
export type BranchSuggestionsRequest = z.infer<typeof BranchSuggestionsRequestSchema>;
export type BranchSuggestionsResponse = z.infer<typeof BranchSuggestionsResponseSchema>;
export type ForgeSearchItem = z.infer<typeof ForgeSearchItemSchema>;
export type ForgeSearchKind = "issue" | "change_request";
export type ForgeSearchRequest = z.infer<typeof ForgeSearchRequestSchema>;
export type ForgeSearchResponse = z.infer<typeof ForgeSearchResponseSchema>;
export type GitHubSearchItem = z.infer<typeof GitHubSearchItemSchema>;
export type GitHubSearchKind = z.infer<typeof GitHubSearchKindSchema>;
export type GitHubSearchRequest = z.infer<typeof GitHubSearchRequestSchema>;
export type GitHubSearchResponse = z.infer<typeof GitHubSearchResponseSchema>;
export type ChangeRequestCheckoutSource = z.infer<typeof ChangeRequestCheckoutSourceSchema>;
export type CreatePaseoWorktreeRequest = z.infer<typeof CreatePaseoWorktreeRequestSchema>;
export type DirectorySuggestionsRequest = z.infer<typeof DirectorySuggestionsRequestSchema>;
export type DirectorySuggestionsResponse = z.infer<typeof DirectorySuggestionsResponseSchema>;
export type PaseoWorktreeListRequest = z.infer<typeof PaseoWorktreeListRequestSchema>;
export type PaseoWorktreeListResponse = z.infer<typeof PaseoWorktreeListResponseSchema>;
export type PaseoWorktreeArchiveRequest = z.infer<typeof PaseoWorktreeArchiveRequestSchema>;
export type PaseoWorktreeArchiveResponse = z.infer<typeof PaseoWorktreeArchiveResponseSchema>;
export type WorkspaceSetupStatusRequest = z.infer<typeof WorkspaceSetupStatusRequestSchema>;
export type WorkspaceSetupRunRequest = z.infer<typeof WorkspaceSetupRunRequestSchema>;
export type LegacyListAvailableEditorsRequest = z.infer<
typeof LegacyListAvailableEditorsRequestSchema
>;
export type LegacyOpenInEditorRequest = z.infer<typeof LegacyOpenInEditorRequestSchema>;
export type OpenProjectRequest = z.infer<typeof OpenProjectRequestSchema>;
export type ProjectAddRequest = z.infer<typeof ProjectAddRequestSchema>;
export type ProjectCreateDirectoryRequest = z.infer<typeof ProjectCreateDirectoryRequestSchema>;
export type ProjectCreateDirectoryErrorCode = z.infer<typeof ProjectCreateDirectoryErrorCodeSchema>;
export type WorkspaceGithubSearchRepositoriesRequest = z.infer<
typeof WorkspaceGithubSearchRepositoriesRequestSchema
>;
export type ProjectGithubCloneRequest = z.infer<typeof ProjectGithubCloneRequestSchema>;
export type ProjectGithubCloneProtocol = z.infer<typeof ProjectGithubCloneProtocolSchema>;
export type ArchiveWorkspaceRequest = z.infer<typeof ArchiveWorkspaceRequestSchema>;
export type WorkspaceClearAttentionRequest = z.infer<typeof WorkspaceClearAttentionRequestSchema>;
export type WorkspaceMarkUnreadRequest = z.infer<typeof WorkspaceMarkUnreadRequestSchema>;
export type FileExplorerRequest = z.infer<typeof FileExplorerRequestSchema>;
export type FileExplorerResponse = z.infer<typeof FileExplorerResponseSchema>;
export type FileVersion = z.infer<typeof FileVersionSchema>;
export type FileSubscribeRequest = z.infer<typeof FileSubscribeRequestSchema>;
export type FileSubscribeResponse = z.infer<typeof FileSubscribeResponseSchema>;
export type FileUnsubscribeRequest = z.infer<typeof FileUnsubscribeRequestSchema>;
export type FileUnsubscribeResponse = z.infer<typeof FileUnsubscribeResponseSchema>;
export type FileWriteRequest = z.infer<typeof FileWriteRequestSchema>;
export type FileWriteResponse = z.infer<typeof FileWriteResponseSchema>;
export type FileEntryCreateRequest = z.infer<typeof FileEntryCreateRequestSchema>;
export type FileEntryCreateResponse = z.infer<typeof FileEntryCreateResponseSchema>;
export type FileEntryRenameRequest = z.infer<typeof FileEntryRenameRequestSchema>;
export type FileEntryRenameResponse = z.infer<typeof FileEntryRenameResponseSchema>;
export type FileEntryDuplicateRequest = z.infer<typeof FileEntryDuplicateRequestSchema>;
export type FileEntryDuplicateResponse = z.infer<typeof FileEntryDuplicateResponseSchema>;
export type FileEntryDeleteRequest = z.infer<typeof FileEntryDeleteRequestSchema>;
export type FileEntryDeleteResponse = z.infer<typeof FileEntryDeleteResponseSchema>;
export type FileWriteResult = z.infer<typeof FileWriteResultSchema>;
export type FileUpdate = z.infer<typeof FileUpdateSchema>;
export type ProjectIconRequest = z.infer<typeof ProjectIconRequestSchema>;
export type ProjectIconResponse = z.infer<typeof ProjectIconResponseSchema>;
export type ProjectIconGetRequest = z.infer<typeof ProjectIconGetRequestSchema>;
export type ProjectIconGetResponse = z.infer<typeof ProjectIconGetResponseSchema>;
export type ProjectIcon = z.infer<typeof ProjectIconSchema>;
export type FileDownloadTokenRequest = z.infer<typeof FileDownloadTokenRequestSchema>;
export type FileDownloadTokenResponse = z.infer<typeof FileDownloadTokenResponseSchema>;
export type FileUploadRequest = z.infer<typeof FileUploadRequestSchema>;
export type FileUploadResponse = z.infer<typeof FileUploadResponseSchema>;
export type RestartServerRequestMessage = z.infer<typeof RestartServerRequestMessageSchema>;
export type ShutdownServerRequestMessage = z.infer<typeof ShutdownServerRequestMessageSchema>;
export type ClearAgentAttentionMessage = z.infer<typeof ClearAgentAttentionMessageSchema>;
export type ClearAgentAttentionResponseMessage = z.infer<
typeof ClearAgentAttentionResponseMessageSchema
>;
export type ClientHeartbeatMessage = z.infer<typeof ClientHeartbeatMessageSchema>;
export type ListCommandsRequest = z.infer<typeof ListCommandsRequestSchema>;
export type ListCommandsResponse = z.infer<typeof ListCommandsResponseSchema>;
export type RegisterPushTokenMessage = z.infer<typeof RegisterPushTokenMessageSchema>;
export type PushUnregisterRequest = z.infer<typeof PushUnregisterRequestSchema>;
export type PushUnregisterResponse = z.infer<typeof PushUnregisterResponseSchema>;
// Terminal message types
export type ListTerminalsRequest = z.infer<typeof ListTerminalsRequestSchema>;
export type ListTerminalsResponse = z.infer<typeof ListTerminalsResponseSchema>;
export type SubscribeTerminalsRequest = z.infer<typeof SubscribeTerminalsRequestSchema>;
export type UnsubscribeTerminalsRequest = z.infer<typeof UnsubscribeTerminalsRequestSchema>;
export type TerminalsChanged = z.infer<typeof TerminalsChangedSchema>;
export type CreateTerminalRequest = z.infer<typeof CreateTerminalRequestSchema>;
export type CreateTerminalResponse = z.infer<typeof CreateTerminalResponseSchema>;
export type RenameTerminalRequest = z.infer<typeof RenameTerminalRequestSchema>;
export type RenameTerminalResponse = z.infer<typeof RenameTerminalResponseSchema>;
export type StartWorkspaceScriptRequest = z.infer<typeof StartWorkspaceScriptRequestSchema>;
export type StartWorkspaceScriptResponse = z.infer<
typeof StartWorkspaceScriptResponseMessageSchema
>;
export type SubscribeTerminalRequest = z.infer<typeof SubscribeTerminalRequestSchema>;
export type SubscribeTerminalResponse = z.infer<typeof SubscribeTerminalResponseSchema>;
export type UnsubscribeTerminalRequest = z.infer<typeof UnsubscribeTerminalRequestSchema>;
export type TerminalInput = z.infer<typeof TerminalInputSchema>;
export type TerminalCell = z.infer<typeof TerminalCellSchema>;
export type TerminalCursorStyle = z.infer<typeof TerminalCursorStyleSchema>;
export type TerminalCursor = z.infer<typeof TerminalCursorSchema>;
export type TerminalState = z.infer<typeof TerminalStateSchema>;
export type CloseItemsRequest = z.infer<typeof CloseItemsRequestMessageSchema>;
export type CloseItemsResponse = z.infer<typeof CloseItemsResponseSchema>;
export type KillTerminalRequest = z.infer<typeof KillTerminalRequestSchema>;
export type KillTerminalResponse = z.infer<typeof KillTerminalResponseSchema>;
export type CaptureTerminalRequest = z.infer<typeof CaptureTerminalRequestSchema>;
export type CaptureTerminalResponse = z.infer<typeof CaptureTerminalResponseSchema>;
export type TerminalStreamExit = z.infer<typeof TerminalStreamExitSchema>;
// ============================================================================
// WebSocket Level Messages (wraps session messages)
// ============================================================================
// WebSocket-only messages (not session messages)
export const WSPingMessageSchema = z.object({
type: z.literal("ping"),
});
export const WSPongMessageSchema = z.object({
type: z.literal("pong"),
});
export const WSHelloMessageSchema = z.object({
type: z.literal("hello"),
clientId: z.string().min(1),
clientType: z.enum(["mobile", "browser", "cli", "mcp", "hub"]),
protocolVersion: z.number().int(),
auth: z
.discriminatedUnion("kind", [
z.object({ kind: z.literal("password"), password: z.string() }),
z.object({ kind: z.literal("localCredential"), token: z.string() }),
])
.optional(),
appVersion: z.string().optional(),
capabilities: z
.object({
voice: z.boolean().optional(),
[CLIENT_CAPS.helloRejection]: z.boolean().optional(),
pushNotifications: z.boolean().optional(),
[CLIENT_CAPS.explicitEventSubscriptions]: z.boolean().optional(),
[CLIENT_CAPS.allProviders]: z.boolean().optional(),
[CLIENT_CAPS.reasoningMergeEnum]: z.boolean().optional(),
[CLIENT_CAPS.selectiveAgentTimeline]: z.boolean().optional(),
[CLIENT_CAPS.customModeIcons]: z.boolean().optional(),
[CLIENT_CAPS.terminalReflowableSnapshot]: z.boolean().optional(),
[CLIENT_CAPS.providerSubagents]: z.boolean().optional(),
[CLIENT_CAPS.projectUpdates]: z.boolean().optional(),
[CLIENT_CAPS.compactProviderSnapshots]: z.boolean().optional(),
[CLIENT_CAPS.providerSnapshotReferences]: z.boolean().optional(),
[CLIENT_CAPS.timelineReplacementInvalidation]: z.boolean().optional(),
[CLIENT_CAPS.timelineNotifications]: z.boolean().optional(),
[CLIENT_CAPS.browserHost]: BrowserAutomationHostCapabilitySchema.optional(),
})
.passthrough()
.optional(),
});
export const WSRecordingStateMessageSchema = z.object({
type: z.literal("recording_state"),
isRecording: z.boolean(),
});
// Wrapped session message
export const WSSessionInboundSchema = z.object({
type: z.literal("session"),
message: SessionInboundMessageSchema,
});
export const WSSessionOutboundSchema = z.object({
type: z.literal("session"),
message: SessionOutboundMessageSchema,
});
export const WSHelloRejectedMessageSchema = z.object({
type: z.literal("hello.rejected"),
reason: z.enum(["password_required", "incorrect_password", "incompatible_protocol"]),
accepts: z.array(z.literal("password")),
});
// Complete WebSocket message schemas
export const WSInboundMessageSchema = z.discriminatedUnion("type", [
WSPingMessageSchema,
WSHelloMessageSchema,
WSRecordingStateMessageSchema,
WSSessionInboundSchema,
]);
export const WSOutboundMessageSchema = z.discriminatedUnion("type", [
WSPongMessageSchema,
WSSessionOutboundSchema,
WSHelloRejectedMessageSchema,
]);
export type WSInboundMessage = z.infer<typeof WSInboundMessageSchema>;
export type WSOutboundMessage = z.infer<typeof WSOutboundMessageSchema>;
export type WSHelloMessage = z.infer<typeof WSHelloMessageSchema>;
// ============================================================================
// Helper functions for message conversion
// ============================================================================
/**
* Extract session message from WebSocket message
* Returns null if message should be handled at WS level only
*/
export function extractSessionMessage(wsMsg: WSInboundMessage): SessionInboundMessage | null {
if (wsMsg.type === "session") {
return wsMsg.message;
}
// Ping and recording_state are WS-level only
return null;
}
/**
* Wrap session message in WebSocket envelope
*/
export function wrapSessionMessage(sessionMsg: SessionOutboundMessage): WSOutboundMessage {
return {
type: "session",
message: sessionMsg,
};
}
export function parseServerInfoStatusPayload(payload: unknown): ServerInfoStatusPayload | null {
const parsed = ServerInfoStatusPayloadSchema.safeParse(payload);
if (!parsed.success) {
return null;
}
return parsed.data;
}