opencode-google-auth 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.mjs","names":[],"sources":["../src/lib/services/config.ts","../src/lib/services/opencode.ts","../src/lib/logger.ts","../src/lib/services/oauth.ts","../src/lib/services/session.ts","../src/lib/runtime.ts","../src/transform/request.ts","../src/transform/response.ts","../src/transform/stream.ts","../src/main.ts"],"sourcesContent":["import type { GoogleGenerativeAIProviderOptions } from \"@ai-sdk/google\"\nimport { Context, Effect, pipe } from \"effect\"\nimport type {\n ModelsDev,\n OpenCodeModel,\n OpenCodeProvider,\n Provider,\n} from \"../../types\"\n\nexport interface WrappedBody {\n readonly project: string\n readonly request: unknown\n readonly model: string\n}\n\nexport interface RequestContext {\n readonly body: WrappedBody\n readonly headers: Headers\n readonly url: URL\n}\n\nexport interface ProviderConfigShape {\n readonly SERVICE_NAME: string\n readonly DISPLAY_NAME: string\n readonly ENDPOINTS: readonly string[]\n readonly HEADERS: Readonly<Record<string, string>>\n readonly SCOPES: readonly string[]\n readonly CLIENT_ID: string\n readonly CLIENT_SECRET: string\n readonly getConfig: (modelsDev: ModelsDev) => OpenCodeProvider\n readonly transformRequest?: (context: RequestContext) => Effect.Effect<{\n body: Record<string, unknown>\n headers: Headers\n url: URL\n }>\n}\n\nexport class ProviderConfig extends Context.Tag(\"ProviderConfig\")<\n ProviderConfig,\n ProviderConfigShape\n>() {}\n\nexport const CODE_ASSIST_VERSION = \"v1internal\"\n\nexport const CLIENT_METADATA = {\n ideType: \"IDE_UNSPECIFIED\",\n platform: \"PLATFORM_UNSPECIFIED\",\n pluginType: \"GEMINI\",\n} as const\n\nexport const GEMINI_CLI_MODELS = [\n \"gemini-2.5-pro\",\n \"gemini-2.5-flash\",\n \"gemini-2.5-flash-lite\",\n \"gemini-3-pro-preview\",\n \"gemini-3-flash-preview\",\n] as const\n\nexport const ANTIGRAVITY_MODELS = [\n \"gemini-3-flash\",\n \"gemini-3-pro-low\",\n \"gemini-3-pro-high\",\n \"claude-sonnet-4-5\",\n \"claude-sonnet-4-5-thinking\",\n \"claude-opus-4-5-thinking\",\n] as const\n\nexport const geminiCliConfig = (): ProviderConfigShape => ({\n SERVICE_NAME: \"gemini-cli\",\n DISPLAY_NAME: \"Gemini CLI\",\n CLIENT_ID:\n \"681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com\",\n CLIENT_SECRET: \"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\",\n SCOPES: [\n \"https://www.googleapis.com/auth/cloud-platform\",\n \"https://www.googleapis.com/auth/userinfo.email\",\n \"https://www.googleapis.com/auth/userinfo.profile\",\n ],\n ENDPOINTS: [\"https://cloudcode-pa.googleapis.com\"],\n HEADERS: {\n \"User-Agent\": \"google-api-nodejs-client/9.15.1\",\n \"X-Goog-Api-Client\": \"gl-node/22.17.0\",\n \"Client-Metadata\":\n \"ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI\",\n },\n getConfig: (modelsDev) => {\n const provider = modelsDev.google as Provider\n const filteredModels = pipe(\n provider.models,\n (models) => Object.entries(models),\n (entries) =>\n entries.filter(([key]) =>\n (GEMINI_CLI_MODELS as readonly string[]).includes(key),\n ),\n (filtered) => Object.fromEntries(filtered),\n )\n\n return {\n ...provider,\n id: geminiCliConfig().SERVICE_NAME,\n name: geminiCliConfig().DISPLAY_NAME,\n api: geminiCliConfig().ENDPOINTS.at(0) as string,\n models: filteredModels as Record<string, OpenCodeModel>,\n }\n },\n})\n\nexport const antigravityConfig = (): ProviderConfigShape => ({\n SERVICE_NAME: \"antigravity\",\n DISPLAY_NAME: \"Antigravity\",\n CLIENT_ID:\n \"1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com\",\n CLIENT_SECRET: \"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\",\n SCOPES: [\n \"https://www.googleapis.com/auth/cloud-platform\",\n \"https://www.googleapis.com/auth/userinfo.email\",\n \"https://www.googleapis.com/auth/userinfo.profile\",\n \"https://www.googleapis.com/auth/cclog\",\n \"https://www.googleapis.com/auth/experimentsandconfigs\",\n ],\n ENDPOINTS: [\n \"https://daily-cloudcode-pa.sandbox.googleapis.com\",\n \"https://autopush-cloudcode-pa.sandbox.googleapis.com\",\n \"https://cloudcode-pa.googleapis.com\",\n ],\n HEADERS: {\n \"User-Agent\": \"antigravity/1.11.5 windows/amd64\",\n \"X-Goog-Api-Client\": \"google-cloud-sdk vscode_cloudshelleditor/0.1\",\n \"Client-Metadata\":\n '{\"ideType\":\"IDE_UNSPECIFIED\",\"platform\":\"PLATFORM_UNSPECIFIED\",\"pluginType\":\"GEMINI\"}',\n },\n getConfig: (modelsDev) => {\n const googleProvider = modelsDev.google as Provider\n const googleVertextProvider = modelsDev[\n \"google-vertex-anthropic\"\n ] as Provider\n\n const geminiFlash = googleProvider.models[\n \"gemini-3-flash-preview\"\n ] as OpenCodeModel\n const geminiPro = googleProvider.models[\n \"gemini-3-pro-preview\"\n ] as OpenCodeModel\n const claudeSonnet = googleVertextProvider.models[\n \"claude-sonnet-4-5@20250929\"\n ] as OpenCodeModel\n const claudeOpus = googleVertextProvider.models[\n \"claude-opus-4-5@20251101\"\n ] as OpenCodeModel\n\n const models: Record<string, OpenCodeModel> = {\n \"gemini-3-flash\": {\n ...geminiFlash,\n id: \"gemini-3-flash\",\n },\n \"gemini-3-pro-low\": {\n ...geminiPro,\n id: \"gemini-3-pro-low\",\n name: \"Gemini 3 Pro (Low)\",\n temperature: false,\n options: {\n thinkingConfig: {\n thinkingLevel: \"low\",\n },\n } satisfies GoogleGenerativeAIProviderOptions,\n },\n \"gemini-3-pro-high\": {\n ...geminiPro,\n id: \"gemini-3-pro-high\",\n name: \"Gemini 3 Pro (High)\",\n temperature: false,\n options: {\n thinkingConfig: {\n thinkingLevel: \"high\",\n },\n } satisfies GoogleGenerativeAIProviderOptions,\n },\n \"claude-sonnet-4-5\": {\n ...claudeSonnet,\n id: \"claude-sonnet-4-5\",\n reasoning: false,\n options: {\n thinkingConfig: {\n includeThoughts: false,\n },\n } satisfies GoogleGenerativeAIProviderOptions,\n },\n \"claude-sonnet-4-5-thinking\": {\n ...claudeSonnet,\n id: \"claude-sonnet-4-5-thinking\",\n name: \"Claude Sonnet 4.5 (Reasoning)\",\n },\n \"claude-opus-4-5-thinking\": {\n ...claudeOpus,\n id: \"claude-opus-4-5-thinking\",\n name: \"Claude Opus 4.5 (Reasoning)\",\n },\n }\n\n return {\n ...googleProvider,\n id: antigravityConfig().SERVICE_NAME,\n name: antigravityConfig().DISPLAY_NAME,\n api: antigravityConfig().ENDPOINTS.at(2) as string,\n models,\n }\n },\n transformRequest: Effect.fn(function* (context) {\n yield* Effect.log(\n \"Transforming request for: \",\n antigravityConfig().SERVICE_NAME,\n )\n\n const { body, headers, url } = context\n const innerRequest = body.request as Record<string, unknown>\n\n let sessionId: string | undefined\n if (\n innerRequest.labels\n && typeof innerRequest.labels === \"object\"\n && \"sessionId\" in innerRequest.labels\n ) {\n const labels = innerRequest.labels as Record<string, unknown>\n sessionId = labels.sessionId as string\n delete labels.sessionId\n if (Object.keys(labels).length === 0) {\n delete innerRequest.labels\n }\n }\n\n // Handle thinkingConfig for Claude models\n const isClaude = body.model.toLowerCase().includes(\"claude\")\n const isThinking = body.model.toLowerCase().includes(\"thinking\")\n\n if (isClaude && body.request && typeof body.request === \"object\") {\n const request = body.request as Record<string, unknown>\n const generationConfig = request.generationConfig as\n | Record<string, unknown>\n | undefined\n\n innerRequest.toolConfig = {\n functionCallingConfig: {\n mode: \"VALIDATED\",\n },\n }\n\n // For non-thinking Claude, remove thinkingConfig entirely\n if (!isThinking && generationConfig?.thinkingConfig) {\n delete generationConfig.thinkingConfig\n }\n\n // For thinking Claude, convert camelCase to snake_case and add default budget\n if (isThinking && generationConfig?.thinkingConfig) {\n const thinkingConfig = generationConfig.thinkingConfig as Record<\n string,\n unknown\n >\n\n if (thinkingConfig.includeThoughts !== undefined) {\n thinkingConfig.include_thoughts = thinkingConfig.includeThoughts\n delete thinkingConfig.includeThoughts\n }\n\n if (thinkingConfig.thinkingBudget !== undefined) {\n thinkingConfig.thinking_budget = thinkingConfig.thinkingBudget\n delete thinkingConfig.thinkingBudget\n }\n\n // Add default thinking_budget if not present (required for Claude thinking)\n if (thinkingConfig.thinking_budget === undefined) {\n thinkingConfig.thinking_budget = 32768 // Default to high tier\n }\n }\n\n if (isThinking) {\n headers.set(\"anthropic-beta\", \"interleaved-thinking-2025-05-14\")\n }\n }\n\n if (sessionId) {\n const hashedSession = yield* Effect.promise(() => hash(sessionId))\n\n const finalSessionId = [\n `-${crypto.randomUUID()}`,\n body.model,\n body.project,\n `seed-${hashedSession}`,\n ].join(\":\")\n\n innerRequest.sessionId = finalSessionId\n }\n\n if (innerRequest.tools && Array.isArray(innerRequest.tools)) {\n const tools = innerRequest.tools as Array<Record<string, unknown>>\n for (const tool of tools) {\n if (\n tool.functionDeclarations\n && Array.isArray(tool.functionDeclarations)\n ) {\n const functionDeclarations = tool.functionDeclarations as Array<\n Record<string, unknown>\n >\n for (let i = 0; i < functionDeclarations.length; i++) {\n const declaration = functionDeclarations[i]\n if (declaration && declaration.name === \"todoread\") {\n functionDeclarations[i] = {\n ...functionDeclarations[i],\n parameters: {\n type: \"object\",\n properties: {\n _placeholder: {\n type: \"boolean\",\n description: \"Placeholder. Always pass true.\",\n },\n },\n required: [\"_placeholder\"],\n additionalProperties: false,\n },\n }\n }\n }\n }\n }\n }\n\n // if (\n // innerRequest.systemInstruction\n // && typeof innerRequest.systemInstruction === \"object\"\n // ) {\n // const systemInstruction = innerRequest.systemInstruction as Record<\n // string,\n // unknown\n // >\n\n // if (systemInstruction.parts && Array.isArray(systemInstruction.parts)) {\n // let parts = systemInstruction.parts as Array<{ text: string }>\n\n // parts.unshift({\n // text: \"You are Antigravity, a powerful agentic AI coding assistant designed by the Google DeepMind team working on Advanced Agentic Coding.\",\n // })\n // }\n // }\n\n if (\n innerRequest.systemInstruction\n && typeof innerRequest.systemInstruction === \"object\"\n ) {\n const systemInstruction = innerRequest.systemInstruction as Record<\n string,\n unknown\n >\n\n systemInstruction.role = \"user\"\n }\n\n return {\n headers,\n url,\n body: {\n ...body,\n requestType: \"agent\",\n userAgent: \"antigravity\",\n requestId: `agent-${crypto.randomUUID()}`,\n },\n }\n }),\n})\n\nasync function hash(str: string) {\n const encoder = new TextEncoder()\n const data = encoder.encode(str)\n\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", data)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n const hashHex = hashArray.map((b) => b.toString(16).padStart(2, \"0\")).join(\"\")\n\n return hashHex.slice(0, 16)\n}\n","import type { PluginInput } from \"@opencode-ai/plugin\"\nimport { Context } from \"effect\"\n\nexport class OpenCodeContext extends Context.Tag(\"OpenCodeContext\")<\n OpenCodeContext,\n PluginInput\n>() {}\n","import { PlatformLogger } from \"@effect/platform\"\nimport { Effect, Inspectable, Logger, LogLevel, pipe } from \"effect\"\nimport path from \"node:path\"\nimport type { OpenCodeLogLevel } from \"../types\"\nimport { ProviderConfig } from \"./services/config\"\nimport { OpenCodeContext } from \"./services/opencode\"\n\nconst makeOpenCodeLogger = Effect.gen(function* () {\n const openCode = yield* OpenCodeContext\n const config = yield* ProviderConfig\n\n return Logger.make((log) => {\n let level: OpenCodeLogLevel = \"debug\"\n\n if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Error)) {\n level = \"error\"\n } else if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Warning)) {\n level = \"warn\"\n } else if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Info)) {\n level = \"info\"\n }\n\n const message = Inspectable.toStringUnknown(log.message)\n\n void openCode.client.app.log({\n body: {\n level,\n message,\n service: config.SERVICE_NAME,\n },\n })\n })\n})\n\nexport const combinedLogger = Effect.gen(function* () {\n const openCodeLogger = yield* makeOpenCodeLogger\n const fileLogger = yield* pipe(\n Logger.jsonLogger,\n PlatformLogger.toFile(path.join(import.meta.dir, \"plugin.log\")),\n )\n\n return Logger.zip(openCodeLogger, fileLogger)\n})\n","/**\n * OAuth service\n *\n * Handles initial OAuth authentication flow only.\n * Token refresh is handled by the Session service.\n */\n\nimport {\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse,\n} from \"@effect/platform\"\nimport { BunHttpServer } from \"@effect/platform-bun\"\nimport { Data, Deferred, Effect, Fiber, Schema } from \"effect\"\nimport { OAuth2Client } from \"google-auth-library\"\nimport type { BunServeOptions } from \"../../types\"\nimport { ProviderConfig } from \"./config\"\n\nexport class OAuthError extends Data.TaggedError(\"OAuthError\")<{\n readonly reason: \"browser\" | \"callback\" | \"state_mismatch\" | \"token_exchange\"\n readonly message: string\n readonly cause?: unknown\n}> {}\n\nconst SuccessParamsSchema = Schema.Struct({\n code: Schema.String,\n state: Schema.String,\n})\n\nconst FailureParamsSchema = Schema.Struct({\n error: Schema.String,\n error_description: Schema.optional(Schema.String),\n state: Schema.optional(Schema.String),\n})\n\nconst isFailureParams = Schema.is(FailureParamsSchema)\n\nconst ParamsSchema = Schema.Union(SuccessParamsSchema, FailureParamsSchema)\n\nclass OAuth extends Effect.Service<OAuth>()(\"OAuth\", {\n effect: Effect.gen(function* () {\n const config = yield* ProviderConfig\n\n const client = new OAuth2Client({\n clientId: config.CLIENT_ID,\n clientSecret: config.CLIENT_SECRET,\n })\n const serverOptions: BunServeOptions = { port: 0 }\n const ServerLive = BunHttpServer.layerServer(serverOptions)\n\n const authenticate = Effect.gen(function* () {\n yield* HttpServer.logAddress\n\n const deferredParams = yield* Deferred.make<\n typeof SuccessParamsSchema.Type,\n OAuthError\n >()\n\n const redirectUri = yield* HttpServer.addressFormattedWith((address) =>\n Effect.succeed(`${address}/oauth2callback`),\n )\n const state = crypto.randomUUID()\n\n const authUrl = client.generateAuthUrl({\n state,\n redirect_uri: redirectUri,\n access_type: \"offline\",\n scope: config.SCOPES as unknown as string[],\n prompt: \"consent\",\n })\n yield* Effect.log(`OAuth2 authorization URL: ${authUrl}`)\n\n const serverFiber = yield* HttpRouter.empty.pipe(\n HttpRouter.get(\n \"/oauth2callback\",\n Effect.gen(function* () {\n const params =\n yield* HttpServerRequest.schemaSearchParams(ParamsSchema)\n\n if (isFailureParams(params)) {\n yield* Deferred.fail(\n deferredParams,\n new OAuthError({\n reason: \"callback\",\n message: `${params.error} - ${params.error_description ?? \"No additional details provided\"}`,\n }),\n )\n } else {\n yield* Deferred.succeed(deferredParams, params)\n }\n\n return yield* HttpServerResponse.text(\"You may now close this tab.\")\n }).pipe(Effect.tapError(Effect.logError)),\n ),\n HttpServer.serveEffect(),\n Effect.fork,\n )\n\n yield* Effect.log(\"Started OAuth2 callback server\")\n\n const search = yield* Deferred.await(deferredParams)\n yield* Effect.log(\"Received OAuth2 callback with params\", search)\n\n yield* Fiber.interrupt(serverFiber)\n\n if (state !== search.state) {\n return yield* new OAuthError({\n reason: \"state_mismatch\",\n message: \"Invalid state parameter. Possible CSRF attack.\",\n })\n }\n\n const result = yield* Effect.tryPromise({\n try: () =>\n client.getToken({\n code: search.code,\n redirect_uri: redirectUri,\n }),\n catch: (cause) =>\n new OAuthError({\n reason: \"token_exchange\",\n message: \"Failed to exchange authorization code for tokens\",\n cause,\n }),\n })\n\n return result.tokens\n }).pipe(Effect.provide(ServerLive), Effect.scoped)\n\n return { authenticate }\n }),\n}) {}\n\nexport { OAuth }\n","import {\n HttpClient,\n HttpClientRequest,\n HttpClientResponse,\n} from \"@effect/platform\"\nimport { Data, Effect, pipe, Ref, Schema } from \"effect\"\nimport { OAuth2Client } from \"google-auth-library\"\nimport type { Credentials } from \"../../types\"\nimport { CODE_ASSIST_VERSION, ProviderConfig } from \"./config\"\nimport { OpenCodeContext } from \"./opencode\"\n\nexport class SessionError extends Data.TaggedError(\"SessionError\")<{\n readonly reason:\n | \"project_fetch\"\n | \"token_refresh\"\n | \"no_tokens\"\n | \"unauthorized\"\n readonly message: string\n readonly cause?: unknown\n}> {}\n\nexport class TokenExpiredError extends Data.TaggedError(\"TokenExpiredError\")<{\n readonly message?: string\n}> {}\n\nconst CodeAssistTier = Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n description: Schema.String,\n userDefinedCloudaicompanionProject: Schema.Boolean,\n isDefault: Schema.optional(Schema.Boolean),\n})\n\nconst LoadCodeAssistResponse = Schema.Struct({\n currentTier: CodeAssistTier,\n allowedTiers: Schema.Array(CodeAssistTier),\n cloudaicompanionProject: Schema.String,\n gcpManaged: Schema.Boolean,\n manageSubscriptionUri: Schema.String,\n})\n\nexport type LoadCodeAssistResponse = typeof LoadCodeAssistResponse.Type\n\nexport class Session extends Effect.Service<Session>()(\"Session\", {\n effect: Effect.gen(function* () {\n const config = yield* ProviderConfig\n const openCode = yield* OpenCodeContext\n const httpClient = yield* HttpClient.HttpClient\n\n const credentialsRef = yield* Ref.make<Credentials | null>(null)\n const projectRef = yield* Ref.make<LoadCodeAssistResponse | null>(null)\n\n const endpoint = config.ENDPOINTS.at(0) as string\n const oauthClient = new OAuth2Client({\n clientId: config.CLIENT_ID,\n clientSecret: config.CLIENT_SECRET,\n })\n\n const getCredentials = Effect.gen(function* () {\n const current = yield* Ref.get(credentialsRef)\n if (!current) {\n return yield* new SessionError({\n reason: \"no_tokens\",\n message: \"No credentials set\",\n })\n }\n\n return current\n })\n\n const refreshTokens = Effect.gen(function* () {\n const credentials = yield* getCredentials\n oauthClient.setCredentials(credentials)\n\n const result = yield* Effect.tryPromise({\n try: () => oauthClient.refreshAccessToken(),\n catch: (cause) =>\n new SessionError({\n reason: \"token_refresh\",\n message: \"Failed to refresh access token\",\n cause,\n }),\n })\n\n const newCredentials = result.credentials\n yield* Ref.set(credentialsRef, newCredentials as Credentials)\n\n const accessToken = newCredentials.access_token\n const refreshToken = newCredentials.refresh_token\n const expiryDate = newCredentials.expiry_date\n\n if (accessToken && refreshToken && expiryDate) {\n yield* Effect.promise(() =>\n openCode.client.auth.set({\n path: { id: config.SERVICE_NAME },\n body: {\n type: \"oauth\",\n access: accessToken,\n refresh: refreshToken,\n expires: expiryDate,\n },\n }),\n )\n }\n\n return newCredentials\n })\n\n const fetchAttempt = Effect.gen(function* () {\n const credentials = yield* getCredentials\n\n const request = yield* HttpClientRequest.post(\n `${endpoint}/${CODE_ASSIST_VERSION}:loadCodeAssist`,\n ).pipe(\n HttpClientRequest.bearerToken(credentials.access_token),\n HttpClientRequest.bodyJson({\n metadata: {\n ideType: \"IDE_UNSPECIFIED\",\n platform: \"PLATFORM_UNSPECIFIED\",\n pluginType: \"GEMINI\",\n },\n }),\n )\n\n return yield* pipe(\n httpClient.execute(request),\n Effect.andThen(\n HttpClientResponse.matchStatus({\n \"2xx\": (res) =>\n HttpClientResponse.schemaBodyJson(LoadCodeAssistResponse)(res),\n 401: () => new TokenExpiredError({ message: \"Token expired\" }),\n orElse: (response) =>\n new SessionError({\n reason: \"project_fetch\",\n message: `HTTP error: ${response.status}`,\n }),\n }),\n ),\n )\n })\n\n const fetchProject = fetchAttempt.pipe(\n Effect.catchTag(\"TokenExpiredError\", () =>\n pipe(\n Effect.log(\"Token expired, refreshing...\"),\n Effect.flatMap(() => refreshTokens),\n Effect.flatMap(() => fetchAttempt),\n ),\n ),\n Effect.catchAll((error) => {\n if (error instanceof SessionError) {\n return Effect.fail(error)\n }\n return Effect.fail(\n new SessionError({\n reason: \"project_fetch\",\n message: \"Failed to fetch project\",\n cause: error,\n }),\n )\n }),\n )\n\n const ensureProject = Effect.gen(function* () {\n const cached = yield* Ref.get(projectRef)\n if (cached !== null) {\n return cached\n }\n const project = yield* fetchProject\n yield* Ref.set(projectRef, project)\n return project\n })\n\n const getAccessToken = Effect.gen(function* () {\n const currentCreds = yield* Ref.get(credentialsRef)\n\n if (!currentCreds?.access_token) {\n return yield* new SessionError({\n reason: \"no_tokens\",\n message: \"No access token available\",\n })\n }\n\n const buffer = 5 * 60 * 1000\n const isExpired = (currentCreds.expiry_date ?? 0) < Date.now() + buffer\n\n let accessToken = currentCreds.access_token\n\n if (isExpired) {\n yield* Effect.log(\"Access token expired, refreshing...\")\n const refreshed = yield* refreshTokens\n if (!refreshed.access_token) {\n return yield* new SessionError({\n reason: \"token_refresh\",\n message: \"Refresh did not return access token\",\n })\n }\n accessToken = refreshed.access_token\n }\n\n yield* ensureProject\n return accessToken\n })\n\n return {\n setCredentials: (credentials: Credentials) =>\n Ref.set(credentialsRef, credentials),\n getAccessToken,\n ensureProject,\n }\n }),\n}) {}\n","import { FetchHttpClient } from \"@effect/platform\"\nimport { BunFileSystem } from \"@effect/platform-bun\"\nimport type { PluginInput } from \"@opencode-ai/plugin\"\nimport { Layer, Logger, ManagedRuntime, pipe } from \"effect\"\nimport { combinedLogger } from \"./logger\"\nimport { ProviderConfig, type ProviderConfigShape } from \"./services/config\"\nimport { OAuth } from \"./services/oauth\"\nimport { OpenCodeContext } from \"./services/opencode\"\nimport { Session } from \"./services/session\"\n\nexport const makeRuntime = ({\n providerConfig,\n openCodeCtx,\n}: {\n providerConfig: ProviderConfigShape\n openCodeCtx: PluginInput\n}) => {\n const LoggerLive = Logger.replaceScoped(Logger.defaultLogger, combinedLogger)\n const ProviderConfigLive = Layer.succeed(ProviderConfig, providerConfig)\n const OpenCodeLive = Layer.succeed(OpenCodeContext, openCodeCtx)\n\n const MainLive = pipe(\n Layer.empty,\n Layer.provide(LoggerLive),\n Layer.provide(BunFileSystem.layer),\n Layer.merge(OAuth.Default),\n Layer.merge(Session.Default),\n Layer.provideMerge(OpenCodeLive),\n Layer.provideMerge(FetchHttpClient.layer),\n Layer.provideMerge(ProviderConfigLive),\n )\n\n return ManagedRuntime.make(MainLive)\n}\n","import { regex } from \"arkregex\"\nimport { Effect, pipe } from \"effect\"\nimport {\n CODE_ASSIST_VERSION,\n ProviderConfig,\n type RequestContext,\n} from \"../lib/services/config\"\nimport { Session } from \"../lib/services/session\"\n\nconst STREAM_ACTION = \"streamGenerateContent\"\nconst PATH_PATTERN = regex(\"/models/(?<model>[^:]+):(?<action>\\\\w+)\")\n\nexport const transformRequest = Effect.fn(\"transformRequest\")(function* (\n input: Parameters<typeof fetch>[0],\n init: Parameters<typeof fetch>[1],\n endpoint: string,\n) {\n const config = yield* ProviderConfig\n const session = yield* Session\n const accessToken = yield* session.getAccessToken\n const project = yield* session.ensureProject\n const projectId = project.cloudaicompanionProject\n\n const url = new URL(input instanceof Request ? input.url : input)\n\n // Rewrite the URL to use the specified endpoint\n const endpointUrl = new URL(endpoint)\n url.protocol = endpointUrl.protocol\n url.host = endpointUrl.host\n\n const match = PATH_PATTERN.exec(url.pathname)\n if (!match) {\n return {\n input: url.toString(),\n init: init ?? {},\n streaming: false,\n }\n }\n\n const { model, action } = match.groups\n const streaming = action === STREAM_ACTION\n\n // Transform URL to internal endpoint\n url.pathname = `/${CODE_ASSIST_VERSION}:${action}`\n if (streaming) {\n url.searchParams.set(\"alt\", \"sse\")\n }\n\n // Transform headers\n const headers = new Headers(init?.headers)\n headers.delete(\"x-api-key\")\n // headers.delete(\"x-goog-api-key\")\n headers.set(\"x-opencode-tools-debug\", \"1\")\n headers.set(\"Authorization\", `Bearer ${accessToken}`)\n\n for (const [key, value] of Object.entries(config.HEADERS)) {\n headers.set(key, value)\n }\n\n if (streaming) {\n headers.set(\"Accept\", \"text/event-stream\")\n }\n\n // Wrap and transform request\n const isJson = typeof init?.body === \"string\"\n const parsedBody = yield* pipe(\n Effect.try(() => (isJson ? JSON.parse(init.body as string) : null)),\n Effect.orElseSucceed(() => null),\n )\n\n const wrappedBody = {\n project: projectId,\n model,\n request: parsedBody ?? {},\n }\n\n const {\n body: transformedBody,\n headers: finalHeaders,\n url: finalUrl,\n } =\n config.transformRequest ?\n yield* config.transformRequest({\n body: wrappedBody,\n headers,\n url,\n } satisfies RequestContext)\n : { body: wrappedBody, headers, url }\n\n const finalBody =\n isJson && parsedBody ? JSON.stringify(transformedBody) : init?.body\n\n return {\n input: finalUrl.toString(),\n init: {\n ...init,\n headers: finalHeaders,\n body: finalBody,\n },\n streaming,\n }\n})\n","export const transformNonStreamingResponse = async (\n response: Response,\n): Promise<Response> => {\n const contentType = response.headers.get(\"content-type\")\n\n if (!contentType?.includes(\"application/json\")) {\n return response\n }\n\n try {\n const cloned = response.clone()\n const parsed = (await cloned.json()) as { response?: unknown }\n\n if (parsed.response !== undefined) {\n const { response: responseData, ...rest } = parsed\n return new Response(JSON.stringify({ ...rest, ...responseData }), {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n })\n }\n } catch {\n // Return original if parse fails\n }\n\n return response\n}\n","import {\n encoder,\n type Event,\n makeChannel,\n Retry,\n} from \"@effect/experimental/Sse\"\nimport { pipe, Stream } from \"effect\"\n\nconst parseAndMerge = (event: Event): string => {\n if (!event.data) {\n return encoder.write(event)\n }\n\n try {\n const parsed = JSON.parse(event.data) as {\n response?: Record<string, unknown>\n }\n if (parsed.response) {\n const { response, ...rest } = parsed\n return encoder.write({\n ...event,\n data: JSON.stringify({ ...rest, ...response }),\n })\n }\n\n return encoder.write(event)\n } catch {\n return encoder.write(event)\n }\n}\n\nconst parseSSE = (body: ReadableStream<Uint8Array>) =>\n pipe(\n Stream.fromReadableStream(\n () => body,\n (error) => error,\n ),\n Stream.decodeText,\n Stream.pipeThroughChannel(makeChannel()),\n Stream.map((event) =>\n Retry.is(event) ? encoder.write(event) : parseAndMerge(event),\n ),\n Stream.encodeText,\n )\n\nexport const transformStreamingResponse = (response: Response) => {\n if (!response.body) {\n return response\n }\n\n const transformed = parseSSE(response.body)\n const readable = Stream.toReadableStream(\n transformed as Stream.Stream<Uint8Array, never, never>,\n )\n\n return new Response(readable, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n })\n}\n","import type {\n GoogleGenerativeAIProviderOptions,\n GoogleGenerativeAIProviderSettings,\n} from \"@ai-sdk/google\"\nimport { HttpClient } from \"@effect/platform\"\nimport type { Plugin } from \"@opencode-ai/plugin\"\nimport { Effect } from \"effect\"\nimport { makeRuntime } from \"./lib/runtime\"\nimport {\n antigravityConfig,\n geminiCliConfig,\n ProviderConfig,\n} from \"./lib/services/config\"\nimport { OAuth } from \"./lib/services/oauth\"\nimport { Session } from \"./lib/services/session\"\nimport { transformRequest } from \"./transform/request\"\nimport { transformNonStreamingResponse } from \"./transform/response\"\nimport { transformStreamingResponse } from \"./transform/stream\"\nimport type { Credentials, ModelsDev } from \"./types\"\n\nconst fetchModelsDev = Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.get(\"https://models.dev/api.json\")\n return (yield* response.json) as ModelsDev\n})\n\nconst customFetch = Effect.fn(function* (\n input: Parameters<typeof fetch>[0],\n init: Parameters<typeof fetch>[1],\n) {\n const config = yield* ProviderConfig\n\n let lastResponse: Response | null = null\n\n for (const endpoint of config.ENDPOINTS) {\n const result = yield* transformRequest(input, init, endpoint)\n\n const { request, ...loggedBody } = JSON.parse(result.init.body as string)\n const generationConfig = request.generationConfig\n\n yield* Effect.log(\n \"Transformed request (Omitting request except generationConfig) :\",\n result.streaming,\n result.input,\n { ...loggedBody, request: { generationConfig } },\n )\n\n const response = yield* Effect.promise(() =>\n fetch(result.input, result.init),\n )\n\n // On 429 or 403, try next endpoint\n if (response.status === 429 || response.status === 403) {\n yield* Effect.log(`${response.status} on ${endpoint}, trying next...`)\n lastResponse = response\n continue\n }\n\n if (!response.ok) {\n const cloned = response.clone()\n const clonedJson = yield* Effect.promise(() => cloned.json())\n\n yield* Effect.log(\n \"Received response:\",\n cloned.status,\n clonedJson,\n cloned.headers,\n )\n }\n\n return result.streaming ?\n transformStreamingResponse(response)\n : yield* Effect.promise(() => transformNonStreamingResponse(response))\n }\n\n // All endpoints exhausted with 429\n yield* Effect.logWarning(\"All endpoints rate limited (429)\")\n return lastResponse as Response\n}, Effect.tapDefect(Effect.logError))\n\nexport const geminiCli: Plugin = async (context) => {\n const runtime = makeRuntime({\n openCodeCtx: context,\n providerConfig: geminiCliConfig(),\n })\n\n const config = await runtime.runPromise(\n Effect.gen(function* () {\n const providerConfig = yield* ProviderConfig\n const modelsDev = yield* fetchModelsDev\n\n return providerConfig.getConfig(modelsDev)\n }),\n )\n\n return {\n config: async (cfg) => {\n cfg.provider ??= {}\n cfg.provider[config.id as string] = config\n },\n auth: {\n provider: config.id as string,\n loader: async (getAuth) => {\n const auth = await getAuth()\n if (auth.type !== \"oauth\") return {}\n\n const credentials: Credentials = {\n access_token: auth.access,\n refresh_token: auth.refresh,\n expiry_date: auth.expires,\n }\n\n await runtime.runPromise(\n Effect.gen(function* () {\n const session = yield* Session\n yield* session.setCredentials(credentials)\n }),\n )\n\n return {\n apiKey: \"\",\n fetch: (async (input, init) => {\n const response = await runtime.runPromise(customFetch(input, init))\n return response\n }) as typeof fetch,\n } satisfies GoogleGenerativeAIProviderSettings\n },\n methods: [\n {\n type: \"oauth\",\n label: \"OAuth with Google\",\n authorize: async () => {\n const result = await runtime.runPromise(\n Effect.gen(function* () {\n const oauth = yield* OAuth\n return yield* oauth.authenticate\n }),\n )\n\n return {\n url: \"\",\n method: \"auto\",\n instructions: \"You are now authenticated!\",\n callback: async () => {\n const accessToken = result.access_token\n const refreshToken = result.refresh_token\n const expiryDate = result.expiry_date\n\n if (!accessToken || !refreshToken || !expiryDate) {\n return { type: \"failed\" }\n }\n\n return {\n type: \"success\",\n provider: config.id as string,\n access: accessToken,\n refresh: refreshToken,\n expires: expiryDate,\n }\n },\n }\n },\n },\n ],\n },\n }\n}\n\nexport const antigravity: Plugin = async (context) => {\n const runtime = makeRuntime({\n openCodeCtx: context,\n providerConfig: antigravityConfig(),\n })\n\n const config = await runtime.runPromise(\n Effect.gen(function* () {\n const providerConfig = yield* ProviderConfig\n const modelsDev = yield* fetchModelsDev\n\n return providerConfig.getConfig(modelsDev)\n }),\n )\n\n return {\n config: async (cfg) => {\n cfg.provider ??= {}\n cfg.provider[config.id as string] = config\n },\n auth: {\n provider: config.id as string,\n loader: async (getAuth) => {\n const auth = await getAuth()\n if (auth.type !== \"oauth\") return {}\n\n const credentials: Credentials = {\n access_token: auth.access,\n refresh_token: auth.refresh,\n expiry_date: auth.expires,\n }\n\n await runtime.runPromise(\n Effect.gen(function* () {\n const session = yield* Session\n yield* session.setCredentials(credentials)\n }),\n )\n\n return {\n apiKey: \"\",\n fetch: (async (input, init) => {\n const response = await runtime.runPromise(customFetch(input, init))\n return response\n }) as typeof fetch,\n } satisfies GoogleGenerativeAIProviderSettings\n },\n methods: [\n {\n type: \"oauth\",\n label: \"OAuth with Google\",\n authorize: async () => {\n const result = await runtime.runPromise(\n Effect.gen(function* () {\n const oauth = yield* OAuth\n return yield* oauth.authenticate\n }),\n )\n\n return {\n url: \"\",\n method: \"auto\",\n instructions: \"You are now authenticated!\",\n callback: async () => {\n const accessToken = result.access_token\n const refreshToken = result.refresh_token\n const expiryDate = result.expiry_date\n\n if (!accessToken || !refreshToken || !expiryDate) {\n return { type: \"failed\" }\n }\n\n return {\n type: \"success\",\n provider: config.id as string,\n access: accessToken,\n refresh: refreshToken,\n expires: expiryDate,\n }\n },\n }\n },\n },\n ],\n },\n \"chat.params\": async (input, output) => {\n await runtime.runPromise(\n Effect.log(\"chat.params event before:\", input.model, output.options),\n )\n\n if (input.model.providerID === config.id) {\n output.options = {\n ...output.options,\n labels: {\n sessionId: input.sessionID,\n },\n } satisfies GoogleGenerativeAIProviderOptions\n }\n\n await runtime.runPromise(\n Effect.log(\"chat.params event after:\", input.model, output.options),\n )\n },\n }\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "opencode-google-auth",
3
+ "version": "0.0.1",
4
+ "description": "_description_",
5
+ "keywords": [
6
+ "opencode-google-auth"
7
+ ],
8
+ "homepage": "https://github.com/ericc-ch/opencode-google-auth",
9
+ "bugs": "https://github.com/ericc-ch/opencode-google-auth/issues",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/ericc-ch/opencode-google-auth.git"
13
+ },
14
+ "author": {
15
+ "name": "Erick Christian",
16
+ "email": "erickchristian48@gmail.com"
17
+ },
18
+ "type": "module",
19
+ "exports": {
20
+ ".": {
21
+ "default": "./dist/main.mjs"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "src"
27
+ ],
28
+ "scripts": {
29
+ "prebuild": "bun ./scripts/fetch-models.ts",
30
+ "build": "tsdown",
31
+ "predeploy": "bun run build",
32
+ "deploy": "bun ./scripts/deploy.ts",
33
+ "format": "prettier --write .",
34
+ "lint": "oxlint",
35
+ "prepack": "bun run build",
36
+ "release": "bumpp && npm publish",
37
+ "typecheck": "tsgo"
38
+ },
39
+ "dependencies": {
40
+ "@effect/experimental": "^0.58.0",
41
+ "@effect/platform": "^0.94.1",
42
+ "@effect/platform-bun": "^0.87.0",
43
+ "@opencode-ai/plugin": "^1.1.15",
44
+ "arkregex": "^0.0.5",
45
+ "effect": "^3.19.14",
46
+ "google-auth-library": "^10.5.0"
47
+ },
48
+ "devDependencies": {
49
+ "@ai-sdk/google": "^3.0.7",
50
+ "@effect/language-service": "^0.64.1",
51
+ "@types/bun": "^1.3.5",
52
+ "@types/yargs": "^17.0.35",
53
+ "@typescript/native-preview": "^7.0.0-dev.20260112.1",
54
+ "ai": "^6.0.30",
55
+ "bumpp": "^10.3.2",
56
+ "oxlint": "^1.39.0",
57
+ "oxlint-tsgolint": "^0.11.0",
58
+ "prettier": "^3.7.4",
59
+ "tsdown": "^0.19.0",
60
+ "typescript": "^5.9.3",
61
+ "yargs": "^18.0.0"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ }
66
+ }
@@ -0,0 +1,43 @@
1
+ import { PlatformLogger } from "@effect/platform"
2
+ import { Effect, Inspectable, Logger, LogLevel, pipe } from "effect"
3
+ import path from "node:path"
4
+ import type { OpenCodeLogLevel } from "../types"
5
+ import { ProviderConfig } from "./services/config"
6
+ import { OpenCodeContext } from "./services/opencode"
7
+
8
+ const makeOpenCodeLogger = Effect.gen(function* () {
9
+ const openCode = yield* OpenCodeContext
10
+ const config = yield* ProviderConfig
11
+
12
+ return Logger.make((log) => {
13
+ let level: OpenCodeLogLevel = "debug"
14
+
15
+ if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Error)) {
16
+ level = "error"
17
+ } else if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Warning)) {
18
+ level = "warn"
19
+ } else if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Info)) {
20
+ level = "info"
21
+ }
22
+
23
+ const message = Inspectable.toStringUnknown(log.message)
24
+
25
+ void openCode.client.app.log({
26
+ body: {
27
+ level,
28
+ message,
29
+ service: config.SERVICE_NAME,
30
+ },
31
+ })
32
+ })
33
+ })
34
+
35
+ export const combinedLogger = Effect.gen(function* () {
36
+ const openCodeLogger = yield* makeOpenCodeLogger
37
+ const fileLogger = yield* pipe(
38
+ Logger.jsonLogger,
39
+ PlatformLogger.toFile(path.join(import.meta.dir, "plugin.log")),
40
+ )
41
+
42
+ return Logger.zip(openCodeLogger, fileLogger)
43
+ })
@@ -0,0 +1,34 @@
1
+ import { FetchHttpClient } from "@effect/platform"
2
+ import { BunFileSystem } from "@effect/platform-bun"
3
+ import type { PluginInput } from "@opencode-ai/plugin"
4
+ import { Layer, Logger, ManagedRuntime, pipe } from "effect"
5
+ import { combinedLogger } from "./logger"
6
+ import { ProviderConfig, type ProviderConfigShape } from "./services/config"
7
+ import { OAuth } from "./services/oauth"
8
+ import { OpenCodeContext } from "./services/opencode"
9
+ import { Session } from "./services/session"
10
+
11
+ export const makeRuntime = ({
12
+ providerConfig,
13
+ openCodeCtx,
14
+ }: {
15
+ providerConfig: ProviderConfigShape
16
+ openCodeCtx: PluginInput
17
+ }) => {
18
+ const LoggerLive = Logger.replaceScoped(Logger.defaultLogger, combinedLogger)
19
+ const ProviderConfigLive = Layer.succeed(ProviderConfig, providerConfig)
20
+ const OpenCodeLive = Layer.succeed(OpenCodeContext, openCodeCtx)
21
+
22
+ const MainLive = pipe(
23
+ Layer.empty,
24
+ Layer.provide(LoggerLive),
25
+ Layer.provide(BunFileSystem.layer),
26
+ Layer.merge(OAuth.Default),
27
+ Layer.merge(Session.Default),
28
+ Layer.provideMerge(OpenCodeLive),
29
+ Layer.provideMerge(FetchHttpClient.layer),
30
+ Layer.provideMerge(ProviderConfigLive),
31
+ )
32
+
33
+ return ManagedRuntime.make(MainLive)
34
+ }
@@ -0,0 +1,194 @@
1
+ import { describe, expect, it } from "bun:test"
2
+ import { transformRequest } from "../../transform/request"
3
+ import { antigravityConfig, ProviderConfig } from "./config"
4
+ import { Session } from "./session"
5
+ import { Effect, Layer, pipe } from "effect"
6
+ import { generateText } from "ai"
7
+ import { createGoogleGenerativeAI } from "@ai-sdk/google"
8
+
9
+ describe("Antigravity transformRequest", () => {
10
+ const baseParams = {
11
+ accessToken: "test-token",
12
+ projectId: "test-project-123",
13
+ }
14
+
15
+ const config = antigravityConfig()
16
+
17
+ const MockSession = Layer.succeed(
18
+ Session,
19
+ Session.of({
20
+ getAccessToken: Effect.succeed(baseParams.accessToken),
21
+ ensureProject: Effect.succeed({
22
+ cloudaicompanionProject: baseParams.projectId,
23
+ currentTier: {
24
+ id: "free",
25
+ name: "Free",
26
+ description: "",
27
+ userDefinedCloudaicompanionProject: false,
28
+ userDefinedProjectId: "test-project-123",
29
+ },
30
+ allowedTiers: [],
31
+ gcpManaged: false,
32
+ manageSubscriptionUri: "",
33
+ }),
34
+ setCredentials: () => Effect.void,
35
+ } as unknown as Session),
36
+ ).pipe(Layer.provideMerge(Layer.succeed(ProviderConfig, config)))
37
+
38
+ const setupTest = () => {
39
+ let capturedBody: unknown = null
40
+
41
+ const mockFetch = async (input: string | Request, init?: RequestInit) => {
42
+ const result = await pipe(
43
+ transformRequest(
44
+ input,
45
+ init as unknown as Parameters<typeof fetch>[1],
46
+ "https://example.com",
47
+ ),
48
+ Effect.provide(MockSession),
49
+ Effect.runPromise,
50
+ )
51
+ capturedBody = JSON.parse(result.init.body as string)
52
+
53
+ return new Response(
54
+ JSON.stringify({
55
+ candidates: [
56
+ {
57
+ content: {
58
+ parts: [{ text: "Hello" }],
59
+ role: "model",
60
+ },
61
+ finishReason: "STOP",
62
+ },
63
+ ],
64
+ usageMetadata: {
65
+ promptTokenCount: 1,
66
+ candidatesTokenCount: 1,
67
+ totalTokenCount: 2,
68
+ },
69
+ }),
70
+ {
71
+ status: 200,
72
+ headers: { "Content-Type": "application/json" },
73
+ },
74
+ )
75
+ }
76
+
77
+ const google = createGoogleGenerativeAI({
78
+ apiKey: "test-key",
79
+ fetch: mockFetch as typeof fetch,
80
+ })
81
+
82
+ return {
83
+ google,
84
+ getCapturedBody: () => capturedBody as Record<string, unknown>,
85
+ }
86
+ }
87
+
88
+ it("propagates sessionId from labels and cleans up labels", async () => {
89
+ const { google, getCapturedBody } = setupTest()
90
+
91
+ await generateText({
92
+ model: google("gemini-1.5-flash"),
93
+ prompt: "Hello",
94
+ providerOptions: {
95
+ google: {
96
+ labels: {
97
+ sessionId: "test-session-id",
98
+ otherLabel: "keep-me",
99
+ },
100
+ },
101
+ },
102
+ })
103
+
104
+ const body = getCapturedBody()
105
+ const innerRequest = body.request as Record<string, unknown>
106
+ const labels = innerRequest.labels as Record<string, unknown>
107
+
108
+ // sessionId should be moved to innerRequest and transformed
109
+ // The actual format is `-${uuid}:${model}:${project}:seed-${hashedSession}`
110
+ expect(innerRequest.sessionId).not.toBe("test-session-id")
111
+ expect(innerRequest.sessionId).toBeString()
112
+ expect(
113
+ (innerRequest.sessionId as string).includes("gemini-1.5-flash"),
114
+ ).toBe(true)
115
+ expect(
116
+ (innerRequest.sessionId as string).includes("test-project-123"),
117
+ ).toBe(true)
118
+
119
+ // labels should be cleaned up (sessionId removed)
120
+ expect(labels.sessionId).toBeUndefined()
121
+ expect(labels.otherLabel).toBe("keep-me")
122
+
123
+ // Metadata should be present
124
+ expect(body.requestType).toBe("agent")
125
+ expect(body.userAgent).toBe("antigravity")
126
+ expect(body.requestId).toBeDefined()
127
+ })
128
+
129
+ it("removes labels object if it becomes empty after sessionId extraction", async () => {
130
+ const { google, getCapturedBody } = setupTest()
131
+
132
+ await generateText({
133
+ model: google("gemini-1.5-flash"),
134
+ prompt: "Hello",
135
+ providerOptions: {
136
+ google: {
137
+ labels: {
138
+ sessionId: "test-session-id",
139
+ },
140
+ },
141
+ },
142
+ })
143
+
144
+ const body = getCapturedBody()
145
+ const innerRequest = body.request as Record<string, unknown>
146
+
147
+ expect(innerRequest.sessionId).not.toBe("test-session-id")
148
+ expect(innerRequest.sessionId).toBeString()
149
+ expect(innerRequest.labels).toBeUndefined()
150
+ })
151
+
152
+ it("replaces todoread tool definition with placeholder schema", async () => {
153
+ const { google, getCapturedBody } = setupTest()
154
+
155
+ await generateText({
156
+ model: google("gemini-1.5-flash"),
157
+ prompt: "Hello",
158
+ tools: {
159
+ todoread: {
160
+ description: "Use this tool to read your todo list",
161
+ parameters: { type: "object", properties: {} },
162
+ },
163
+ },
164
+ })
165
+
166
+ const body = getCapturedBody()
167
+ const innerRequest = body.request as Record<string, unknown>
168
+ const tools = innerRequest.tools as Array<Record<string, unknown>>
169
+ const firstTool = tools[0]
170
+ expect(firstTool).toBeDefined()
171
+ if (!firstTool) throw new Error("firstTool is undefined")
172
+ const functionDeclarations = firstTool.functionDeclarations as Array<
173
+ Record<string, unknown>
174
+ >
175
+ const todoread = functionDeclarations.find((f) => f.name === "todoread")
176
+
177
+ expect(todoread).toBeDefined()
178
+ expect(todoread).toEqual({
179
+ name: "todoread",
180
+ description: "Use this tool to read your todo list",
181
+ parameters: {
182
+ type: "object",
183
+ properties: {
184
+ _placeholder: {
185
+ type: "boolean",
186
+ description: "Placeholder. Always pass true.",
187
+ },
188
+ },
189
+ required: ["_placeholder"],
190
+ additionalProperties: false,
191
+ },
192
+ })
193
+ })
194
+ })