Add remote Paseo Agent configuration

This commit is contained in:
Mohamed Boudra
2026-07-02 08:38:27 +02:00
parent c9cd414571
commit 44afecc366
26 changed files with 2778 additions and 76 deletions
@@ -0,0 +1,80 @@
import { describe, expect, test } from "vitest";
import { SessionInboundMessageSchema, SessionOutboundMessageSchema } from "./messages.js";
describe("Paseo Agent config RPC schemas", () => {
test("parses provider config requests with providerType outside the message type field", () => {
const parsed = SessionInboundMessageSchema.parse({
type: "config.paseo_agent.set_provider.request",
requestId: "req-set-openrouter",
name: "openrouter-main",
providerType: "openrouter",
options: {
apiKey: "sk-test",
models: [{ id: "anthropic/claude-3.7-sonnet", reasoning: true }],
},
});
expect(parsed.type).toBe("config.paseo_agent.set_provider.request");
expect(parsed.providerType).toBe("openrouter");
});
test("parses redacted provider responses without raw secret fields", () => {
const parsed = SessionOutboundMessageSchema.parse({
type: "config.paseo_agent.get_providers.response",
payload: {
requestId: "req-get",
defaultModel: "openrouter-main/anthropic/claude-3.7-sonnet",
providers: [
{
name: "openrouter-main",
providerType: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
api: "openai-completions",
models: [{ id: "anthropic/claude-3.7-sonnet" }],
auth: { kind: "api_key", configured: true, source: "literal" },
available: true,
error: null,
},
],
error: null,
},
});
expect(parsed.payload.providers[0]?.providerType).toBe("openrouter");
expect(JSON.stringify(parsed)).not.toContain("apiKey");
});
test("preserves future OAuth credential fields on inbound schema parse", () => {
const parsed = SessionInboundMessageSchema.parse({
type: "config.paseo_agent.store_chatgpt_credential.request",
requestId: "req-oauth",
providerName: "chatgpt",
credential: {
type: "oauth",
access: "access-token",
refresh: "refresh-token",
expires: 123,
accountId: "acct_123",
futureField: { keep: true },
},
});
expect(parsed.credential.futureField).toEqual({ keep: true });
});
test("parses ChatGPT provider config separately from credential storage", () => {
const parsed = SessionInboundMessageSchema.parse({
type: "config.paseo_agent.set_provider.request",
requestId: "req-set-chatgpt",
name: "chatgpt",
providerType: "openai-codex",
options: {
models: [{ id: "gpt-5.3-codex", reasoning: true }],
},
});
expect(parsed.providerType).toBe("openai-codex");
expect(JSON.stringify(parsed)).not.toContain("access-token");
});
});
+194
View File
@@ -1922,6 +1922,112 @@ export const ListProviderFeaturesRequestMessageSchema = z.object({
requestId: z.string(),
});
const PaseoAgentProviderTypeSchema = z.enum([
"openrouter",
"openai",
"anthropic",
"opencode",
"openai-compatible",
"openai-codex",
"custom",
]);
const PaseoAgentProviderModelConfigSchema = z
.object({
id: z.string().min(1),
label: z.string().min(1).optional(),
api: z.string().min(1).optional(),
reasoning: z.boolean().optional(),
contextWindow: z.number().int().positive().optional(),
maxTokens: z.number().int().positive().optional(),
})
.strict();
const PaseoAgentSetProviderOptionsSchema = z
.object({
apiKey: z.string().min(1).optional(),
baseUrl: z.string().url().optional(),
api: z.string().min(1).optional(),
headers: z.record(z.string()).optional(),
authHeader: z.boolean().optional(),
models: z.array(PaseoAgentProviderModelConfigSchema).min(1),
})
.strict();
const PaseoAgentOAuthCredentialSchema = z
.object({
type: z.literal("oauth"),
access: z.string(),
refresh: z.string(),
expires: z.number(),
accountId: z.string().min(1).optional(),
})
.passthrough();
export const PaseoAgentProviderAuthStateSchema = z
.object({
kind: z.enum(["api_key", "oauth", "none"]),
configured: z.boolean(),
source: z
.enum(["literal", "env", "default_env", "command", "stored", "refresh_token"])
.optional(),
hint: z.string().optional(),
})
.strict();
export const RedactedPaseoAgentProviderConfigSchema = z
.object({
name: z.string().min(1),
providerType: z.enum([
"openrouter",
"openai",
"anthropic",
"opencode",
"openai-compatible",
"openai-codex",
"custom",
]),
baseUrl: z.string().optional(),
api: z.string().optional(),
models: z.array(PaseoAgentProviderModelConfigSchema),
auth: PaseoAgentProviderAuthStateSchema,
available: z.boolean(),
error: z.string().nullable().optional(),
})
.strict();
export const PaseoAgentGetProvidersRequestSchema = z.object({
type: z.literal("config.paseo_agent.get_providers.request"),
requestId: z.string(),
});
export const PaseoAgentSetProviderRequestSchema = z.object({
type: z.literal("config.paseo_agent.set_provider.request"),
requestId: z.string(),
name: z.string().trim().min(1),
providerType: PaseoAgentProviderTypeSchema,
options: PaseoAgentSetProviderOptionsSchema,
});
export const PaseoAgentRemoveProviderRequestSchema = z.object({
type: z.literal("config.paseo_agent.remove_provider.request"),
requestId: z.string(),
name: z.string().trim().min(1),
});
export const PaseoAgentSetDefaultModelRequestSchema = z.object({
type: z.literal("config.paseo_agent.set_default_model.request"),
requestId: z.string(),
model: z.string().trim().min(1).nullable(),
});
export const PaseoAgentStoreChatGptCredentialRequestSchema = z.object({
type: z.literal("config.paseo_agent.store_chatgpt_credential.request"),
requestId: z.string(),
providerName: z.string().trim().min(1),
credential: PaseoAgentOAuthCredentialSchema,
});
export const ListCommandsRequestSchema = z.object({
type: z.literal("list_commands_request"),
agentId: z.string(),
@@ -2073,6 +2179,11 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
ListProviderModelsRequestMessageSchema,
ListProviderModesRequestMessageSchema,
ListProviderFeaturesRequestMessageSchema,
PaseoAgentGetProvidersRequestSchema,
PaseoAgentSetProviderRequestSchema,
PaseoAgentRemoveProviderRequestSchema,
PaseoAgentSetDefaultModelRequestSchema,
PaseoAgentStoreChatGptCredentialRequestSchema,
ListAvailableProvidersRequestMessageSchema,
GetProvidersSnapshotRequestMessageSchema,
RefreshProvidersSnapshotRequestMessageSchema,
@@ -2364,6 +2475,8 @@ export const ServerInfoStatusPayloadSchema = z
daemonSelfUpdate: z.boolean().optional(),
// COMPAT(agentForkContext): added in v0.1.102, remove gate after 2026-12-28.
agentForkContext: z.boolean().optional(),
// COMPAT(paseoAgentConfig): added in v0.1.85, remove gate after 2026-11-30.
paseoAgentConfig: z.boolean().optional(),
})
.optional(),
})
@@ -3870,6 +3983,57 @@ export const ListProviderFeaturesResponseMessageSchema = z.object({
}),
});
export const PaseoAgentGetProvidersResponseSchema = z.object({
type: z.literal("config.paseo_agent.get_providers.response"),
payload: z.object({
requestId: z.string(),
defaultModel: z.string().nullable(),
providers: z.array(RedactedPaseoAgentProviderConfigSchema),
error: z.string().nullable(),
}),
});
export const PaseoAgentSetProviderResponseSchema = z.object({
type: z.literal("config.paseo_agent.set_provider.response"),
payload: z.object({
requestId: z.string(),
success: z.boolean(),
provider: RedactedPaseoAgentProviderConfigSchema.nullable(),
error: z.string().nullable(),
}),
});
export const PaseoAgentRemoveProviderResponseSchema = z.object({
type: z.literal("config.paseo_agent.remove_provider.response"),
payload: z.object({
requestId: z.string(),
success: z.boolean(),
removed: z.boolean(),
error: z.string().nullable(),
}),
});
export const PaseoAgentSetDefaultModelResponseSchema = z.object({
type: z.literal("config.paseo_agent.set_default_model.response"),
payload: z.object({
requestId: z.string(),
success: z.boolean(),
defaultModel: z.string().nullable(),
error: z.string().nullable(),
}),
});
export const PaseoAgentStoreChatGptCredentialResponseSchema = z.object({
type: z.literal("config.paseo_agent.store_chatgpt_credential.response"),
payload: z.object({
requestId: z.string(),
success: z.boolean(),
providerName: z.string(),
auth: PaseoAgentProviderAuthStateSchema,
error: z.string().nullable(),
}),
});
const ProviderAvailabilitySchema = z.object({
provider: AgentProviderSchema,
available: z.boolean(),
@@ -4265,6 +4429,11 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
ListProviderModelsResponseMessageSchema,
ListProviderModesResponseMessageSchema,
ListProviderFeaturesResponseMessageSchema,
PaseoAgentGetProvidersResponseSchema,
PaseoAgentSetProviderResponseSchema,
PaseoAgentRemoveProviderResponseSchema,
PaseoAgentSetDefaultModelResponseSchema,
PaseoAgentStoreChatGptCredentialResponseSchema,
ListAvailableProvidersResponseSchema,
GetProvidersSnapshotResponseMessageSchema,
ProvidersSnapshotUpdateMessageSchema,
@@ -4397,6 +4566,22 @@ export type ListProviderModesResponseMessage = z.infer<
export type ListProviderFeaturesResponseMessage = z.infer<
typeof ListProviderFeaturesResponseMessageSchema
>;
export type RedactedPaseoAgentProviderConfig = z.infer<
typeof RedactedPaseoAgentProviderConfigSchema
>;
export type PaseoAgentProviderAuthState = z.infer<typeof PaseoAgentProviderAuthStateSchema>;
export type PaseoAgentOAuthCredential = z.infer<typeof PaseoAgentOAuthCredentialSchema>;
export type PaseoAgentGetProvidersResponse = z.infer<typeof PaseoAgentGetProvidersResponseSchema>;
export type PaseoAgentSetProviderResponse = z.infer<typeof PaseoAgentSetProviderResponseSchema>;
export type PaseoAgentRemoveProviderResponse = z.infer<
typeof PaseoAgentRemoveProviderResponseSchema
>;
export type PaseoAgentSetDefaultModelResponse = z.infer<
typeof PaseoAgentSetDefaultModelResponseSchema
>;
export type PaseoAgentStoreChatGptCredentialResponse = z.infer<
typeof PaseoAgentStoreChatGptCredentialResponseSchema
>;
export type ListAvailableProvidersResponse = z.infer<typeof ListAvailableProvidersResponseSchema>;
export type DaemonGetStatusResponse = z.infer<typeof DaemonGetStatusResponseSchema>;
export type DaemonGetPairingOfferResponse = z.infer<typeof DaemonGetPairingOfferResponseSchema>;
@@ -4473,6 +4658,15 @@ export type ListProviderModesRequestMessage = z.infer<typeof ListProviderModesRe
export type ListProviderFeaturesRequestMessage = z.infer<
typeof ListProviderFeaturesRequestMessageSchema
>;
export type PaseoAgentGetProvidersRequest = z.infer<typeof PaseoAgentGetProvidersRequestSchema>;
export type PaseoAgentSetProviderRequest = z.infer<typeof PaseoAgentSetProviderRequestSchema>;
export type PaseoAgentRemoveProviderRequest = z.infer<typeof PaseoAgentRemoveProviderRequestSchema>;
export type PaseoAgentSetDefaultModelRequest = z.infer<
typeof PaseoAgentSetDefaultModelRequestSchema
>;
export type PaseoAgentStoreChatGptCredentialRequest = z.infer<
typeof PaseoAgentStoreChatGptCredentialRequestSchema
>;
export type ListAvailableProvidersRequestMessage = z.infer<
typeof ListAvailableProvidersRequestMessageSchema
>;