diff --git a/src/agents.ts b/src/agents.ts index 1725cf9..be587ff 100644 --- a/src/agents.ts +++ b/src/agents.ts @@ -289,6 +289,65 @@ export type RuntimesListResponse = z.infer; export const GatewayTypeSchema = z.enum(["telegram", "farcaster", "slack"]); export type GatewayType = z.infer; +/** How a framework receives events from a provider. */ +export const ChannelTransportModeSchema = z.enum([ + "polling", + "webhook", + "socket", + "http", + "relay", +]); +export type ChannelTransportMode = z.infer; + +/** Who owns the connector process and its credentials. */ +export const ChannelManagementModeSchema = z.enum([ + "runtime-native", + "perkos-managed-relay", + "user-managed-native", +]); +export type ChannelManagementMode = z.infer; + +export const ChannelLoginModeSchema = z.enum([ + "token", + "oauth", + "qr", + "embedded-signup", +]); +export type ChannelLoginMode = z.infer; + +export const RuntimeChannelFieldSchema = z.object({ + key: z.string().min(1), + label: z.string().min(1), + required: z.boolean(), + secret: z.boolean(), + input: z.enum(["text", "password", "url", "select", "boolean"]), + options: z.array(z.object({ value: z.string(), label: z.string() })).optional(), + help: z.string().optional(), +}); +export type RuntimeChannelField = z.infer; + +/** + * Versionable capability advertised by the PerkOS control plane for one + * framework/provider/transport combination. The UI renders this schema rather + * than assuming Hermes and OpenClaw accept the same configuration. + */ +export const RuntimeChannelCapabilitySchema = z.object({ + adapterId: z.string().min(1), + framework: AgentRuntimeSchema, + provider: GatewayTypeSchema, + label: z.string().min(1), + transportMode: ChannelTransportModeSchema, + managementMode: ChannelManagementModeSchema, + loginMode: ChannelLoginModeSchema, + requiresAlwaysOn: z.boolean(), + requiresPersistentStorage: z.boolean(), + supportsManagedRelay: z.boolean(), + fields: z.array(RuntimeChannelFieldSchema), +}); +export type RuntimeChannelCapability = z.infer< + typeof RuntimeChannelCapabilitySchema +>; + export const AgentGatewayStatusSchema = z.enum(["pending", "active", "error"]); export type AgentGatewayStatus = z.infer; @@ -301,6 +360,11 @@ export type AgentGatewayStatus = z.infer; */ export const AgentGatewayRecordSchema = z.object({ type: GatewayTypeSchema, + adapterId: z.string().optional(), + framework: AgentRuntimeSchema.optional(), + transportMode: ChannelTransportModeSchema.optional(), + managementMode: ChannelManagementModeSchema.optional(), + requiresAlwaysOn: z.boolean().optional(), enabled: z.boolean(), nonSecretConfig: z.record(z.string(), z.string()), secretsProvided: z.array(z.string()), @@ -308,6 +372,7 @@ export const AgentGatewayRecordSchema = z.object({ secretArns: z.record(z.string(), z.string()).optional(), status: AgentGatewayStatusSchema, statusMessage: z.string().optional(), + lastProbeAt: z.string().optional(), createdAt: z.string(), updatedAt: z.string(), }); @@ -326,6 +391,9 @@ export type AgentGateways = z.infer; */ export const GatewayUpsertInputSchema = z.object({ type: GatewayTypeSchema, + adapterId: z.string().optional(), + transportMode: ChannelTransportModeSchema.optional(), + managementMode: ChannelManagementModeSchema.optional(), enabled: z.boolean(), nonSecretConfig: z.record(z.string(), z.string()).optional(), secrets: z.record(z.string(), z.string()).optional(), diff --git a/tests/agents.test.ts b/tests/agents.test.ts index ecfe277..f4f64cc 100644 --- a/tests/agents.test.ts +++ b/tests/agents.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { AgentGatewayRecordSchema, + RuntimeChannelCapabilitySchema, AgentRuntimeSchema, AgentSchema, AgentStatusSchema, @@ -148,6 +149,34 @@ describe("GatewayTypeSchema + AgentGatewayRecordSchema", () => { }); }); +describe("RuntimeChannelCapabilitySchema", () => { + it("models a framework-specific Telegram transport", () => { + const out = RuntimeChannelCapabilitySchema.parse({ + adapterId: "hermes.telegram.polling.v1", + framework: "Hermes", + provider: "telegram", + label: "Telegram ยท Hermes polling", + transportMode: "polling", + managementMode: "runtime-native", + loginMode: "token", + requiresAlwaysOn: true, + requiresPersistentStorage: false, + supportsManagedRelay: false, + fields: [ + { + key: "botToken", + label: "Bot token", + required: true, + secret: true, + input: "password", + }, + ], + }); + expect(out.framework).toBe("Hermes"); + expect(out.transportMode).toBe("polling"); + }); +}); + describe("GatewayUpsertInputSchema", () => { it("parses a Farcaster enable with secrets stripped at the boundary", () => { const out = GatewayUpsertInputSchema.parse({