This commit is contained in:
Mavericks Studio 2026-09-13 00:41:16 -07:00 committed by GitHub
commit 790bf44ebf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 557 additions and 7 deletions

View File

@ -69,6 +69,11 @@ const hermesLocalCLIAdapter: CLIAdapterModule = {
formatStdoutEvent: printHermesStreamEvent,
};
const googleVertexCLIAdapter: CLIAdapterModule = {
type: "google_vertex",
formatStdoutEvent: printHermesStreamEvent,
};
const openclawGatewayCLIAdapter: CLIAdapterModule = {
type: "openclaw_gateway",
formatStdoutEvent: printOpenClawGatewayStreamEvent,
@ -87,6 +92,7 @@ const adaptersByType = new Map<string, CLIAdapterModule>(
kimiLocalCLIAdapter,
hermesGatewayCLIAdapter,
hermesLocalCLIAdapter,
googleVertexCLIAdapter,
openclawGatewayCLIAdapter,
processCLIAdapter,
httpCLIAdapter,

View File

@ -158,7 +158,7 @@ Invariant: every business record belongs to exactly one company.
- `status` enum: `active | paused | idle | running | error | pending_approval | terminated`
- `reports_to` uuid fk `agents.id` null
- `capabilities` text null
- `adapter_type` text; built-ins include `process`, `http`, `claude_local`, `codex_local`, `gemini_local`, `opencode_local`, `pi_local`, `cursor`, `hermes_local`, `hermes_gateway`, and `openclaw_gateway`
- `adapter_type` text; built-ins include `process`, `http`, `claude_local`, `codex_local`, `gemini_local`, `google_vertex`, `opencode_local`, `pi_local`, `cursor`, `hermes_local`, `hermes_gateway`, and `openclaw_gateway`
- `adapter_config` jsonb not null
- `runtime_config` jsonb not null default `{}`; contains Paperclip runtime policy such as heartbeat scheduling and debug settings
- `default_environment_id` uuid fk `environments.id` null

View File

@ -0,0 +1,32 @@
---
title: "Google Vertex AI"
description: "Run Paperclip agents on Gemini models through Google Cloud Vertex AI"
---
The `google_vertex` adapter runs the local Hermes agent harness with its provider fixed to Google Vertex AI. It uses Gemini models through Vertex's OpenAI-compatible endpoint while retaining Hermes tools, skills, and session continuity.
## Prerequisites
- Hermes Agent 0.21.2 or newer on the execution host
- A Google Cloud project with the Vertex AI API enabled and billing active
- An identity with the `roles/aiplatform.user` role
- Either a service-account JSON file or Google Application Default Credentials (ADC)
## Configure an agent
Choose **Google Vertex AI** when creating an agent, then set:
- **Model**: a Vertex model ID such as `google/gemini-3.8-flash`
- **Google Cloud project ID**: optional when embedded in the credential
- **Vertex region**: defaults to `global`; Gemini 3 preview models require it
- **Service-account JSON path**: optional absolute path on the execution host; leave blank to use ADC
Vertex uses OAuth2 rather than a static API key. Paperclip stores only the credential-file path and non-secret routing configuration. Hermes mints and refreshes short-lived access tokens at runtime.
For local development with ADC, configure Google Cloud on the same host that executes the agent:
```bash
gcloud auth application-default login
```
For servers, place the service-account JSON outside the repository, restrict its filesystem permissions, and configure its absolute path in the adapter. Do not paste service-account JSON or access tokens into agent configuration fields.

View File

@ -21,6 +21,7 @@ When a heartbeat fires, Paperclip:
| [Claude Code](/adapters/claude-local) | `claude_local` | Runs Claude Code CLI locally, with a native ACP engine when available |
| [Codex](/adapters/codex-local) | `codex_local` | Runs OpenAI Codex CLI locally, with a native ACP engine when available |
| [Gemini CLI](/adapters/gemini-local) | `gemini_local` | Runs Gemini CLI locally (experimental — adapter package exists, not yet in stable type enum) |
| [Google Vertex AI](/adapters/google-vertex) | `google_vertex` | Runs Hermes with Gemini on Vertex AI using OAuth2 service-account credentials or ADC |
| [Kimi Code CLI](/adapters/kimi-local) | `kimi_local` | Runs Kimi Code CLI locally through ACP, with explicitly selectable headless `-p` mode |
| OpenCode | `opencode_local` | Runs OpenCode CLI locally (multi-provider `provider/model`) |
| Cursor | `cursor` | Runs Cursor in background mode |

View File

@ -41,6 +41,7 @@ Built-in adapters:
- `pi_local`: runs an embedded Pi agent locally
- `hermes_local`: starts your local `hermes` CLI through `@paperclipai/hermes-paperclip-adapter`
- `hermes_gateway`: calls an already-running Hermes API server through `@paperclipai/hermes-paperclip-adapter/gateway`
- `google_vertex`: runs Hermes locally with its provider fixed to Google Vertex AI, using OAuth2 service-account credentials or ADC
- `openclaw_gateway`: connects to an OpenClaw gateway endpoint
- `process`: generic shell command adapter
- `http`: calls an external HTTP endpoint
@ -49,7 +50,7 @@ External plugin adapters (install via the adapter manager or API):
- `droid_local`: runs your local Factory Droid CLI (`@henkey/droid-paperclip-adapter`)
For local CLI adapters (`claude_local`, `codex_local`, `opencode_local`, `hermes_local`, `droid_local`), Paperclip assumes the CLI is already installed and authenticated on the host machine. For `hermes_gateway`, Paperclip assumes the Hermes API server is already running, reachable from the Paperclip server, and configured with an API key. The older `@paperclipai/adapter-hermes-gateway` npm package is only a deprecated compatibility shim; the adapter type remains `hermes_gateway`.
For local CLI adapters (`claude_local`, `codex_local`, `opencode_local`, `hermes_local`, `google_vertex`, `droid_local`), Paperclip assumes the CLI is already installed and authenticated on the host machine. `google_vertex` uses Hermes with Vertex OAuth2 credentials rather than a static model-provider key. For `hermes_gateway`, Paperclip assumes the Hermes API server is already running, reachable from the Paperclip server, and configured with an API key. The older `@paperclipai/adapter-hermes-gateway` npm package is only a deprecated compatibility shim; the adapter type remains `hermes_gateway`.
## 3.2 Runtime behavior

View File

@ -102,6 +102,7 @@
"adapters/overview",
"adapters/claude-local",
"adapters/codex-local",
"adapters/google-vertex",
"adapters/process",
"adapters/http",
"adapters/external-adapters",

View File

@ -43,6 +43,7 @@ export const LEGACY_SESSIONED_ADAPTER_TYPES = new Set([
"cursor",
"gemini_local",
"hermes_local",
"google_vertex",
"kimi_local",
"opencode_local",
"pi_local",
@ -94,6 +95,11 @@ export const ADAPTER_SESSION_MANAGEMENT: Record<string, AdapterSessionManagement
nativeContextManagement: "confirmed",
defaultSessionCompaction: ADAPTER_MANAGED_SESSION_POLICY,
},
google_vertex: {
supportsSessionResume: true,
nativeContextManagement: "confirmed",
defaultSessionCompaction: ADAPTER_MANAGED_SESSION_POLICY,
},
};
function isRecord(value: unknown): value is Record<string, unknown> {

View File

@ -4,10 +4,11 @@ A [Paperclip](https://paperclip.ing) adapter package that lets you run [Hermes A
Hermes Agent is a full-featured AI agent by [Nous Research](https://nousresearch.com) with 30+ native tools, persistent memory, session persistence, 80+ skills, MCP support, and multi-provider model access.
This package owns both built-in Hermes adapter types:
This package owns three built-in Hermes adapter types:
- `hermes_local` runs the local Hermes CLI as a child process. The package root exports remain compatible with the original local adapter.
- `hermes_gateway` calls an already-running Hermes API server over HTTP/SSE. Gateway entrypoints live under the `./gateway` export namespace.
- `google_vertex` runs the local Hermes CLI with the provider fixed to Google Vertex AI. Vertex entrypoints live under the `./vertex` export namespace.
Choose `hermes_local` when Paperclip and Hermes run on the same trusted host
and Paperclip should start `hermes chat` for each heartbeat. Choose

View File

@ -32,7 +32,10 @@
"./gateway/server": "./src/gateway/server/index.ts",
"./gateway/ui": "./src/gateway/ui/index.ts",
"./gateway/cli": "./src/gateway/cli/index.ts",
"./gateway/ui-parser": "./gateway-ui-parser.cjs"
"./gateway/ui-parser": "./gateway-ui-parser.cjs",
"./vertex": "./src/vertex/index.ts",
"./vertex/server": "./src/vertex/server/index.ts",
"./vertex/ui": "./src/vertex/ui/index.ts"
},
"paperclip": {
"adapterUiParser": "1.0.0"
@ -73,7 +76,19 @@
"types": "./dist/gateway/cli/index.d.ts",
"import": "./dist/gateway/cli/index.js"
},
"./gateway/ui-parser": "./gateway-ui-parser.cjs"
"./gateway/ui-parser": "./gateway-ui-parser.cjs",
"./vertex": {
"types": "./dist/vertex/index.d.ts",
"import": "./dist/vertex/index.js"
},
"./vertex/server": {
"types": "./dist/vertex/server/index.d.ts",
"import": "./dist/vertex/server/index.js"
},
"./vertex/ui": {
"types": "./dist/vertex/ui/index.d.ts",
"import": "./dist/vertex/ui/index.js"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@ -6,6 +6,7 @@ import { expect, test } from "vitest";
import {
createHermesGatewayServerAdapter,
createHermesLocalServerAdapter,
createGoogleVertexServerAdapter,
createServerAdapter,
hermesGatewayType,
} from "./index.js";
@ -42,6 +43,21 @@ test("root package export keeps explicit local and gateway adapter factories", (
expect(gatewayAdapter.supportsInstructionsBundle).toBe(false);
});
test("root package export exposes the Google Vertex AI adapter factory", () => {
const adapter = createGoogleVertexServerAdapter();
expect(adapter.type).toBe("google_vertex");
expect(adapter.models?.[0]).toEqual({
id: "google/gemini-3.8-flash",
label: "Gemini 3.8 Flash",
});
expect(adapter.supportsLocalAgentJwt).toBe(true);
expect(adapter.supportsInstructionsBundle).toBe(true);
expect(typeof adapter.execute).toBe("function");
expect(typeof adapter.testEnvironment).toBe("function");
expect(typeof adapter.getConfigSchema).toBe("function");
});
test("gateway subpath export exposes the Hermes Gateway adapter entrypoint", () => {
const adapter = createGatewayServerAdapterFromSubpath();

View File

@ -36,6 +36,13 @@ export {
models as hermesGatewayModels,
type as hermesGatewayType,
} from "./gateway/index.js";
export {
createServerAdapter as createGoogleVertexServerAdapter,
agentConfigurationDoc as googleVertexAgentConfigurationDoc,
label as googleVertexLabel,
models as googleVertexModels,
type as googleVertexType,
} from "./vertex/index.js";
/**
* Models available through Hermes Agent.

View File

@ -45,6 +45,7 @@ export const VALID_PROVIDERS = [
"minimax",
"minimax-cn",
"kilocode",
"vertex",
] as const;
/**

View File

@ -0,0 +1,66 @@
import type { ServerAdapterModule } from "@paperclipai/adapter-utils";
import { resolveHermesCommand } from "../server/execute.js";
import { listHermesSkills, syncHermesSkills } from "../server/skills.js";
import { sessionCodec } from "../server/index.js";
import {
executeGoogleVertex,
getGoogleVertexConfigSchema,
testGoogleVertexEnvironment,
} from "./server/index.js";
import {
GOOGLE_VERTEX_ADAPTER_LABEL,
GOOGLE_VERTEX_ADAPTER_TYPE,
GOOGLE_VERTEX_MODELS,
} from "./shared/constants.js";
export const type = GOOGLE_VERTEX_ADAPTER_TYPE;
export const label = GOOGLE_VERTEX_ADAPTER_LABEL;
export const models = [...GOOGLE_VERTEX_MODELS];
export const agentConfigurationDoc = `# Google Vertex AI Configuration
This adapter runs Hermes Agent with the Google Vertex AI provider fixed for every heartbeat.
It uses Gemini models through Vertex's OpenAI-compatible endpoint.
## Authentication
Vertex uses OAuth2, not a static API key. Configure one of:
- \`credentialsPath\`: absolute path to a service-account JSON file on the execution host.
- Application Default Credentials (ADC): leave \`credentialsPath\` blank and configure ADC on the host.
Set \`projectId\` when it cannot be inferred from the credential. The default region is \`global\`, which is required by Gemini 3 preview models. Hermes mints and refreshes short-lived OAuth2 tokens at runtime.
`;
export function createServerAdapter(): ServerAdapterModule {
return {
type,
execute: executeGoogleVertex,
testEnvironment: testGoogleVertexEnvironment,
sessionCodec,
sessionManagement: {
supportsSessionResume: true,
nativeContextManagement: "confirmed",
defaultSessionCompaction: {
enabled: true,
maxSessionRuns: 0,
maxRawInputTokens: 0,
maxSessionAgeHours: 0,
},
},
listSkills: listHermesSkills,
syncSkills: syncHermesSkills,
models,
supportsLocalAgentJwt: true,
supportsInstructionsBundle: true,
instructionsPathKey: "instructionsFilePath",
requiresMaterializedRuntimeSkills: false,
getRuntimeCommandSpec: (config) => {
const command = resolveHermesCommand(config);
return { command, detectCommand: command, installCommand: null };
},
agentConfigurationDoc,
getConfigSchema: getGoogleVertexConfigSchema,
};
}

View File

@ -0,0 +1,61 @@
import type { AdapterConfigSchema } from "@paperclipai/adapter-utils";
import { DEFAULT_GRACE_SEC, DEFAULT_TIMEOUT_SEC } from "../../shared/constants.js";
import { DEFAULT_GOOGLE_VERTEX_REGION } from "../shared/constants.js";
export function getGoogleVertexConfigSchema(): AdapterConfigSchema {
return {
fields: [
{
key: "projectId",
label: "Google Cloud project ID",
type: "text",
hint: "Optional when the credential contains a project ID; otherwise required.",
},
{
key: "region",
label: "Vertex region",
type: "text",
default: DEFAULT_GOOGLE_VERTEX_REGION,
hint: "Use global for Gemini 3 preview models; pin a region only when your deployment requires it.",
},
{
key: "credentialsPath",
label: "Service-account JSON path",
type: "text",
hint: "Optional absolute path on the execution host. Leave blank to use Application Default Credentials.",
meta: { path: true },
},
{
key: "timeoutSec",
label: "Timeout seconds",
type: "number",
default: DEFAULT_TIMEOUT_SEC,
},
{
key: "graceSec",
label: "Grace seconds",
type: "number",
default: DEFAULT_GRACE_SEC,
},
{
key: "maxTurnsPerRun",
label: "Max turns per run",
type: "number",
hint: "Optional Hermes tool-calling iteration limit.",
},
{
key: "persistSession",
label: "Persist session",
type: "toggle",
default: true,
},
{
key: "toolsets",
label: "Toolsets",
type: "text",
hint: "Optional comma-separated Hermes toolsets, such as terminal,file,web.",
},
],
};
}

View File

@ -0,0 +1,39 @@
import { describe, expect, it } from "vitest";
import {
DEFAULT_GOOGLE_VERTEX_MODEL,
DEFAULT_GOOGLE_VERTEX_REGION,
GOOGLE_VERTEX_PROVIDER,
} from "../shared/constants.js";
import { buildGoogleVertexRuntimeConfig } from "./config.js";
describe("buildGoogleVertexRuntimeConfig", () => {
it("fixes the provider and supplies safe Vertex defaults", () => {
expect(buildGoogleVertexRuntimeConfig({ provider: "openrouter" })).toMatchObject({
provider: GOOGLE_VERTEX_PROVIDER,
model: DEFAULT_GOOGLE_VERTEX_MODEL,
env: { VERTEX_REGION: DEFAULT_GOOGLE_VERTEX_REGION },
});
});
it("maps routing fields and the service-account pointer into Hermes env", () => {
const config = buildGoogleVertexRuntimeConfig({
model: " google/gemini-3.1-pro-preview ",
projectId: " project-123 ",
region: " us-central1 ",
credentialsPath: " /secure/vertex.json ",
env: { EXISTING: "value" },
});
expect(config).toMatchObject({
provider: "vertex",
model: "google/gemini-3.1-pro-preview",
env: {
EXISTING: "value",
VERTEX_PROJECT_ID: "project-123",
VERTEX_REGION: "us-central1",
VERTEX_CREDENTIALS_PATH: "/secure/vertex.json",
},
});
});
});

View File

@ -0,0 +1,51 @@
import type { AdapterExecutionContext, AdapterEnvironmentTestContext } from "@paperclipai/adapter-utils";
import {
DEFAULT_GOOGLE_VERTEX_MODEL,
DEFAULT_GOOGLE_VERTEX_REGION,
GOOGLE_VERTEX_PROVIDER,
} from "../shared/constants.js";
function nonEmptyString(value: unknown): string | undefined {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}
export function buildGoogleVertexRuntimeConfig(
config: Record<string, unknown>,
): Record<string, unknown> {
const env =
config.env && typeof config.env === "object" && !Array.isArray(config.env)
? { ...(config.env as Record<string, unknown>) }
: {};
const projectId = nonEmptyString(config.projectId);
const region = nonEmptyString(config.region) ?? DEFAULT_GOOGLE_VERTEX_REGION;
const credentialsPath = nonEmptyString(config.credentialsPath);
if (projectId) env.VERTEX_PROJECT_ID = projectId;
if (region) env.VERTEX_REGION = region;
if (credentialsPath) env.VERTEX_CREDENTIALS_PATH = credentialsPath;
return {
...config,
env,
provider: GOOGLE_VERTEX_PROVIDER,
model: nonEmptyString(config.model) ?? DEFAULT_GOOGLE_VERTEX_MODEL,
};
}
export function withGoogleVertexExecutionConfig(
ctx: AdapterExecutionContext,
): AdapterExecutionContext {
const config = buildGoogleVertexRuntimeConfig(ctx.config ?? {});
return {
...ctx,
config,
agent: { ...ctx.agent, adapterConfig: config },
};
}
export function withGoogleVertexTestConfig(
ctx: AdapterEnvironmentTestContext,
): AdapterEnvironmentTestContext {
return { ...ctx, config: buildGoogleVertexRuntimeConfig(ctx.config ?? {}) };
}

View File

@ -0,0 +1,75 @@
import type {
AdapterEnvironmentCheck,
AdapterEnvironmentTestContext,
AdapterEnvironmentTestResult,
AdapterExecutionContext,
} from "@paperclipai/adapter-utils";
import { access } from "node:fs/promises";
import { execute as executeHermes } from "../../server/execute.js";
import { testEnvironment as testHermesEnvironment } from "../../server/test.js";
import { GOOGLE_VERTEX_ADAPTER_TYPE } from "../shared/constants.js";
import {
buildGoogleVertexRuntimeConfig,
withGoogleVertexExecutionConfig,
withGoogleVertexTestConfig,
} from "./config.js";
export { getGoogleVertexConfigSchema } from "./config-schema.js";
export { buildGoogleVertexRuntimeConfig } from "./config.js";
export async function executeGoogleVertex(ctx: AdapterExecutionContext) {
return executeHermes(withGoogleVertexExecutionConfig(ctx));
}
async function vertexCredentialCheck(
config: Record<string, unknown>,
): Promise<AdapterEnvironmentCheck> {
const env = (config.env ?? {}) as Record<string, unknown>;
const credentialPath =
typeof env.VERTEX_CREDENTIALS_PATH === "string" && env.VERTEX_CREDENTIALS_PATH.length > 0
? env.VERTEX_CREDENTIALS_PATH
: typeof env.GOOGLE_APPLICATION_CREDENTIALS === "string" && env.GOOGLE_APPLICATION_CREDENTIALS.length > 0
? env.GOOGLE_APPLICATION_CREDENTIALS
: null;
if (credentialPath) {
try {
await access(credentialPath);
} catch {
return {
level: "error",
message: "Vertex service-account credential file is not readable",
hint: "Check credentialsPath on the selected execution host, or leave it blank to use Application Default Credentials.",
code: "google_vertex_service_account_unreadable",
};
}
return {
level: "info",
message: "Vertex service-account credential path is configured",
hint: "Hermes will mint and refresh OAuth2 access tokens at runtime; tokens are never stored in Paperclip config.",
code: "google_vertex_service_account_configured",
};
}
return {
level: "info",
message: "Vertex will use Google Application Default Credentials",
hint: "Ensure ADC is available on the selected execution host and can access the configured Google Cloud project.",
code: "google_vertex_adc",
};
}
export async function testGoogleVertexEnvironment(
ctx: AdapterEnvironmentTestContext,
): Promise<AdapterEnvironmentTestResult> {
const vertexCtx = withGoogleVertexTestConfig(ctx);
const result = await testHermesEnvironment(vertexCtx);
const checks = result.checks.filter((check) => check.code !== "hermes_no_api_keys");
checks.push(await vertexCredentialCheck(buildGoogleVertexRuntimeConfig(ctx.config ?? {})));
const hasErrors = checks.some((check) => check.level === "error");
const hasWarnings = checks.some((check) => check.level === "warn");
return {
...result,
adapterType: GOOGLE_VERTEX_ADAPTER_TYPE,
status: hasErrors ? "fail" : hasWarnings ? "warn" : "pass",
checks,
};
}

View File

@ -0,0 +1,19 @@
export const GOOGLE_VERTEX_ADAPTER_TYPE = "google_vertex";
export const GOOGLE_VERTEX_ADAPTER_LABEL = "Google Vertex AI";
export const GOOGLE_VERTEX_PROVIDER = "vertex";
export const DEFAULT_GOOGLE_VERTEX_MODEL = "google/gemini-3.8-flash";
export const DEFAULT_GOOGLE_VERTEX_REGION = "global";
/** Vertex's OpenAI-compatible endpoint has no model-list route. */
export const GOOGLE_VERTEX_MODELS = [
{ id: "google/gemini-3.8-flash", label: "Gemini 3.8 Flash" },
{ id: "google/gemini-3.7-flash", label: "Gemini 3.7 Flash" },
{ id: "google/gemini-3.1-pro-preview", label: "Gemini 3.1 Pro Preview" },
{ id: "google/gemini-3-pro-preview", label: "Gemini 3 Pro Preview" },
{ id: "google/gemini-3.6-flash", label: "Gemini 3.6 Flash" },
{ id: "google/gemini-3.5-flash", label: "Gemini 3.5 Flash" },
{ id: "google/gemini-3.5-flash-lite", label: "Gemini 3.5 Flash Lite" },
{ id: "google/gemini-3-flash-preview", label: "Gemini 3 Flash Preview" },
{ id: "google/gemini-3.1-flash-lite-preview", label: "Gemini 3.1 Flash Lite Preview" },
{ id: "google/gemini-3.1-flash-lite", label: "Gemini 3.1 Flash Lite" },
] as const;

View File

@ -0,0 +1,65 @@
import { describe, expect, it } from "vitest";
import type { CreateConfigValues } from "@paperclipai/adapter-utils";
import { DEFAULT_GOOGLE_VERTEX_MODEL } from "../shared/constants.js";
import { buildGoogleVertexConfig } from "./build-config.js";
function values(overrides: Partial<CreateConfigValues> = {}): CreateConfigValues {
return {
adapterType: "google_vertex",
cwd: "",
promptTemplate: "",
model: "",
thinkingEffort: "",
chrome: false,
dangerouslySkipPermissions: false,
search: false,
fastMode: false,
dangerouslyBypassSandbox: false,
command: "",
args: "",
extraArgs: "",
envVars: "",
envBindings: {},
url: "",
bootstrapPrompt: "",
payloadTemplateJson: "",
maxTurnsPerRun: 0,
heartbeatEnabled: true,
intervalSec: 3600,
...overrides,
};
}
describe("buildGoogleVertexConfig", () => {
it("creates a Vertex-only config with the default model and region", () => {
expect(buildGoogleVertexConfig(values())).toMatchObject({
provider: "vertex",
model: DEFAULT_GOOGLE_VERTEX_MODEL,
region: "global",
persistSession: true,
});
});
it("keeps schema routing fields while preventing a provider override", () => {
expect(
buildGoogleVertexConfig(
values({
model: "google/gemini-3.1-pro-preview",
adapterSchemaValues: {
provider: "openrouter",
projectId: "project-123",
region: "us-central1",
credentialsPath: "/secure/vertex.json",
},
}),
),
).toMatchObject({
provider: "vertex",
model: "google/gemini-3.1-pro-preview",
projectId: "project-123",
region: "us-central1",
credentialsPath: "/secure/vertex.json",
});
});
});

View File

@ -0,0 +1,23 @@
import type { CreateConfigValues } from "@paperclipai/adapter-utils";
import { buildHermesConfig } from "../../ui/build-config.js";
import {
DEFAULT_GOOGLE_VERTEX_MODEL,
DEFAULT_GOOGLE_VERTEX_REGION,
GOOGLE_VERTEX_PROVIDER,
} from "../shared/constants.js";
export function buildGoogleVertexConfig(values: CreateConfigValues): Record<string, unknown> {
return {
...buildHermesConfig({
...values,
model: values.model.trim() || DEFAULT_GOOGLE_VERTEX_MODEL,
}),
...(values.adapterSchemaValues ?? {}),
provider: GOOGLE_VERTEX_PROVIDER,
region:
typeof values.adapterSchemaValues?.region === "string" && values.adapterSchemaValues.region.trim()
? values.adapterSchemaValues.region.trim()
: DEFAULT_GOOGLE_VERTEX_REGION,
};
}

View File

@ -0,0 +1,2 @@
export { buildGoogleVertexConfig } from "./build-config.js";
export { parseHermesStdoutLine as parseGoogleVertexStdoutLine } from "../../ui/parse-stdout.js";

View File

@ -32,6 +32,7 @@ export const AGENT_ADAPTER_TYPES = [
"paperclip_runner",
"cursor_cloud",
"gemini_local",
"google_vertex",
"grok_local",
"hermes_gateway",
"hermes_local",

View File

@ -189,6 +189,20 @@ describe("server adapter registry", () => {
expect(requireServerAdapter("hermes_gateway")).toBe(builtInGateway);
});
it("ships Google Vertex AI as a Hermes-backed built-in adapter", () => {
const adapter = findServerAdapter("google_vertex");
expect(adapter).not.toBeNull();
expect(adapter?.supportsLocalAgentJwt).toBe(true);
expect(adapter?.supportsInstructionsBundle).toBe(true);
expect(adapter?.requiresMaterializedRuntimeSkills).toBe(false);
expect(adapter?.getConfigSchema).toBeTypeOf("function");
expect(adapter?.models?.[0]).toEqual({
id: "google/gemini-3.8-flash",
label: "Gemini 3.8 Flash",
});
});
it("exposes capability flags from registered adapters", () => {
const adapterWithCaps: ServerAdapterModule = {
type: "external_test",

View File

@ -9,6 +9,7 @@ export const BUILTIN_ADAPTER_TYPES = new Set([
"cursor_cloud",
"cursor",
"gemini_local",
"google_vertex",
"grok_local",
"hermes_gateway",
"hermes_local",

View File

@ -62,6 +62,7 @@ describe("built-in runtime connection tool delivery", () => {
["grok_local", "environment"],
["hermes_gateway", "invocation_context"],
["hermes_local", "environment"],
["google_vertex", "environment"],
["kimi_local", "environment"],
["openclaw_gateway", "invocation_context"],
["opencode_local", "environment"],

View File

@ -98,6 +98,7 @@ import {
import {
createHermesGatewayServerAdapter,
createHermesLocalServerAdapter,
createGoogleVertexServerAdapter,
} from "@paperclipai/hermes-paperclip-adapter";
import {
execute as openCodeExecute,
@ -788,6 +789,11 @@ const hermesLocalAdapter: ServerAdapterModule = {
runtimeToolDelivery: "environment",
};
const googleVertexAdapter: ServerAdapterModule = {
...createGoogleVertexServerAdapter(),
runtimeToolDelivery: "environment",
};
const openclawGatewayAdapter: ServerAdapterModule = {
type: "openclaw_gateway",
runtimeToolDelivery: "invocation_context",
@ -865,6 +871,7 @@ function registerBuiltInAdapters() {
kimiLocalAdapter,
hermesGatewayAdapter,
hermesLocalAdapter,
googleVertexAdapter,
openclawGatewayAdapter,
processAdapter,
httpAdapter,

View File

@ -9,6 +9,7 @@ const SESSIONED_LOCAL_ADAPTERS = new Set([
"cursor",
"gemini_local",
"hermes_local",
"google_vertex",
"kimi_local",
"opencode_local",
"pi_local",

View File

@ -491,6 +491,7 @@ export function agentRoutes(
codex_local: "instructionsFilePath",
droid_local: "instructionsFilePath",
gemini_local: "instructionsFilePath",
google_vertex: "instructionsFilePath",
kimi_local: "instructionsFilePath",
opencode_local: "instructionsFilePath",
cursor: "instructionsFilePath",

View File

@ -824,6 +824,7 @@ const GIT_SENSITIVE_LOCAL_ADAPTER_TYPES = new Set([
"gemini_local",
"grok_local",
"hermes_local",
"google_vertex",
"kimi_local",
"opencode_local",
"pi_local",
@ -1213,6 +1214,7 @@ const SESSIONED_LOCAL_ADAPTERS = new Set([
"cursor",
"gemini_local",
"hermes_local",
"google_vertex",
"kimi_local",
"opencode_local",
"pi_local",
@ -8871,12 +8873,12 @@ export function resolveSkillTestRunCompletionForHeartbeatOutcome(
return null;
}
const HERMES_ADAPTER_TYPE = "hermes_local";
const HERMES_ADAPTER_TYPES = new Set(["hermes_local", "google_vertex"]);
const HERMES_SESSION_ID_REGEX =
/^(?:\d{8}_\d{6}_[A-Za-z0-9_-]{4,}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
function requiresCanonicalSessionIds(adapterType: string | null | undefined) {
return adapterType === HERMES_ADAPTER_TYPE;
return typeof adapterType === "string" && HERMES_ADAPTER_TYPES.has(adapterType);
}
function isCanonicalSessionIdForAdapter(

View File

@ -9,6 +9,7 @@ describe("adapter display registry", () => {
expect(getAdapterLabel("acpx_local")).toBe("ACPX (retired)");
expect(getAdapterLabel("cursor")).toBe("Cursor");
expect(getAdapterLabel("gemini_local")).toBe("Gemini CLI");
expect(getAdapterLabel("google_vertex")).toBe("Google Vertex AI");
expect(getAdapterLabel("grok_local")).toBe("Grok Build");
expect(getAdapterLabel("kimi_local")).toBe("Kimi Code");
expect(getAdapterLabel("hermes_local")).toBe("Hermes");
@ -22,6 +23,7 @@ describe("adapter display registry", () => {
acpx_local: "ACPX (retired)",
cursor: "Cursor",
gemini_local: "Gemini CLI",
google_vertex: "Google Vertex AI",
grok_local: "Grok Build",
kimi_local: "Kimi Code",
hermes_local: "Hermes",

View File

@ -91,6 +91,11 @@ const adapterDisplayMap: Record<string, AdapterDisplayInfo> = {
description: "Gemini CLI harness",
icon: Gem,
},
google_vertex: {
label: "Google Vertex AI",
description: "Gemini on Google Cloud via Hermes",
icon: Gem,
},
grok_local: {
label: "Grok Build",
description: "Grok Build harness",

View File

@ -0,0 +1,14 @@
import type { UIAdapterModule } from "../types";
import {
buildGoogleVertexConfig,
parseGoogleVertexStdoutLine,
} from "@paperclipai/hermes-paperclip-adapter/vertex/ui";
import { SchemaConfigFields } from "../schema-config-fields";
export const googleVertexUIAdapter: UIAdapterModule = {
type: "google_vertex",
label: "Google Vertex AI",
parseStdoutLine: parseGoogleVertexStdoutLine,
ConfigFields: SchemaConfigFields,
buildAdapterConfig: buildGoogleVertexConfig,
};

View File

@ -77,4 +77,11 @@ describe("ui adapter registry", () => {
expect(getUIAdapter(type)).toBe(builtin);
}
});
it("registers the Google Vertex AI UI adapter", () => {
const adapter = getUIAdapter("google_vertex");
expect(adapter.label).toBe("Google Vertex AI");
expect(adapter.ConfigFields).toBe(SchemaConfigFields);
});
});

View File

@ -5,6 +5,7 @@ import { paperclipRunnerUIAdapter } from "./paperclip-runner";
import { cursorCloudUIAdapter } from "./cursor-cloud";
import { cursorLocalUIAdapter } from "./cursor";
import { geminiLocalUIAdapter } from "./gemini-local";
import { googleVertexUIAdapter } from "./google-vertex";
import { grokLocalUIAdapter } from "./grok-local";
import { kimiLocalUIAdapter } from "./kimi-local";
import { hermesGatewayUIAdapter } from "./hermes-gateway";
@ -59,6 +60,7 @@ function registerBuiltInUIAdapters() {
paperclipRunnerUIAdapter,
cursorCloudUIAdapter,
geminiLocalUIAdapter,
googleVertexUIAdapter,
grokLocalUIAdapter,
kimiLocalUIAdapter,
hermesGatewayUIAdapter,

View File

@ -25,6 +25,7 @@ const KNOWN_DEFAULTS: Record<string, AdapterCapabilities> = {
paperclip_runner: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: false, requiresMaterializedRuntimeSkills: false, supportsAcp: false },
cursor: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: true, supportsAcp: false },
gemini_local: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: true, supportsAcp: true },
google_vertex: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: false, supportsAcp: false },
grok_local: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: true, supportsAcp: false, login: { panelMode: "displayed_code", timeoutPolicy: "caller_bounded" } },
kimi_local: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: true, supportsAcp: true },
opencode_local: { supportsInstructionsBundle: true, supportsSkills: true, supportsLocalAgentJwt: true, requiresMaterializedRuntimeSkills: true, supportsAcp: false },

View File

@ -1091,6 +1091,7 @@ function OnboardingWizardInner({
adapterType === "claude_local" ||
adapterType === "codex_local" ||
adapterType === "gemini_local" ||
adapterType === "google_vertex" ||
adapterType === "kimi_local" ||
adapterType === "opencode_local" ||
adapterType === "pi_local" ||

View File

@ -27,6 +27,7 @@ const brandMarks: Record<string, { src: string; dark?: string }> = {
claude_local: { src: "/brands/claude-color.svg" },
codex_local: { src: "/brands/codex-color.svg" },
gemini_local: { src: "/brands/adapters/gemini-color.svg" },
google_vertex: { src: "/brands/adapters/gemini-color.svg" },
kimi_local: {
src: "/brands/adapters/kimi-color-light.svg",
dark: "/brands/adapters/kimi-color.svg",

View File

@ -28,6 +28,7 @@ const ENABLED_INVITE_ADAPTERS = new Set([
"claude_local",
"codex_local",
"gemini_local",
"google_vertex",
"kimi_local",
"opencode_local",
"pi_local",