@adport/provider-google 0.5.1 → 0.6.0

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.
package/dist/index.d.ts CHANGED
@@ -10,10 +10,14 @@ interface GoogleCredentials {
10
10
  refreshToken: string;
11
11
  /** Manager (MCC) customer id, digits only. */
12
12
  loginCustomerId?: string;
13
+ /** Per-operating-customer manager ids for multi-manager hosted runtimes. */
14
+ loginCustomerIds?: Record<string, string>;
13
15
  }
14
16
  interface SearchOptions {
15
17
  /** Stop after this many rows. */
16
18
  maxRows?: number;
19
+ /** Override the manager context for this search; null explicitly omits it. */
20
+ loginCustomerId?: string | null;
17
21
  }
18
22
  declare const DEFAULT_API_VERSION = "v25";
19
23
  declare function normalizeCustomerId(id: string): string;
@@ -28,6 +32,7 @@ declare class GoogleAdsRestClient {
28
32
  private readonly fetchImpl;
29
33
  private accessToken?;
30
34
  constructor(credentials: GoogleCredentials, version?: string, fetchImpl?: typeof fetch);
35
+ setLoginCustomerId(customerId: string, loginCustomerId: string): void;
31
36
  getAccessToken(): Promise<string>;
32
37
  private request;
33
38
  listAccessibleCustomers(): Promise<string[]>;
@@ -62,10 +67,13 @@ declare function formatGoogleAdsError(status: number, raw: string): string;
62
67
  declare class GoogleAdsProvider implements AdProvider {
63
68
  private readonly client;
64
69
  readonly id = "google";
70
+ private readonly discoveredLoginCustomerIds;
65
71
  constructor(client: GoogleAdsRestClient);
66
72
  capabilities(): ProviderCapabilities;
67
73
  standardActions(): StandardActions;
68
74
  listAccounts(): Promise<Account[]>;
75
+ /** Manager headers discovered for client accounts during account listing. */
76
+ loginCustomerIds(): Record<string, string>;
69
77
  report(query: NormalizedQuery): Promise<Report>;
70
78
  private toReportRow;
71
79
  gaqlSearch(input: {
package/dist/index.js CHANGED
@@ -20,6 +20,9 @@ var GoogleAdsRestClient = class {
20
20
  version;
21
21
  fetchImpl;
22
22
  accessToken;
23
+ setLoginCustomerId(customerId, loginCustomerId) {
24
+ (this.credentials.loginCustomerIds ??= {})[normalizeCustomerId(customerId)] = normalizeCustomerId(loginCustomerId);
25
+ }
23
26
  async getAccessToken() {
24
27
  if (this.accessToken && this.accessToken.expiresAt > Date.now()) {
25
28
  return this.accessToken.token;
@@ -53,8 +56,11 @@ var GoogleAdsRestClient = class {
53
56
  "developer-token": this.credentials.developerToken,
54
57
  "content-type": "application/json"
55
58
  };
56
- if (this.credentials.loginCustomerId) {
57
- headers["login-customer-id"] = normalizeCustomerId(this.credentials.loginCustomerId);
59
+ const operatingCustomerId = path.match(/^customers\/(\d{10})\//)?.[1];
60
+ const mappedLoginCustomerId = operatingCustomerId ? this.credentials.loginCustomerIds?.[operatingCustomerId] : void 0;
61
+ const loginCustomerId = init.loginCustomerId === null ? void 0 : init.loginCustomerId ?? mappedLoginCustomerId ?? this.credentials.loginCustomerId;
62
+ if (loginCustomerId) {
63
+ headers["login-customer-id"] = normalizeCustomerId(loginCustomerId);
58
64
  }
59
65
  const response = await this.fetchImpl(`${API_BASE}/${this.version}/${path}`, {
60
66
  method: init.method,
@@ -69,7 +75,10 @@ var GoogleAdsRestClient = class {
69
75
  }
70
76
  async listAccessibleCustomers() {
71
77
  const data = await this.request("customers:listAccessibleCustomers", {
72
- method: "GET"
78
+ method: "GET",
79
+ // Google documents that this call is scoped only by the OAuth user; a
80
+ // manager header has no effect and must not leak between cloud tenants.
81
+ loginCustomerId: null
73
82
  });
74
83
  return (data.resourceNames ?? []).map((name) => name.replace("customers/", ""));
75
84
  }
@@ -81,7 +90,7 @@ var GoogleAdsRestClient = class {
81
90
  do {
82
91
  const data = await this.request(
83
92
  `customers/${normalizeCustomerId(customerId)}/googleAds:search`,
84
- { method: "POST", body: { query, pageToken } }
93
+ { method: "POST", body: { query, pageToken }, loginCustomerId: options.loginCustomerId }
85
94
  );
86
95
  rows.push(...data.results ?? []);
87
96
  pageToken = data.nextPageToken;
@@ -158,6 +167,7 @@ var GoogleAdsProvider = class {
158
167
  }
159
168
  client;
160
169
  id = "google";
170
+ discoveredLoginCustomerIds = /* @__PURE__ */ new Map();
161
171
  capabilities() {
162
172
  return { serverDryRun: true };
163
173
  }
@@ -171,27 +181,69 @@ var GoogleAdsProvider = class {
171
181
  }
172
182
  async listAccounts() {
173
183
  const ids = await this.client.listAccessibleCustomers();
174
- const accounts = [];
184
+ const directlyAccessible = new Set(ids);
185
+ const accounts = /* @__PURE__ */ new Map();
186
+ const managers = [];
175
187
  for (const id of ids) {
176
188
  try {
177
189
  const rows = await this.client.search(
178
190
  id,
179
191
  "SELECT customer.id, customer.descriptive_name, customer.currency_code, customer.status, customer.manager FROM customer LIMIT 1",
180
- { maxRows: 1 }
192
+ { maxRows: 1, loginCustomerId: id }
181
193
  );
182
194
  const customer = rows[0]?.customer ?? {};
183
- accounts.push({
195
+ if (customer.manager) managers.push(id);
196
+ else accounts.set(id, {
184
197
  provider: this.id,
185
198
  id,
186
199
  name: customer.descriptiveName ?? `(account ${id})`,
187
200
  currency: customer.currencyCode,
188
- status: customer.manager ? `${customer.status ?? "UNKNOWN"} (manager)` : customer.status
201
+ status: customer.status
189
202
  });
190
203
  } catch {
191
- accounts.push({ provider: this.id, id, name: "(details unavailable)", status: "UNKNOWN" });
204
+ accounts.set(id, { provider: this.id, id, name: "(details unavailable)", status: "UNKNOWN" });
192
205
  }
193
206
  }
194
- return accounts;
207
+ for (const rootManagerId of managers) {
208
+ const pending = [rootManagerId];
209
+ const visited = /* @__PURE__ */ new Set();
210
+ while (pending.length > 0) {
211
+ const managerId = pending.shift();
212
+ if (visited.has(managerId)) continue;
213
+ visited.add(managerId);
214
+ const rows = await this.client.search(
215
+ managerId,
216
+ "SELECT customer_client.client_customer, customer_client.id, customer_client.level, customer_client.manager, customer_client.descriptive_name, customer_client.currency_code, customer_client.status FROM customer_client WHERE customer_client.level <= 1",
217
+ { maxRows: 1e4, loginCustomerId: rootManagerId }
218
+ );
219
+ for (const row of rows) {
220
+ const customer = row.customerClient ?? {};
221
+ if (Number(customer.level ?? 0) === 0) continue;
222
+ const customerId = customer.clientCustomer?.replace("customers/", "") ?? String(customer.id ?? "");
223
+ if (!/^\d{10}$/.test(customerId)) continue;
224
+ if (customer.manager) {
225
+ pending.push(customerId);
226
+ continue;
227
+ }
228
+ if (!directlyAccessible.has(customerId)) {
229
+ this.discoveredLoginCustomerIds.set(customerId, rootManagerId);
230
+ this.client.setLoginCustomerId(customerId, rootManagerId);
231
+ }
232
+ if (!accounts.has(customerId)) accounts.set(customerId, {
233
+ provider: this.id,
234
+ id: customerId,
235
+ name: customer.descriptiveName ?? `(account ${customerId})`,
236
+ currency: customer.currencyCode,
237
+ status: customer.status
238
+ });
239
+ }
240
+ }
241
+ }
242
+ return [...accounts.values()];
243
+ }
244
+ /** Manager headers discovered for client accounts during account listing. */
245
+ loginCustomerIds() {
246
+ return Object.fromEntries(this.discoveredLoginCustomerIds);
195
247
  }
196
248
  async report(query) {
197
249
  const range = resolveDateRange(query.dateRange);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/provider.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\n\n/**\n * Matches the google-ads.yaml naming conventions so credentials translate 1:1.\n */\nexport interface GoogleCredentials {\n developerToken: string;\n clientId: string;\n clientSecret: string;\n refreshToken: string;\n /** Manager (MCC) customer id, digits only. */\n loginCustomerId?: string;\n}\n\nexport interface SearchOptions {\n /** Stop after this many rows. */\n maxRows?: number;\n}\n\nconst TOKEN_URL = 'https://oauth2.googleapis.com/token';\nconst API_BASE = 'https://googleads.googleapis.com';\n// Matches the API version the reference implementation pinned; bump deliberately.\nexport const DEFAULT_API_VERSION = 'v25';\n\nexport function normalizeCustomerId(id: string): string {\n const normalized = id.replace(/^customers\\//, '').replace(/-/g, '').trim();\n if (!/^\\d{10}$/.test(normalized)) {\n throw new AdportError('INVALID_INPUT', `\"${id}\" is not a valid Google Ads customer id (expected 10 digits).`);\n }\n return normalized;\n}\n\n/**\n * Minimal Google Ads REST client: OAuth token refresh + search + mutate.\n * Deliberately dependency-free — the REST interface is stable and we control\n * exactly what goes over the wire.\n */\nexport class GoogleAdsRestClient {\n private accessToken?: { token: string; expiresAt: number };\n\n constructor(\n private readonly credentials: GoogleCredentials,\n private readonly version: string = process.env.GOOGLE_ADS_API_VERSION ?? DEFAULT_API_VERSION,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {}\n\n async getAccessToken(): Promise<string> {\n if (this.accessToken && this.accessToken.expiresAt > Date.now()) {\n return this.accessToken.token;\n }\n const response = await this.fetchImpl(TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n client_id: this.credentials.clientId,\n client_secret: this.credentials.clientSecret,\n refresh_token: this.credentials.refreshToken,\n grant_type: 'refresh_token',\n }),\n });\n if (!response.ok) {\n const body = await response.text();\n throw new AdportError(\n 'PROVIDER_ERROR',\n `Google OAuth token refresh failed (${response.status}). Re-run \\`adport connect google\\` if the refresh token was revoked.`,\n safeJson(body),\n );\n }\n const data = (await response.json()) as { access_token: string; expires_in: number };\n this.accessToken = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1000 };\n return this.accessToken.token;\n }\n\n private async request<T>(path: string, init: { method: string; body?: unknown }): Promise<T> {\n const token = await this.getAccessToken();\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n 'developer-token': this.credentials.developerToken,\n 'content-type': 'application/json',\n };\n if (this.credentials.loginCustomerId) {\n headers['login-customer-id'] = normalizeCustomerId(this.credentials.loginCustomerId);\n }\n const response = await this.fetchImpl(`${API_BASE}/${this.version}/${path}`, {\n method: init.method,\n headers,\n body: init.body === undefined ? undefined : JSON.stringify(init.body),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', formatGoogleAdsError(response.status, raw), safeJson(raw));\n }\n return (raw ? JSON.parse(raw) : {}) as T;\n }\n\n async listAccessibleCustomers(): Promise<string[]> {\n const data = await this.request<{ resourceNames?: string[] }>('customers:listAccessibleCustomers', {\n method: 'GET',\n });\n return (data.resourceNames ?? []).map((name) => name.replace('customers/', ''));\n }\n\n /** Paged GAQL search; accumulates rows up to options.maxRows (default 1000). */\n async search(\n customerId: string,\n query: string,\n options: SearchOptions = {},\n ): Promise<Array<Record<string, unknown>>> {\n const maxRows = options.maxRows ?? 1000;\n const rows: Array<Record<string, unknown>> = [];\n let pageToken: string | undefined;\n do {\n const data = await this.request<{ results?: Array<Record<string, unknown>>; nextPageToken?: string }>(\n `customers/${normalizeCustomerId(customerId)}/googleAds:search`,\n { method: 'POST', body: { query, pageToken } },\n );\n rows.push(...(data.results ?? []));\n pageToken = data.nextPageToken;\n } while (pageToken && rows.length < maxRows);\n return rows.slice(0, maxRows);\n }\n\n /**\n * Service-specific mutate (campaigns, campaignBudgets, adGroups, adGroupCriteria, adGroupAds).\n * validateOnly=true is the server-side dry run.\n */\n async mutate(\n customerId: string,\n service: string,\n operations: Array<Record<string, unknown>>,\n { validateOnly }: { validateOnly: boolean },\n ): Promise<{ results?: Array<{ resourceName?: string }> }> {\n const body: Record<string, unknown> = { operations, validateOnly };\n // CustomerManagerLinkService does not expose partial_failure in its mutate\n // request, unlike most resource-specific Google Ads mutate services.\n if (service !== 'customerManagerLinks') body.partialFailure = false;\n return this.request(`customers/${normalizeCustomerId(customerId)}/${service}:mutate`, {\n method: 'POST',\n body,\n });\n }\n\n /** Cross-service atomic mutate (e.g. budget + campaign with temp resource ids). */\n async googleAdsMutate(\n customerId: string,\n mutateOperations: Array<Record<string, unknown>>,\n { validateOnly }: { validateOnly: boolean },\n ): Promise<{ mutateOperationResponses?: Array<Record<string, { resourceName?: string }>> }> {\n return this.request(`customers/${normalizeCustomerId(customerId)}/googleAds:mutate`, {\n method: 'POST',\n body: { mutateOperations, validateOnly, partialFailure: false },\n });\n }\n}\n\nfunction safeJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/**\n * Render Google Ads failures with field paths and request id so an agent can\n * self-correct (\"error at operations[0].create.name: ...\").\n */\nexport function formatGoogleAdsError(status: number, raw: string): string {\n const parsed = safeJson(raw) as {\n error?: {\n message?: string;\n details?: Array<{ requestId?: string; errors?: Array<GoogleAdsErrorItem> }>;\n };\n };\n const error = parsed?.error;\n if (!error) return `Google Ads API error (HTTP ${status})`;\n const lines: string[] = [];\n let requestId: string | undefined;\n for (const detail of error.details ?? []) {\n requestId = requestId ?? detail.requestId;\n for (const item of detail.errors ?? []) {\n const path = (item.location?.fieldPathElements ?? [])\n .map((el) => (el.index !== undefined ? `${el.fieldName}[${el.index}]` : el.fieldName))\n .join('.');\n lines.push(`${path ? `at ${path}: ` : ''}${item.message}`);\n }\n }\n const head = `Google Ads API error (HTTP ${status}): ${error.message ?? 'request failed'}`;\n const tail = requestId ? ` [request-id: ${requestId}]` : '';\n return lines.length > 0 ? `${head}\\n ${lines.join('\\n ')}${tail}` : `${head}${tail}`;\n}\n\ninterface GoogleAdsErrorItem {\n message: string;\n location?: { fieldPathElements?: Array<{ fieldName: string; index?: number }> };\n}\n","import {\n AdportError,\n resolveDateRange,\n type Account,\n type AdProvider,\n type MetricName,\n type NormalizedQuery,\n type ProviderCapabilities,\n type Report,\n type ReportRow,\n type StandardActions,\n type WriteGuard,\n type WriteOperation,\n type WritePreview,\n type WriteResult,\n} from '@adport/core';\nimport { GoogleAdsRestClient, normalizeCustomerId } from './client.js';\n\nconst LEVEL_RESOURCE = {\n account: 'customer',\n campaign: 'campaign',\n ad_group: 'ad_group',\n ad: 'ad_group_ad',\n} as const;\n\n/** Base metrics fetched from the API; ratio metrics are derived client-side for consistency. */\nconst BASE_METRIC_FIELDS: Record<string, string> = {\n spend: 'metrics.cost_micros',\n impressions: 'metrics.impressions',\n clicks: 'metrics.clicks',\n conversions: 'metrics.conversions',\n conversion_value: 'metrics.conversions_value',\n};\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: (validateOnly: boolean) => Promise<string[]>;\n}\n\nexport class GoogleAdsProvider implements AdProvider {\n readonly id = 'google';\n\n constructor(private readonly client: GoogleAdsRestClient) {}\n\n capabilities(): ProviderCapabilities {\n return { serverDryRun: true };\n }\n\n standardActions(): StandardActions {\n return {\n pauseCampaign: (accountId, campaignId) => ({\n tool: 'google_set_campaign_status',\n input: { account_id: accountId, campaign_id: campaignId, status: 'PAUSED' },\n }),\n };\n }\n\n async listAccounts(): Promise<Account[]> {\n const ids = await this.client.listAccessibleCustomers();\n const accounts: Account[] = [];\n for (const id of ids) {\n try {\n const rows = await this.client.search(\n id,\n 'SELECT customer.id, customer.descriptive_name, customer.currency_code, customer.status, customer.manager FROM customer LIMIT 1',\n { maxRows: 1 },\n );\n const customer = (rows[0]?.customer ?? {}) as {\n descriptiveName?: string;\n currencyCode?: string;\n status?: string;\n manager?: boolean;\n };\n accounts.push({\n provider: this.id,\n id,\n name: customer.descriptiveName ?? `(account ${id})`,\n currency: customer.currencyCode,\n status: customer.manager ? `${customer.status ?? 'UNKNOWN'} (manager)` : customer.status,\n });\n } catch {\n accounts.push({ provider: this.id, id, name: '(details unavailable)', status: 'UNKNOWN' });\n }\n }\n return accounts;\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n const range = resolveDateRange(query.dateRange);\n const resource = LEVEL_RESOURCE[query.level];\n const accountIds =\n query.accountIds ??\n (await this.listAccounts()).filter((a) => !a.status?.includes('manager')).map((a) => a.id);\n\n const baseMetrics = new Set<string>();\n for (const metric of query.metrics) {\n const field = BASE_METRIC_FIELDS[metric];\n if (field) baseMetrics.add(field);\n }\n // Ratio metrics need their inputs.\n const wants = (m: MetricName) => query.metrics.includes(m);\n if (wants('ctr')) ['metrics.clicks', 'metrics.impressions'].forEach((f) => baseMetrics.add(f));\n if (wants('cpc') || wants('cpa') || wants('cpm') || wants('roas')) baseMetrics.add('metrics.cost_micros');\n if (wants('cpc')) baseMetrics.add('metrics.clicks');\n if (wants('cpm')) baseMetrics.add('metrics.impressions');\n if (wants('cpa')) baseMetrics.add('metrics.conversions');\n if (wants('roas')) baseMetrics.add('metrics.conversions_value');\n\n const entityFields = {\n account: ['customer.id', 'customer.descriptive_name', 'customer.status'],\n campaign: ['campaign.id', 'campaign.name', 'campaign.status'],\n ad_group: ['ad_group.id', 'ad_group.name', 'ad_group.status', 'campaign.name'],\n ad: ['ad_group_ad.ad.id', 'ad_group_ad.status', 'ad_group.name'],\n }[query.level];\n\n const gaql =\n `SELECT ${[...entityFields, ...baseMetrics].join(', ')} FROM ${resource} ` +\n `WHERE segments.date BETWEEN '${range.start}' AND '${range.end}' ` +\n `PARAMETERS omit_unselected_resource_names=true`;\n\n const rows: ReportRow[] = [];\n for (const accountId of accountIds) {\n const results = await this.client.search(accountId, gaql, { maxRows: query.limit ?? 1000 });\n for (const row of results) {\n rows.push(this.toReportRow(row, accountId, query));\n }\n }\n return { rows };\n }\n\n private toReportRow(row: Record<string, unknown>, accountId: string, query: NormalizedQuery): ReportRow {\n const metricsRaw = (row.metrics ?? {}) as Record<string, string | number>;\n const num = (key: string) => Number(metricsRaw[key] ?? 0);\n const spend = num('costMicros') / 1_000_000;\n const impressions = num('impressions');\n const clicks = num('clicks');\n const conversions = num('conversions');\n const conversionValue = num('conversionsValue');\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: round2(conversionValue),\n ctr: impressions > 0 ? round2((clicks / impressions) * 100) : 0,\n cpc: clicks > 0 ? round2(spend / clicks) : 0,\n cpm: impressions > 0 ? round2((spend / impressions) * 1000) : 0,\n cpa: conversions > 0 ? round2(spend / conversions) : 0,\n roas: spend > 0 ? round2(conversionValue / spend) : 0,\n };\n\n let entity: ReportRow['entity'];\n switch (query.level) {\n case 'account': {\n const customer = (row.customer ?? {}) as { id?: string; descriptiveName?: string; status?: string };\n entity = { level: 'account', id: String(customer.id ?? accountId), name: customer.descriptiveName ?? accountId, status: customer.status };\n break;\n }\n case 'campaign': {\n const campaign = (row.campaign ?? {}) as { id?: string; name?: string; status?: string };\n entity = { level: 'campaign', id: String(campaign.id ?? ''), name: campaign.name ?? '', status: campaign.status };\n break;\n }\n case 'ad_group': {\n const adGroup = (row.adGroup ?? {}) as { id?: string; name?: string; status?: string };\n entity = { level: 'ad_group', id: String(adGroup.id ?? ''), name: adGroup.name ?? '', status: adGroup.status };\n break;\n }\n case 'ad': {\n const adGroupAd = (row.adGroupAd ?? {}) as { ad?: { id?: string }; status?: string };\n entity = { level: 'ad', id: String(adGroupAd.ad?.id ?? ''), name: `ad ${adGroupAd.ad?.id ?? '?'}`, status: adGroupAd.status };\n break;\n }\n }\n return {\n provider: this.id,\n accountId,\n entity,\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n };\n }\n\n async gaqlSearch(input: {\n customer_id: string;\n resource: string;\n fields: string[];\n conditions?: string[];\n order_by?: string[];\n limit?: number;\n }): Promise<Array<Record<string, unknown>>> {\n const limit = Math.min(input.limit ?? 200, 10_000);\n let query = `SELECT ${input.fields.join(', ')} FROM ${input.resource}`;\n if (input.conditions?.length) query += ` WHERE ${input.conditions.join(' AND ')}`;\n if (input.order_by?.length) query += ` ORDER BY ${input.order_by.join(', ')}`;\n query += ` LIMIT ${limit} PARAMETERS omit_unselected_resource_names=true`;\n return this.client.search(input.customer_id, query, { maxRows: limit });\n }\n\n async previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview> {\n const plan = await this.plan(op, guard);\n await plan.execute(true); // server-side dry run — throws on invalid operations\n return {\n summary: plan.summary,\n changes: plan.changes,\n coercions: plan.coercions,\n budgetDeltas: plan.budgetDeltas,\n serverValidated: true,\n };\n }\n\n async applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult> {\n const plan = await this.plan(op, guard);\n const resourceIds = await plan.execute(false);\n return { applied: true, resourceIds };\n }\n\n // ---- write planning ------------------------------------------------------\n\n private async plan(op: WriteOperation, guard: WriteGuard): Promise<WritePlan> {\n const cid = normalizeCustomerId(op.accountId);\n // Tool input was already zod-validated at the registry boundary.\n const payload = op.payload as never;\n switch (op.tool) {\n case 'google_create_campaign':\n return this.planCreateCampaign(cid, payload, guard);\n case 'google_set_campaign_status':\n return this.planSetCampaignStatus(cid, payload);\n case 'google_set_budget':\n return this.planSetBudget(cid, payload);\n case 'google_create_ad_group':\n return this.planCreateAdGroup(cid, payload);\n case 'google_set_ad_group_status':\n return this.planSetAdGroupStatus(cid, payload);\n case 'google_add_keywords':\n return this.planAddKeywords(cid, payload);\n case 'google_set_keyword_status':\n return this.planSetKeywordStatus(cid, payload);\n case 'google_remove_keywords':\n return this.planRemoveKeywords(cid, payload);\n case 'google_create_responsive_search_ad':\n return this.planCreateRsa(cid, payload, guard);\n case 'google_set_bid_ceiling':\n return this.planSetBidCeiling(cid, payload);\n case 'google_set_bidding_strategy':\n return this.planSetBiddingStrategy(cid, payload);\n case 'google_api_create':\n return this.planApiCreate(cid, payload, guard);\n case 'google_api_update':\n return this.planApiUpdate(cid, payload);\n case 'google_api_remove':\n return this.planApiRemove(cid, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `google: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planApiCreate(\n cid: string,\n payload: { service: string; creates: Array<Record<string, unknown>> },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const service = validateGoogleService(payload.service);\n const creates = structuredClone(payload.creates);\n validateCustomerReferences(creates, cid);\n const coercions: string[] = [];\n if (guard.forcePausedCreation && service === 'campaigns') {\n for (const [index, create] of creates.entries()) {\n if (create.status !== 'PAUSED') {\n create.status = 'PAUSED';\n coercions.push(`creates[${index}].status coerced to PAUSED by policy (paused_creation)`);\n }\n }\n }\n const budgetDeltas = service === 'campaignBudgets' ? collectGoogleBudgetCreates(creates) : [];\n const operations = creates.map((create) => ({ create }));\n return {\n summary: `Create ${creates.length} Google Ads ${service} resource(s)`,\n changes: creates.map((create) => `+ ${service} ${JSON.stringify(create)}`),\n coercions,\n budgetDeltas,\n execute: async (validateOnly) => {\n const result = await this.client.mutate(cid, service, operations, { validateOnly });\n return (result.results ?? []).flatMap((item) => item.resourceName ? [item.resourceName] : []);\n },\n };\n }\n\n private async planApiUpdate(\n cid: string,\n payload: { service: string; updates: Array<{ update: Record<string, unknown>; update_mask: string }> },\n ): Promise<WritePlan> {\n const service = validateGoogleService(payload.service);\n if (service === 'campaignBudgets' || payload.updates.some((item) => /budget/i.test(item.update_mask))) {\n throw new AdportError(\n 'INVALID_INPUT',\n 'google: budget updates require google_set_budget so the current and proposed values can be policy-checked',\n );\n }\n validateCustomerReferences(payload.updates, cid);\n const operations = payload.updates.map((item) => ({ update: item.update, updateMask: item.update_mask }));\n return {\n summary: `Update ${operations.length} Google Ads ${service} resource(s)`,\n changes: payload.updates.map((item) => `~ ${service} mask=${item.update_mask} ${JSON.stringify(item.update)}`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const result = await this.client.mutate(cid, service, operations, { validateOnly });\n return (result.results ?? []).flatMap((item) => item.resourceName ? [item.resourceName] : []);\n },\n };\n }\n\n private async planApiRemove(\n cid: string,\n payload: { service: string; resource_names: string[] },\n ): Promise<WritePlan> {\n const service = validateGoogleService(payload.service);\n validateCustomerReferences(payload.resource_names, cid);\n const operations = payload.resource_names.map((remove) => ({ remove }));\n return {\n summary: `Permanently remove ${operations.length} Google Ads ${service} resource(s)`,\n changes: payload.resource_names.map((name) => `- ${name}`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const result = await this.client.mutate(cid, service, operations, { validateOnly });\n return (result.results ?? []).map((item, index) => item.resourceName ?? payload.resource_names[index]!);\n },\n };\n }\n\n private async planCreateCampaign(\n cid: string,\n payload: { name: string; daily_budget_micros: number; channel_type?: string; status?: string },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n let status = payload.status ?? 'ENABLED';\n if (guard.forcePausedCreation && status === 'ENABLED') {\n status = 'PAUSED';\n coercions.push('status coerced to PAUSED by policy (paused_creation)');\n }\n const channelType = payload.channel_type ?? 'SEARCH';\n const budgetTempResource = `customers/${cid}/campaignBudgets/-1`;\n const mutateOperations = [\n {\n campaignBudgetOperation: {\n create: {\n resourceName: budgetTempResource,\n name: `Budget for ${payload.name} (${Date.now()})`,\n amountMicros: String(payload.daily_budget_micros),\n deliveryMethod: 'STANDARD',\n explicitlyShared: false,\n },\n },\n },\n {\n campaignOperation: {\n create: {\n name: payload.name,\n status,\n advertisingChannelType: channelType,\n containsEuPoliticalAdvertising: 'DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING',\n manualCpc: {},\n campaignBudget: budgetTempResource,\n },\n },\n },\n ];\n return {\n summary: `Create ${channelType} campaign \"${payload.name}\" (${status}), daily budget ${payload.daily_budget_micros} micros`,\n changes: [\n `+ campaign_budget ${payload.daily_budget_micros} micros/day (not shared)`,\n `+ campaign \"${payload.name}\" status=${status} channel=${channelType} bidding=manual_cpc`,\n ],\n coercions,\n budgetDeltas: [{ target: `new campaign \"${payload.name}\" daily budget`, toMicros: payload.daily_budget_micros }],\n execute: async (validateOnly) => {\n const res = await this.client.googleAdsMutate(cid, mutateOperations, { validateOnly });\n return (res.mutateOperationResponses ?? [])\n .flatMap((r) => Object.values(r).map((v) => v.resourceName))\n .filter((r): r is string => Boolean(r));\n },\n };\n }\n\n private async planSetCampaignStatus(\n cid: string,\n payload: { campaign_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n return {\n summary: `Set campaign \"${current.name}\" status ${current.status} → ${payload.status}`,\n changes: [`~ campaign ${payload.campaign_id} status ${current.status} → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaigns',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planSetBudget(\n cid: string,\n payload: { campaign_id: string; daily_budget_micros: number },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(cid, payload.campaign_id);\n const changes = [\n `~ campaign_budget ${current.budgetResource} amount ${current.budgetMicros} → ${payload.daily_budget_micros}`,\n ];\n if (current.budgetShared) {\n changes.push('! this budget is SHARED — the change affects every campaign using it');\n }\n return {\n summary: `Change \"${current.name}\" daily budget ${current.budgetMicros} → ${payload.daily_budget_micros} micros`,\n changes,\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${current.name}\" daily budget`,\n fromMicros: current.budgetMicros,\n toMicros: payload.daily_budget_micros,\n },\n ],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaignBudgets',\n [\n {\n update: {\n resourceName: current.budgetResource,\n amountMicros: String(payload.daily_budget_micros),\n },\n updateMask: 'amount_micros',\n },\n ],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? current.budgetResource);\n },\n };\n }\n\n private async planCreateAdGroup(\n cid: string,\n payload: { campaign_id: string; name: string; cpc_bid_micros?: number },\n ): Promise<WritePlan> {\n const campaign = await this.lookupCampaign(cid, payload.campaign_id);\n const create: Record<string, unknown> = {\n name: payload.name,\n campaign: `customers/${cid}/campaigns/${payload.campaign_id}`,\n status: 'ENABLED',\n type: 'SEARCH_STANDARD',\n };\n if (payload.cpc_bid_micros) create.cpcBidMicros = String(payload.cpc_bid_micros);\n return {\n summary: `Create ad group \"${payload.name}\" in campaign \"${campaign.name}\"`,\n changes: [`+ ad_group \"${payload.name}\" in campaign ${payload.campaign_id} (ENABLED — inherits campaign state)`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroups', [{ create }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planSetAdGroupStatus(\n cid: string,\n payload: { ad_group_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const resourceName = `customers/${cid}/adGroups/${payload.ad_group_id}`;\n return {\n summary: `Set ad group ${payload.ad_group_id} status → ${payload.status}`,\n changes: [`~ ad_group ${payload.ad_group_id} status → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroups',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planAddKeywords(\n cid: string,\n payload: { ad_group_id: string; keywords: Array<{ text: string; match_type: string }>; negative?: boolean },\n ): Promise<WritePlan> {\n const operations = payload.keywords.map((kw) => ({\n create: {\n adGroup: `customers/${cid}/adGroups/${payload.ad_group_id}`,\n status: 'ENABLED',\n negative: payload.negative ?? false,\n keyword: { text: kw.text, matchType: kw.match_type },\n },\n }));\n const kind = payload.negative ? 'negative keywords' : 'keywords';\n return {\n summary: `Add ${payload.keywords.length} ${kind} to ad group ${payload.ad_group_id}`,\n changes: payload.keywords.map((kw) => `+ ${payload.negative ? 'negative ' : ''}keyword [${kw.match_type}] \"${kw.text}\"`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroupCriteria', operations, { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planSetKeywordStatus(\n cid: string,\n payload: { ad_group_id: string; criterion_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const resourceName = `customers/${cid}/adGroupCriteria/${payload.ad_group_id}~${payload.criterion_id}`;\n return {\n summary: `Set keyword criterion ${payload.criterion_id} status → ${payload.status}`,\n changes: [`~ ad_group_criterion ${payload.ad_group_id}~${payload.criterion_id} status → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroupCriteria',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planRemoveKeywords(\n cid: string,\n payload: { ad_group_id: string; criterion_ids: string[] },\n ): Promise<WritePlan> {\n const resourceNames = payload.criterion_ids.map(\n (id) => `customers/${cid}/adGroupCriteria/${payload.ad_group_id}~${id}`,\n );\n return {\n summary: `PERMANENTLY remove ${resourceNames.length} keyword criteria from ad group ${payload.ad_group_id}`,\n changes: resourceNames.map((r) => `- ${r}`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroupCriteria',\n resourceNames.map((remove) => ({ remove })),\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planCreateRsa(\n cid: string,\n payload: {\n ad_group_id: string;\n headlines: string[];\n descriptions: string[];\n final_urls: string[];\n path1?: string;\n path2?: string;\n },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n validateRsa(payload);\n const coercions: string[] = [];\n // RSAs always start paused (matches the reference implementation) unless policy says otherwise.\n const status = guard.forcePausedCreation ? 'PAUSED' : 'ENABLED';\n if (guard.forcePausedCreation) coercions.push('ad created PAUSED by policy (paused_creation)');\n const create = {\n adGroup: `customers/${cid}/adGroups/${payload.ad_group_id}`,\n status,\n ad: {\n finalUrls: payload.final_urls,\n responsiveSearchAd: {\n headlines: payload.headlines.map((text) => ({ text })),\n descriptions: payload.descriptions.map((text) => ({ text })),\n ...(payload.path1 ? { path1: payload.path1 } : {}),\n ...(payload.path2 ? { path2: payload.path2 } : {}),\n },\n },\n };\n return {\n summary: `Create responsive search ad (${payload.headlines.length} headlines, ${payload.descriptions.length} descriptions) in ad group ${payload.ad_group_id}`,\n changes: [`+ responsive_search_ad in ad_group ${payload.ad_group_id} status=${status}`],\n coercions,\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroupAds', [{ create }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n /** CPC ceilings exist on TARGET_SPEND (Maximize clicks) and TARGET_IMPRESSION_SHARE. */\n private async planSetBidCeiling(\n cid: string,\n payload: { campaign_id: string; cpc_bid_ceiling_micros: number },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaignBidding(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n let update: Record<string, unknown>;\n let updateMask: string;\n if (current.strategyType === 'TARGET_SPEND') {\n update = { resourceName, targetSpend: { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) } };\n updateMask = 'target_spend.cpc_bid_ceiling_micros';\n } else if (current.strategyType === 'TARGET_IMPRESSION_SHARE') {\n update = {\n resourceName,\n targetImpressionShare: { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) },\n };\n updateMask = 'target_impression_share.cpc_bid_ceiling_micros';\n } else {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `google: campaign \"${current.name}\" uses ${current.strategyType} bidding, which has no CPC bid ceiling. ` +\n 'Use google_set_bidding_strategy to switch strategy (e.g. MAXIMIZE_CLICKS supports a ceiling).',\n );\n }\n // Note: bid ceilings are deliberately NOT reported as budgetDeltas — the\n // budget-delta policy cap targets budgets; bid tuning routinely moves >25%.\n return {\n summary:\n `Set \"${current.name}\" CPC bid ceiling ` +\n `${current.cpcBidCeilingMicros ?? '(unset)'} → ${payload.cpc_bid_ceiling_micros} micros (${current.strategyType})`,\n changes: [\n `~ campaign ${payload.campaign_id} ${updateMask} ${current.cpcBidCeilingMicros ?? '(unset)'} → ${payload.cpc_bid_ceiling_micros}`,\n ],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'campaigns', [{ update, updateMask }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planSetBiddingStrategy(\n cid: string,\n payload: {\n campaign_id: string;\n strategy: 'MANUAL_CPC' | 'MAXIMIZE_CLICKS' | 'MAXIMIZE_CONVERSIONS' | 'MAXIMIZE_CONVERSION_VALUE';\n target_cpa_micros?: number;\n target_roas?: number;\n cpc_bid_ceiling_micros?: number;\n },\n ): Promise<WritePlan> {\n if (payload.target_cpa_micros && payload.strategy !== 'MAXIMIZE_CONVERSIONS') {\n throw new AdportError('INVALID_INPUT', 'target_cpa_micros only applies to MAXIMIZE_CONVERSIONS');\n }\n if (payload.target_roas && payload.strategy !== 'MAXIMIZE_CONVERSION_VALUE') {\n throw new AdportError('INVALID_INPUT', 'target_roas only applies to MAXIMIZE_CONVERSION_VALUE');\n }\n if (payload.cpc_bid_ceiling_micros && payload.strategy !== 'MAXIMIZE_CLICKS') {\n throw new AdportError('INVALID_INPUT', 'cpc_bid_ceiling_micros only applies to MAXIMIZE_CLICKS (target spend)');\n }\n const current = await this.lookupCampaignBidding(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n\n let strategyField: Record<string, unknown>;\n let updateMask: string;\n const details: string[] = [];\n switch (payload.strategy) {\n case 'MANUAL_CPC':\n strategyField = { manualCpc: {} };\n updateMask = 'manual_cpc';\n break;\n case 'MAXIMIZE_CLICKS':\n strategyField = {\n targetSpend: payload.cpc_bid_ceiling_micros\n ? { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) }\n : {},\n };\n updateMask = payload.cpc_bid_ceiling_micros ? 'target_spend.cpc_bid_ceiling_micros' : 'target_spend';\n if (payload.cpc_bid_ceiling_micros) details.push(`ceiling ${payload.cpc_bid_ceiling_micros} micros`);\n break;\n case 'MAXIMIZE_CONVERSIONS':\n strategyField = {\n maximizeConversions: payload.target_cpa_micros\n ? { targetCpaMicros: String(payload.target_cpa_micros) }\n : {},\n };\n updateMask = payload.target_cpa_micros ? 'maximize_conversions.target_cpa_micros' : 'maximize_conversions';\n if (payload.target_cpa_micros) details.push(`target CPA ${payload.target_cpa_micros} micros`);\n break;\n case 'MAXIMIZE_CONVERSION_VALUE':\n strategyField = {\n maximizeConversionValue: payload.target_roas ? { targetRoas: payload.target_roas } : {},\n };\n updateMask = payload.target_roas ? 'maximize_conversion_value.target_roas' : 'maximize_conversion_value';\n if (payload.target_roas) details.push(`target ROAS ${payload.target_roas}`);\n break;\n }\n return {\n summary:\n `Switch \"${current.name}\" bidding ${current.strategyType} → ${payload.strategy}` +\n (details.length > 0 ? ` (${details.join(', ')})` : ''),\n changes: [`~ campaign ${payload.campaign_id} bidding_strategy ${current.strategyType} → ${payload.strategy}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaigns',\n [{ update: { resourceName, ...strategyField }, updateMask }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async lookupCampaignBidding(\n cid: string,\n campaignId: string,\n ): Promise<{ name: string; strategyType: string; cpcBidCeilingMicros?: number }> {\n const rows = await this.client.search(\n cid,\n `SELECT campaign.name, campaign.bidding_strategy_type, campaign.target_spend.cpc_bid_ceiling_micros, campaign.target_impression_share.cpc_bid_ceiling_micros FROM campaign WHERE campaign.id = ${Number(campaignId)} LIMIT 1`,\n { maxRows: 1 },\n );\n const row = rows[0];\n if (!row) {\n throw new AdportError('PROVIDER_ERROR', `google: campaign ${campaignId} not found in account ${cid}`);\n }\n const campaign = row.campaign as {\n name?: string;\n biddingStrategyType?: string;\n targetSpend?: { cpcBidCeilingMicros?: string };\n targetImpressionShare?: { cpcBidCeilingMicros?: string };\n };\n const ceiling =\n campaign.targetSpend?.cpcBidCeilingMicros ?? campaign.targetImpressionShare?.cpcBidCeilingMicros;\n return {\n name: campaign.name ?? campaignId,\n strategyType: campaign.biddingStrategyType ?? 'UNKNOWN',\n cpcBidCeilingMicros: ceiling !== undefined ? Number(ceiling) : undefined,\n };\n }\n\n private async lookupCampaign(\n cid: string,\n campaignId: string,\n ): Promise<{ name: string; status: string; budgetResource: string; budgetMicros: number; budgetShared: boolean }> {\n const rows = await this.client.search(\n cid,\n `SELECT campaign.name, campaign.status, campaign.campaign_budget, campaign_budget.resource_name, campaign_budget.amount_micros, campaign_budget.explicitly_shared FROM campaign WHERE campaign.id = ${Number(campaignId)} LIMIT 1`,\n { maxRows: 1 },\n );\n const row = rows[0];\n if (!row) {\n throw new AdportError('PROVIDER_ERROR', `google: campaign ${campaignId} not found in account ${cid}`);\n }\n const campaign = row.campaign as { name?: string; status?: string };\n const budget = (row.campaignBudget ?? {}) as {\n resourceName?: string;\n amountMicros?: string;\n explicitlyShared?: boolean;\n };\n return {\n name: campaign.name ?? campaignId,\n status: campaign.status ?? 'UNKNOWN',\n budgetResource: budget.resourceName ?? '',\n budgetMicros: Number(budget.amountMicros ?? 0),\n budgetShared: budget.explicitlyShared ?? false,\n };\n }\n}\n\nfunction validateRsa(payload: { headlines: string[]; descriptions: string[]; final_urls: string[] }): void {\n const problems: string[] = [];\n if (payload.headlines.length < 3 || payload.headlines.length > 15) problems.push('3–15 headlines required');\n if (payload.descriptions.length < 2 || payload.descriptions.length > 4) problems.push('2–4 descriptions required');\n if (payload.final_urls.length < 1) problems.push('at least one final_url required');\n for (const h of payload.headlines) if (h.length > 30) problems.push(`headline over 30 chars: \"${h}\"`);\n for (const d of payload.descriptions) if (d.length > 90) problems.push(`description over 90 chars: \"${d}\"`);\n if (problems.length > 0) {\n throw new AdportError('INVALID_INPUT', `Responsive search ad invalid: ${problems.join('; ')}`);\n }\n}\n\nfunction validateGoogleService(service: string): string {\n if (!/^[a-z][A-Za-z0-9]{1,80}$/.test(service)) {\n throw new AdportError('INVALID_INPUT', `google: invalid mutate service \"${service}\"`);\n }\n return service;\n}\n\nfunction validateCustomerReferences(value: unknown, customerId: string): void {\n if (Array.isArray(value)) {\n value.forEach((item) => validateCustomerReferences(item, customerId));\n return;\n }\n if (!value || typeof value !== 'object') {\n if (typeof value === 'string') {\n const match = /^customers\\/(\\d+)/.exec(value);\n if (match && match[1] !== customerId) {\n throw new AdportError('INVALID_INPUT', `google: resource reference belongs to customer ${match[1]}, not ${customerId}`);\n }\n }\n return;\n }\n Object.values(value as Record<string, unknown>).forEach((item) => validateCustomerReferences(item, customerId));\n}\n\nfunction collectGoogleBudgetCreates(creates: Array<Record<string, unknown>>): WritePreview['budgetDeltas'] {\n return creates.map((create, index) => {\n const amount = Number(create.amountMicros);\n if (!Number.isSafeInteger(amount) || amount <= 0) {\n throw new AdportError(\n 'INVALID_INPUT',\n `google: campaignBudgets create ${index} must include a positive integer amountMicros for policy checks`,\n );\n }\n return { target: `new campaign budget ${String(create.name ?? index)}`, toMicros: amount };\n });\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { GoogleAdsProvider } from './provider.js';\n\nconst statusSchema = z.enum(['ENABLED', 'PAUSED']);\nconst matchTypeSchema = z.enum(['EXACT', 'PHRASE', 'BROAD']);\n\nexport function googleTools(provider: GoogleAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'google_gaql',\n namespace: 'google',\n description:\n 'Run a structured Google Ads (GAQL) query: pick a resource (campaign, ad_group, keyword_view, change_event, ...), ' +\n 'fields (e.g. campaign.name, metrics.clicks, segments.date), optional conditions and ordering. ' +\n \"Conditions are raw GAQL predicates, e.g. \\\"segments.date DURING LAST_7_DAYS\\\" or \\\"campaign.status = 'ENABLED'\\\".\",\n input: z.object({\n customer_id: z.string().describe('10-digit account id, dashes ok'),\n resource: z.string().min(1),\n fields: z.array(z.string()).min(1),\n conditions: z.array(z.string()).optional(),\n order_by: z.array(z.string()).optional(),\n limit: z.number().int().positive().max(10_000).default(200),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const rows = await provider.gaqlSearch(input);\n return { rows, row_count: rows.length };\n },\n }),\n guardedWriteTool({\n name: 'google_create_campaign',\n namespace: 'google',\n description:\n 'Create a Google Ads campaign with its own (non-shared) daily budget, atomically. Manual CPC bidding; refine later.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n name: z.string().min(1),\n daily_budget_micros: z.number().int().positive().describe('1 currency unit = 1,000,000 micros'),\n channel_type: z.enum(['SEARCH', 'DISPLAY', 'SHOPPING', 'VIDEO', 'PERFORMANCE_MAX']).default('SEARCH'),\n status: statusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_campaign_status',\n namespace: 'google',\n description: 'Enable or pause a campaign.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_set_budget',\n namespace: 'google',\n description: \"Change a campaign's daily budget. The preview warns when the budget is shared across campaigns.\",\n provider: 'google',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), daily_budget_micros: z.number().int().positive() }),\n }),\n guardedWriteTool({\n name: 'google_set_bid_ceiling',\n namespace: 'google',\n description:\n \"Set a campaign's max CPC bid ceiling (micros). Works for MAXIMIZE_CLICKS (target spend) and \" +\n 'TARGET_IMPRESSION_SHARE strategies; fails with guidance for others.',\n provider: 'google',\n kind: 'update',\n payload: z.object({\n campaign_id: z.string(),\n cpc_bid_ceiling_micros: z.number().int().positive().describe('1 currency unit = 1,000,000 micros'),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_bidding_strategy',\n namespace: 'google',\n description:\n \"Switch a campaign's bidding strategy: MANUAL_CPC, MAXIMIZE_CLICKS (optional cpc_bid_ceiling_micros), \" +\n 'MAXIMIZE_CONVERSIONS (optional target_cpa_micros), MAXIMIZE_CONVERSION_VALUE (optional target_roas). ' +\n 'Also updates the target of the current strategy when the strategy stays the same.',\n provider: 'google',\n kind: 'update',\n payload: z.object({\n campaign_id: z.string(),\n strategy: z.enum(['MANUAL_CPC', 'MAXIMIZE_CLICKS', 'MAXIMIZE_CONVERSIONS', 'MAXIMIZE_CONVERSION_VALUE']),\n target_cpa_micros: z.number().int().positive().optional(),\n target_roas: z.number().positive().optional().describe('e.g. 3.5 = 350% return on ad spend'),\n cpc_bid_ceiling_micros: z.number().int().positive().optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_create_ad_group',\n namespace: 'google',\n description: 'Create a SEARCH_STANDARD ad group in a campaign.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n campaign_id: z.string(),\n name: z.string().min(1),\n cpc_bid_micros: z.number().int().positive().optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_ad_group_status',\n namespace: 'google',\n description: 'Enable or pause an ad group.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ ad_group_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_add_keywords',\n namespace: 'google',\n description: 'Add keywords (or negative keywords) to an ad group.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n ad_group_id: z.string(),\n keywords: z.array(z.object({ text: z.string().min(1), match_type: matchTypeSchema })).min(1),\n negative: z.boolean().default(false),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_keyword_status',\n namespace: 'google',\n description: 'Enable or pause a keyword criterion.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ ad_group_id: z.string(), criterion_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_remove_keywords',\n namespace: 'google',\n description: 'PERMANENTLY remove keyword criteria from an ad group. Prefer google_set_keyword_status to pause.',\n provider: 'google',\n kind: 'remove',\n destructive: true,\n payload: z.object({ ad_group_id: z.string(), criterion_ids: z.array(z.string()).min(1) }),\n }),\n guardedWriteTool({\n name: 'google_create_responsive_search_ad',\n namespace: 'google',\n description: 'Create a responsive search ad (3–15 headlines ≤30 chars, 2–4 descriptions ≤90 chars).',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n ad_group_id: z.string(),\n headlines: z.array(z.string()).min(3).max(15),\n descriptions: z.array(z.string()).min(2).max(4),\n final_urls: z.array(z.string().url()).min(1),\n path1: z.string().max(15).optional(),\n path2: z.string().max(15).optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_api_create',\n namespace: 'google',\n description:\n 'Create resources through any Google Ads v25 mutate service using API-shaped create objects. Campaign status is forced to PAUSED and campaign-budget amounts are policy-checked.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n service: z.string().min(2).describe('REST mutate service, e.g. adGroupAds, assets, campaignCriteria'),\n creates: z.array(z.record(z.string(), z.unknown())).min(1).max(100),\n }),\n }),\n guardedWriteTool({\n name: 'google_api_update',\n namespace: 'google',\n description:\n 'Update non-budget resources through any Google Ads v25 mutate service. Budget updates must use google_set_budget.',\n provider: 'google',\n kind: 'update',\n payload: z.object({\n service: z.string().min(2),\n updates: z.array(z.object({\n update: z.record(z.string(), z.unknown()),\n update_mask: z.string().min(1),\n })).min(1).max(100),\n }),\n }),\n guardedWriteTool({\n name: 'google_api_remove',\n namespace: 'google',\n description: 'Permanently remove resources through any Google Ads v25 mutate service.',\n provider: 'google',\n kind: 'remove',\n destructive: true,\n payload: z.object({\n service: z.string().min(2),\n resource_names: z.array(z.string().min(1)).min(1).max(100),\n }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { GoogleAdsRestClient, type GoogleCredentials } from './client.js';\nimport { GoogleAdsProvider } from './provider.js';\nimport { googleTools } from './tools.js';\n\nexport { GoogleAdsRestClient, DEFAULT_API_VERSION, normalizeCustomerId, formatGoogleAdsError, type GoogleCredentials } from './client.js';\nexport { GoogleAdsProvider } from './provider.js';\nexport { googleTools } from './tools.js';\n\n/**\n * Resolve Google credentials: adport credential store first, then the\n * google-ads.yaml-style GOOGLE_ADS_* environment variables.\n */\nexport async function resolveGoogleCredentials(store: CredentialStore): Promise<GoogleCredentials | undefined> {\n const record = await store.get('google');\n if (record) {\n const { developer_token, client_id, client_secret, refresh_token, login_customer_id } = record.data;\n if (developer_token && client_id && client_secret && refresh_token) {\n return {\n developerToken: developer_token,\n clientId: client_id,\n clientSecret: client_secret,\n refreshToken: refresh_token,\n loginCustomerId: login_customer_id || undefined,\n };\n }\n }\n const env = process.env;\n if (env.GOOGLE_ADS_DEVELOPER_TOKEN && env.GOOGLE_ADS_CLIENT_ID && env.GOOGLE_ADS_CLIENT_SECRET && env.GOOGLE_ADS_REFRESH_TOKEN) {\n return {\n developerToken: env.GOOGLE_ADS_DEVELOPER_TOKEN,\n clientId: env.GOOGLE_ADS_CLIENT_ID,\n clientSecret: env.GOOGLE_ADS_CLIENT_SECRET,\n refreshToken: env.GOOGLE_ADS_REFRESH_TOKEN,\n loginCustomerId: env.GOOGLE_ADS_LOGIN_CUSTOMER_ID || undefined,\n };\n }\n return undefined;\n}\n\n/** Provider module for createContext(); undefined when Google isn't connected. */\nexport async function createGoogleModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveGoogleCredentials(store);\n if (!credentials) return undefined;\n const provider = new GoogleAdsProvider(new GoogleAdsRestClient(credentials));\n return { provider, tools: googleTools(provider) };\n}\n"],"mappings":"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","names":["AdportError","AdportError"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/provider.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\n\n/**\n * Matches the google-ads.yaml naming conventions so credentials translate 1:1.\n */\nexport interface GoogleCredentials {\n developerToken: string;\n clientId: string;\n clientSecret: string;\n refreshToken: string;\n /** Manager (MCC) customer id, digits only. */\n loginCustomerId?: string;\n /** Per-operating-customer manager ids for multi-manager hosted runtimes. */\n loginCustomerIds?: Record<string, string>;\n}\n\nexport interface SearchOptions {\n /** Stop after this many rows. */\n maxRows?: number;\n /** Override the manager context for this search; null explicitly omits it. */\n loginCustomerId?: string | null;\n}\n\nconst TOKEN_URL = 'https://oauth2.googleapis.com/token';\nconst API_BASE = 'https://googleads.googleapis.com';\n// Matches the API version the reference implementation pinned; bump deliberately.\nexport const DEFAULT_API_VERSION = 'v25';\n\nexport function normalizeCustomerId(id: string): string {\n const normalized = id.replace(/^customers\\//, '').replace(/-/g, '').trim();\n if (!/^\\d{10}$/.test(normalized)) {\n throw new AdportError('INVALID_INPUT', `\"${id}\" is not a valid Google Ads customer id (expected 10 digits).`);\n }\n return normalized;\n}\n\n/**\n * Minimal Google Ads REST client: OAuth token refresh + search + mutate.\n * Deliberately dependency-free — the REST interface is stable and we control\n * exactly what goes over the wire.\n */\nexport class GoogleAdsRestClient {\n private accessToken?: { token: string; expiresAt: number };\n\n constructor(\n private readonly credentials: GoogleCredentials,\n private readonly version: string = process.env.GOOGLE_ADS_API_VERSION ?? DEFAULT_API_VERSION,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {}\n\n setLoginCustomerId(customerId: string, loginCustomerId: string): void {\n (this.credentials.loginCustomerIds ??= {})[normalizeCustomerId(customerId)] = normalizeCustomerId(loginCustomerId);\n }\n\n async getAccessToken(): Promise<string> {\n if (this.accessToken && this.accessToken.expiresAt > Date.now()) {\n return this.accessToken.token;\n }\n const response = await this.fetchImpl(TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n client_id: this.credentials.clientId,\n client_secret: this.credentials.clientSecret,\n refresh_token: this.credentials.refreshToken,\n grant_type: 'refresh_token',\n }),\n });\n if (!response.ok) {\n const body = await response.text();\n throw new AdportError(\n 'PROVIDER_ERROR',\n `Google OAuth token refresh failed (${response.status}). Re-run \\`adport connect google\\` if the refresh token was revoked.`,\n safeJson(body),\n );\n }\n const data = (await response.json()) as { access_token: string; expires_in: number };\n this.accessToken = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1000 };\n return this.accessToken.token;\n }\n\n private async request<T>(\n path: string,\n init: { method: string; body?: unknown; loginCustomerId?: string | null },\n ): Promise<T> {\n const token = await this.getAccessToken();\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n 'developer-token': this.credentials.developerToken,\n 'content-type': 'application/json',\n };\n const operatingCustomerId = path.match(/^customers\\/(\\d{10})\\//)?.[1];\n const mappedLoginCustomerId = operatingCustomerId\n ? this.credentials.loginCustomerIds?.[operatingCustomerId]\n : undefined;\n const loginCustomerId = init.loginCustomerId === null\n ? undefined\n : init.loginCustomerId ?? mappedLoginCustomerId ?? this.credentials.loginCustomerId;\n if (loginCustomerId) {\n headers['login-customer-id'] = normalizeCustomerId(loginCustomerId);\n }\n const response = await this.fetchImpl(`${API_BASE}/${this.version}/${path}`, {\n method: init.method,\n headers,\n body: init.body === undefined ? undefined : JSON.stringify(init.body),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', formatGoogleAdsError(response.status, raw), safeJson(raw));\n }\n return (raw ? JSON.parse(raw) : {}) as T;\n }\n\n async listAccessibleCustomers(): Promise<string[]> {\n const data = await this.request<{ resourceNames?: string[] }>('customers:listAccessibleCustomers', {\n method: 'GET',\n // Google documents that this call is scoped only by the OAuth user; a\n // manager header has no effect and must not leak between cloud tenants.\n loginCustomerId: null,\n });\n return (data.resourceNames ?? []).map((name) => name.replace('customers/', ''));\n }\n\n /** Paged GAQL search; accumulates rows up to options.maxRows (default 1000). */\n async search(\n customerId: string,\n query: string,\n options: SearchOptions = {},\n ): Promise<Array<Record<string, unknown>>> {\n const maxRows = options.maxRows ?? 1000;\n const rows: Array<Record<string, unknown>> = [];\n let pageToken: string | undefined;\n do {\n const data = await this.request<{ results?: Array<Record<string, unknown>>; nextPageToken?: string }>(\n `customers/${normalizeCustomerId(customerId)}/googleAds:search`,\n { method: 'POST', body: { query, pageToken }, loginCustomerId: options.loginCustomerId },\n );\n rows.push(...(data.results ?? []));\n pageToken = data.nextPageToken;\n } while (pageToken && rows.length < maxRows);\n return rows.slice(0, maxRows);\n }\n\n /**\n * Service-specific mutate (campaigns, campaignBudgets, adGroups, adGroupCriteria, adGroupAds).\n * validateOnly=true is the server-side dry run.\n */\n async mutate(\n customerId: string,\n service: string,\n operations: Array<Record<string, unknown>>,\n { validateOnly }: { validateOnly: boolean },\n ): Promise<{ results?: Array<{ resourceName?: string }> }> {\n const body: Record<string, unknown> = { operations, validateOnly };\n // CustomerManagerLinkService does not expose partial_failure in its mutate\n // request, unlike most resource-specific Google Ads mutate services.\n if (service !== 'customerManagerLinks') body.partialFailure = false;\n return this.request(`customers/${normalizeCustomerId(customerId)}/${service}:mutate`, {\n method: 'POST',\n body,\n });\n }\n\n /** Cross-service atomic mutate (e.g. budget + campaign with temp resource ids). */\n async googleAdsMutate(\n customerId: string,\n mutateOperations: Array<Record<string, unknown>>,\n { validateOnly }: { validateOnly: boolean },\n ): Promise<{ mutateOperationResponses?: Array<Record<string, { resourceName?: string }>> }> {\n return this.request(`customers/${normalizeCustomerId(customerId)}/googleAds:mutate`, {\n method: 'POST',\n body: { mutateOperations, validateOnly, partialFailure: false },\n });\n }\n}\n\nfunction safeJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/**\n * Render Google Ads failures with field paths and request id so an agent can\n * self-correct (\"error at operations[0].create.name: ...\").\n */\nexport function formatGoogleAdsError(status: number, raw: string): string {\n const parsed = safeJson(raw) as {\n error?: {\n message?: string;\n details?: Array<{ requestId?: string; errors?: Array<GoogleAdsErrorItem> }>;\n };\n };\n const error = parsed?.error;\n if (!error) return `Google Ads API error (HTTP ${status})`;\n const lines: string[] = [];\n let requestId: string | undefined;\n for (const detail of error.details ?? []) {\n requestId = requestId ?? detail.requestId;\n for (const item of detail.errors ?? []) {\n const path = (item.location?.fieldPathElements ?? [])\n .map((el) => (el.index !== undefined ? `${el.fieldName}[${el.index}]` : el.fieldName))\n .join('.');\n lines.push(`${path ? `at ${path}: ` : ''}${item.message}`);\n }\n }\n const head = `Google Ads API error (HTTP ${status}): ${error.message ?? 'request failed'}`;\n const tail = requestId ? ` [request-id: ${requestId}]` : '';\n return lines.length > 0 ? `${head}\\n ${lines.join('\\n ')}${tail}` : `${head}${tail}`;\n}\n\ninterface GoogleAdsErrorItem {\n message: string;\n location?: { fieldPathElements?: Array<{ fieldName: string; index?: number }> };\n}\n","import {\n AdportError,\n resolveDateRange,\n type Account,\n type AdProvider,\n type MetricName,\n type NormalizedQuery,\n type ProviderCapabilities,\n type Report,\n type ReportRow,\n type StandardActions,\n type WriteGuard,\n type WriteOperation,\n type WritePreview,\n type WriteResult,\n} from '@adport/core';\nimport { GoogleAdsRestClient, normalizeCustomerId } from './client.js';\n\nconst LEVEL_RESOURCE = {\n account: 'customer',\n campaign: 'campaign',\n ad_group: 'ad_group',\n ad: 'ad_group_ad',\n} as const;\n\n/** Base metrics fetched from the API; ratio metrics are derived client-side for consistency. */\nconst BASE_METRIC_FIELDS: Record<string, string> = {\n spend: 'metrics.cost_micros',\n impressions: 'metrics.impressions',\n clicks: 'metrics.clicks',\n conversions: 'metrics.conversions',\n conversion_value: 'metrics.conversions_value',\n};\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: (validateOnly: boolean) => Promise<string[]>;\n}\n\nexport class GoogleAdsProvider implements AdProvider {\n readonly id = 'google';\n private readonly discoveredLoginCustomerIds = new Map<string, string>();\n\n constructor(private readonly client: GoogleAdsRestClient) {}\n\n capabilities(): ProviderCapabilities {\n return { serverDryRun: true };\n }\n\n standardActions(): StandardActions {\n return {\n pauseCampaign: (accountId, campaignId) => ({\n tool: 'google_set_campaign_status',\n input: { account_id: accountId, campaign_id: campaignId, status: 'PAUSED' },\n }),\n };\n }\n\n async listAccounts(): Promise<Account[]> {\n const ids = await this.client.listAccessibleCustomers();\n const directlyAccessible = new Set(ids);\n const accounts = new Map<string, Account>();\n const managers: string[] = [];\n for (const id of ids) {\n try {\n const rows = await this.client.search(\n id,\n 'SELECT customer.id, customer.descriptive_name, customer.currency_code, customer.status, customer.manager FROM customer LIMIT 1',\n { maxRows: 1, loginCustomerId: id },\n );\n const customer = (rows[0]?.customer ?? {}) as {\n descriptiveName?: string;\n currencyCode?: string;\n status?: string;\n manager?: boolean;\n };\n if (customer.manager) managers.push(id);\n else accounts.set(id, {\n provider: this.id, id, name: customer.descriptiveName ?? `(account ${id})`,\n currency: customer.currencyCode, status: customer.status,\n });\n } catch {\n accounts.set(id, { provider: this.id, id, name: '(details unavailable)', status: 'UNKNOWN' });\n }\n }\n\n for (const rootManagerId of managers) {\n const pending = [rootManagerId];\n const visited = new Set<string>();\n while (pending.length > 0) {\n const managerId = pending.shift()!;\n if (visited.has(managerId)) continue;\n visited.add(managerId);\n const rows = await this.client.search(\n managerId,\n 'SELECT customer_client.client_customer, customer_client.id, customer_client.level, customer_client.manager, customer_client.descriptive_name, customer_client.currency_code, customer_client.status FROM customer_client WHERE customer_client.level <= 1',\n { maxRows: 10_000, loginCustomerId: rootManagerId },\n );\n for (const row of rows) {\n const customer = (row.customerClient ?? {}) as {\n clientCustomer?: string;\n id?: string | number;\n level?: number | string;\n manager?: boolean;\n descriptiveName?: string;\n currencyCode?: string;\n status?: string;\n };\n if (Number(customer.level ?? 0) === 0) continue;\n const customerId = customer.clientCustomer?.replace('customers/', '') ?? String(customer.id ?? '');\n if (!/^\\d{10}$/.test(customerId)) continue;\n if (customer.manager) {\n pending.push(customerId);\n continue;\n }\n if (!directlyAccessible.has(customerId)) {\n this.discoveredLoginCustomerIds.set(customerId, rootManagerId);\n this.client.setLoginCustomerId(customerId, rootManagerId);\n }\n if (!accounts.has(customerId)) accounts.set(customerId, {\n provider: this.id,\n id: customerId,\n name: customer.descriptiveName ?? `(account ${customerId})`,\n currency: customer.currencyCode,\n status: customer.status,\n });\n }\n }\n }\n return [...accounts.values()];\n }\n\n /** Manager headers discovered for client accounts during account listing. */\n loginCustomerIds(): Record<string, string> {\n return Object.fromEntries(this.discoveredLoginCustomerIds);\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n const range = resolveDateRange(query.dateRange);\n const resource = LEVEL_RESOURCE[query.level];\n const accountIds =\n query.accountIds ??\n (await this.listAccounts()).filter((a) => !a.status?.includes('manager')).map((a) => a.id);\n\n const baseMetrics = new Set<string>();\n for (const metric of query.metrics) {\n const field = BASE_METRIC_FIELDS[metric];\n if (field) baseMetrics.add(field);\n }\n // Ratio metrics need their inputs.\n const wants = (m: MetricName) => query.metrics.includes(m);\n if (wants('ctr')) ['metrics.clicks', 'metrics.impressions'].forEach((f) => baseMetrics.add(f));\n if (wants('cpc') || wants('cpa') || wants('cpm') || wants('roas')) baseMetrics.add('metrics.cost_micros');\n if (wants('cpc')) baseMetrics.add('metrics.clicks');\n if (wants('cpm')) baseMetrics.add('metrics.impressions');\n if (wants('cpa')) baseMetrics.add('metrics.conversions');\n if (wants('roas')) baseMetrics.add('metrics.conversions_value');\n\n const entityFields = {\n account: ['customer.id', 'customer.descriptive_name', 'customer.status'],\n campaign: ['campaign.id', 'campaign.name', 'campaign.status'],\n ad_group: ['ad_group.id', 'ad_group.name', 'ad_group.status', 'campaign.name'],\n ad: ['ad_group_ad.ad.id', 'ad_group_ad.status', 'ad_group.name'],\n }[query.level];\n\n const gaql =\n `SELECT ${[...entityFields, ...baseMetrics].join(', ')} FROM ${resource} ` +\n `WHERE segments.date BETWEEN '${range.start}' AND '${range.end}' ` +\n `PARAMETERS omit_unselected_resource_names=true`;\n\n const rows: ReportRow[] = [];\n for (const accountId of accountIds) {\n const results = await this.client.search(accountId, gaql, { maxRows: query.limit ?? 1000 });\n for (const row of results) {\n rows.push(this.toReportRow(row, accountId, query));\n }\n }\n return { rows };\n }\n\n private toReportRow(row: Record<string, unknown>, accountId: string, query: NormalizedQuery): ReportRow {\n const metricsRaw = (row.metrics ?? {}) as Record<string, string | number>;\n const num = (key: string) => Number(metricsRaw[key] ?? 0);\n const spend = num('costMicros') / 1_000_000;\n const impressions = num('impressions');\n const clicks = num('clicks');\n const conversions = num('conversions');\n const conversionValue = num('conversionsValue');\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: round2(conversionValue),\n ctr: impressions > 0 ? round2((clicks / impressions) * 100) : 0,\n cpc: clicks > 0 ? round2(spend / clicks) : 0,\n cpm: impressions > 0 ? round2((spend / impressions) * 1000) : 0,\n cpa: conversions > 0 ? round2(spend / conversions) : 0,\n roas: spend > 0 ? round2(conversionValue / spend) : 0,\n };\n\n let entity: ReportRow['entity'];\n switch (query.level) {\n case 'account': {\n const customer = (row.customer ?? {}) as { id?: string; descriptiveName?: string; status?: string };\n entity = { level: 'account', id: String(customer.id ?? accountId), name: customer.descriptiveName ?? accountId, status: customer.status };\n break;\n }\n case 'campaign': {\n const campaign = (row.campaign ?? {}) as { id?: string; name?: string; status?: string };\n entity = { level: 'campaign', id: String(campaign.id ?? ''), name: campaign.name ?? '', status: campaign.status };\n break;\n }\n case 'ad_group': {\n const adGroup = (row.adGroup ?? {}) as { id?: string; name?: string; status?: string };\n entity = { level: 'ad_group', id: String(adGroup.id ?? ''), name: adGroup.name ?? '', status: adGroup.status };\n break;\n }\n case 'ad': {\n const adGroupAd = (row.adGroupAd ?? {}) as { ad?: { id?: string }; status?: string };\n entity = { level: 'ad', id: String(adGroupAd.ad?.id ?? ''), name: `ad ${adGroupAd.ad?.id ?? '?'}`, status: adGroupAd.status };\n break;\n }\n }\n return {\n provider: this.id,\n accountId,\n entity,\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n };\n }\n\n async gaqlSearch(input: {\n customer_id: string;\n resource: string;\n fields: string[];\n conditions?: string[];\n order_by?: string[];\n limit?: number;\n }): Promise<Array<Record<string, unknown>>> {\n const limit = Math.min(input.limit ?? 200, 10_000);\n let query = `SELECT ${input.fields.join(', ')} FROM ${input.resource}`;\n if (input.conditions?.length) query += ` WHERE ${input.conditions.join(' AND ')}`;\n if (input.order_by?.length) query += ` ORDER BY ${input.order_by.join(', ')}`;\n query += ` LIMIT ${limit} PARAMETERS omit_unselected_resource_names=true`;\n return this.client.search(input.customer_id, query, { maxRows: limit });\n }\n\n async previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview> {\n const plan = await this.plan(op, guard);\n await plan.execute(true); // server-side dry run — throws on invalid operations\n return {\n summary: plan.summary,\n changes: plan.changes,\n coercions: plan.coercions,\n budgetDeltas: plan.budgetDeltas,\n serverValidated: true,\n };\n }\n\n async applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult> {\n const plan = await this.plan(op, guard);\n const resourceIds = await plan.execute(false);\n return { applied: true, resourceIds };\n }\n\n // ---- write planning ------------------------------------------------------\n\n private async plan(op: WriteOperation, guard: WriteGuard): Promise<WritePlan> {\n const cid = normalizeCustomerId(op.accountId);\n // Tool input was already zod-validated at the registry boundary.\n const payload = op.payload as never;\n switch (op.tool) {\n case 'google_create_campaign':\n return this.planCreateCampaign(cid, payload, guard);\n case 'google_set_campaign_status':\n return this.planSetCampaignStatus(cid, payload);\n case 'google_set_budget':\n return this.planSetBudget(cid, payload);\n case 'google_create_ad_group':\n return this.planCreateAdGroup(cid, payload);\n case 'google_set_ad_group_status':\n return this.planSetAdGroupStatus(cid, payload);\n case 'google_add_keywords':\n return this.planAddKeywords(cid, payload);\n case 'google_set_keyword_status':\n return this.planSetKeywordStatus(cid, payload);\n case 'google_remove_keywords':\n return this.planRemoveKeywords(cid, payload);\n case 'google_create_responsive_search_ad':\n return this.planCreateRsa(cid, payload, guard);\n case 'google_set_bid_ceiling':\n return this.planSetBidCeiling(cid, payload);\n case 'google_set_bidding_strategy':\n return this.planSetBiddingStrategy(cid, payload);\n case 'google_api_create':\n return this.planApiCreate(cid, payload, guard);\n case 'google_api_update':\n return this.planApiUpdate(cid, payload);\n case 'google_api_remove':\n return this.planApiRemove(cid, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `google: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planApiCreate(\n cid: string,\n payload: { service: string; creates: Array<Record<string, unknown>> },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const service = validateGoogleService(payload.service);\n const creates = structuredClone(payload.creates);\n validateCustomerReferences(creates, cid);\n const coercions: string[] = [];\n if (guard.forcePausedCreation && service === 'campaigns') {\n for (const [index, create] of creates.entries()) {\n if (create.status !== 'PAUSED') {\n create.status = 'PAUSED';\n coercions.push(`creates[${index}].status coerced to PAUSED by policy (paused_creation)`);\n }\n }\n }\n const budgetDeltas = service === 'campaignBudgets' ? collectGoogleBudgetCreates(creates) : [];\n const operations = creates.map((create) => ({ create }));\n return {\n summary: `Create ${creates.length} Google Ads ${service} resource(s)`,\n changes: creates.map((create) => `+ ${service} ${JSON.stringify(create)}`),\n coercions,\n budgetDeltas,\n execute: async (validateOnly) => {\n const result = await this.client.mutate(cid, service, operations, { validateOnly });\n return (result.results ?? []).flatMap((item) => item.resourceName ? [item.resourceName] : []);\n },\n };\n }\n\n private async planApiUpdate(\n cid: string,\n payload: { service: string; updates: Array<{ update: Record<string, unknown>; update_mask: string }> },\n ): Promise<WritePlan> {\n const service = validateGoogleService(payload.service);\n if (service === 'campaignBudgets' || payload.updates.some((item) => /budget/i.test(item.update_mask))) {\n throw new AdportError(\n 'INVALID_INPUT',\n 'google: budget updates require google_set_budget so the current and proposed values can be policy-checked',\n );\n }\n validateCustomerReferences(payload.updates, cid);\n const operations = payload.updates.map((item) => ({ update: item.update, updateMask: item.update_mask }));\n return {\n summary: `Update ${operations.length} Google Ads ${service} resource(s)`,\n changes: payload.updates.map((item) => `~ ${service} mask=${item.update_mask} ${JSON.stringify(item.update)}`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const result = await this.client.mutate(cid, service, operations, { validateOnly });\n return (result.results ?? []).flatMap((item) => item.resourceName ? [item.resourceName] : []);\n },\n };\n }\n\n private async planApiRemove(\n cid: string,\n payload: { service: string; resource_names: string[] },\n ): Promise<WritePlan> {\n const service = validateGoogleService(payload.service);\n validateCustomerReferences(payload.resource_names, cid);\n const operations = payload.resource_names.map((remove) => ({ remove }));\n return {\n summary: `Permanently remove ${operations.length} Google Ads ${service} resource(s)`,\n changes: payload.resource_names.map((name) => `- ${name}`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const result = await this.client.mutate(cid, service, operations, { validateOnly });\n return (result.results ?? []).map((item, index) => item.resourceName ?? payload.resource_names[index]!);\n },\n };\n }\n\n private async planCreateCampaign(\n cid: string,\n payload: { name: string; daily_budget_micros: number; channel_type?: string; status?: string },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n let status = payload.status ?? 'ENABLED';\n if (guard.forcePausedCreation && status === 'ENABLED') {\n status = 'PAUSED';\n coercions.push('status coerced to PAUSED by policy (paused_creation)');\n }\n const channelType = payload.channel_type ?? 'SEARCH';\n const budgetTempResource = `customers/${cid}/campaignBudgets/-1`;\n const mutateOperations = [\n {\n campaignBudgetOperation: {\n create: {\n resourceName: budgetTempResource,\n name: `Budget for ${payload.name} (${Date.now()})`,\n amountMicros: String(payload.daily_budget_micros),\n deliveryMethod: 'STANDARD',\n explicitlyShared: false,\n },\n },\n },\n {\n campaignOperation: {\n create: {\n name: payload.name,\n status,\n advertisingChannelType: channelType,\n containsEuPoliticalAdvertising: 'DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING',\n manualCpc: {},\n campaignBudget: budgetTempResource,\n },\n },\n },\n ];\n return {\n summary: `Create ${channelType} campaign \"${payload.name}\" (${status}), daily budget ${payload.daily_budget_micros} micros`,\n changes: [\n `+ campaign_budget ${payload.daily_budget_micros} micros/day (not shared)`,\n `+ campaign \"${payload.name}\" status=${status} channel=${channelType} bidding=manual_cpc`,\n ],\n coercions,\n budgetDeltas: [{ target: `new campaign \"${payload.name}\" daily budget`, toMicros: payload.daily_budget_micros }],\n execute: async (validateOnly) => {\n const res = await this.client.googleAdsMutate(cid, mutateOperations, { validateOnly });\n return (res.mutateOperationResponses ?? [])\n .flatMap((r) => Object.values(r).map((v) => v.resourceName))\n .filter((r): r is string => Boolean(r));\n },\n };\n }\n\n private async planSetCampaignStatus(\n cid: string,\n payload: { campaign_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n return {\n summary: `Set campaign \"${current.name}\" status ${current.status} → ${payload.status}`,\n changes: [`~ campaign ${payload.campaign_id} status ${current.status} → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaigns',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planSetBudget(\n cid: string,\n payload: { campaign_id: string; daily_budget_micros: number },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(cid, payload.campaign_id);\n const changes = [\n `~ campaign_budget ${current.budgetResource} amount ${current.budgetMicros} → ${payload.daily_budget_micros}`,\n ];\n if (current.budgetShared) {\n changes.push('! this budget is SHARED — the change affects every campaign using it');\n }\n return {\n summary: `Change \"${current.name}\" daily budget ${current.budgetMicros} → ${payload.daily_budget_micros} micros`,\n changes,\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${current.name}\" daily budget`,\n fromMicros: current.budgetMicros,\n toMicros: payload.daily_budget_micros,\n },\n ],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaignBudgets',\n [\n {\n update: {\n resourceName: current.budgetResource,\n amountMicros: String(payload.daily_budget_micros),\n },\n updateMask: 'amount_micros',\n },\n ],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? current.budgetResource);\n },\n };\n }\n\n private async planCreateAdGroup(\n cid: string,\n payload: { campaign_id: string; name: string; cpc_bid_micros?: number },\n ): Promise<WritePlan> {\n const campaign = await this.lookupCampaign(cid, payload.campaign_id);\n const create: Record<string, unknown> = {\n name: payload.name,\n campaign: `customers/${cid}/campaigns/${payload.campaign_id}`,\n status: 'ENABLED',\n type: 'SEARCH_STANDARD',\n };\n if (payload.cpc_bid_micros) create.cpcBidMicros = String(payload.cpc_bid_micros);\n return {\n summary: `Create ad group \"${payload.name}\" in campaign \"${campaign.name}\"`,\n changes: [`+ ad_group \"${payload.name}\" in campaign ${payload.campaign_id} (ENABLED — inherits campaign state)`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroups', [{ create }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planSetAdGroupStatus(\n cid: string,\n payload: { ad_group_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const resourceName = `customers/${cid}/adGroups/${payload.ad_group_id}`;\n return {\n summary: `Set ad group ${payload.ad_group_id} status → ${payload.status}`,\n changes: [`~ ad_group ${payload.ad_group_id} status → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroups',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planAddKeywords(\n cid: string,\n payload: { ad_group_id: string; keywords: Array<{ text: string; match_type: string }>; negative?: boolean },\n ): Promise<WritePlan> {\n const operations = payload.keywords.map((kw) => ({\n create: {\n adGroup: `customers/${cid}/adGroups/${payload.ad_group_id}`,\n status: 'ENABLED',\n negative: payload.negative ?? false,\n keyword: { text: kw.text, matchType: kw.match_type },\n },\n }));\n const kind = payload.negative ? 'negative keywords' : 'keywords';\n return {\n summary: `Add ${payload.keywords.length} ${kind} to ad group ${payload.ad_group_id}`,\n changes: payload.keywords.map((kw) => `+ ${payload.negative ? 'negative ' : ''}keyword [${kw.match_type}] \"${kw.text}\"`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroupCriteria', operations, { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planSetKeywordStatus(\n cid: string,\n payload: { ad_group_id: string; criterion_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const resourceName = `customers/${cid}/adGroupCriteria/${payload.ad_group_id}~${payload.criterion_id}`;\n return {\n summary: `Set keyword criterion ${payload.criterion_id} status → ${payload.status}`,\n changes: [`~ ad_group_criterion ${payload.ad_group_id}~${payload.criterion_id} status → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroupCriteria',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planRemoveKeywords(\n cid: string,\n payload: { ad_group_id: string; criterion_ids: string[] },\n ): Promise<WritePlan> {\n const resourceNames = payload.criterion_ids.map(\n (id) => `customers/${cid}/adGroupCriteria/${payload.ad_group_id}~${id}`,\n );\n return {\n summary: `PERMANENTLY remove ${resourceNames.length} keyword criteria from ad group ${payload.ad_group_id}`,\n changes: resourceNames.map((r) => `- ${r}`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroupCriteria',\n resourceNames.map((remove) => ({ remove })),\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planCreateRsa(\n cid: string,\n payload: {\n ad_group_id: string;\n headlines: string[];\n descriptions: string[];\n final_urls: string[];\n path1?: string;\n path2?: string;\n },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n validateRsa(payload);\n const coercions: string[] = [];\n // RSAs always start paused (matches the reference implementation) unless policy says otherwise.\n const status = guard.forcePausedCreation ? 'PAUSED' : 'ENABLED';\n if (guard.forcePausedCreation) coercions.push('ad created PAUSED by policy (paused_creation)');\n const create = {\n adGroup: `customers/${cid}/adGroups/${payload.ad_group_id}`,\n status,\n ad: {\n finalUrls: payload.final_urls,\n responsiveSearchAd: {\n headlines: payload.headlines.map((text) => ({ text })),\n descriptions: payload.descriptions.map((text) => ({ text })),\n ...(payload.path1 ? { path1: payload.path1 } : {}),\n ...(payload.path2 ? { path2: payload.path2 } : {}),\n },\n },\n };\n return {\n summary: `Create responsive search ad (${payload.headlines.length} headlines, ${payload.descriptions.length} descriptions) in ad group ${payload.ad_group_id}`,\n changes: [`+ responsive_search_ad in ad_group ${payload.ad_group_id} status=${status}`],\n coercions,\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroupAds', [{ create }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n /** CPC ceilings exist on TARGET_SPEND (Maximize clicks) and TARGET_IMPRESSION_SHARE. */\n private async planSetBidCeiling(\n cid: string,\n payload: { campaign_id: string; cpc_bid_ceiling_micros: number },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaignBidding(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n let update: Record<string, unknown>;\n let updateMask: string;\n if (current.strategyType === 'TARGET_SPEND') {\n update = { resourceName, targetSpend: { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) } };\n updateMask = 'target_spend.cpc_bid_ceiling_micros';\n } else if (current.strategyType === 'TARGET_IMPRESSION_SHARE') {\n update = {\n resourceName,\n targetImpressionShare: { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) },\n };\n updateMask = 'target_impression_share.cpc_bid_ceiling_micros';\n } else {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `google: campaign \"${current.name}\" uses ${current.strategyType} bidding, which has no CPC bid ceiling. ` +\n 'Use google_set_bidding_strategy to switch strategy (e.g. MAXIMIZE_CLICKS supports a ceiling).',\n );\n }\n // Note: bid ceilings are deliberately NOT reported as budgetDeltas — the\n // budget-delta policy cap targets budgets; bid tuning routinely moves >25%.\n return {\n summary:\n `Set \"${current.name}\" CPC bid ceiling ` +\n `${current.cpcBidCeilingMicros ?? '(unset)'} → ${payload.cpc_bid_ceiling_micros} micros (${current.strategyType})`,\n changes: [\n `~ campaign ${payload.campaign_id} ${updateMask} ${current.cpcBidCeilingMicros ?? '(unset)'} → ${payload.cpc_bid_ceiling_micros}`,\n ],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'campaigns', [{ update, updateMask }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planSetBiddingStrategy(\n cid: string,\n payload: {\n campaign_id: string;\n strategy: 'MANUAL_CPC' | 'MAXIMIZE_CLICKS' | 'MAXIMIZE_CONVERSIONS' | 'MAXIMIZE_CONVERSION_VALUE';\n target_cpa_micros?: number;\n target_roas?: number;\n cpc_bid_ceiling_micros?: number;\n },\n ): Promise<WritePlan> {\n if (payload.target_cpa_micros && payload.strategy !== 'MAXIMIZE_CONVERSIONS') {\n throw new AdportError('INVALID_INPUT', 'target_cpa_micros only applies to MAXIMIZE_CONVERSIONS');\n }\n if (payload.target_roas && payload.strategy !== 'MAXIMIZE_CONVERSION_VALUE') {\n throw new AdportError('INVALID_INPUT', 'target_roas only applies to MAXIMIZE_CONVERSION_VALUE');\n }\n if (payload.cpc_bid_ceiling_micros && payload.strategy !== 'MAXIMIZE_CLICKS') {\n throw new AdportError('INVALID_INPUT', 'cpc_bid_ceiling_micros only applies to MAXIMIZE_CLICKS (target spend)');\n }\n const current = await this.lookupCampaignBidding(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n\n let strategyField: Record<string, unknown>;\n let updateMask: string;\n const details: string[] = [];\n switch (payload.strategy) {\n case 'MANUAL_CPC':\n strategyField = { manualCpc: {} };\n updateMask = 'manual_cpc';\n break;\n case 'MAXIMIZE_CLICKS':\n strategyField = {\n targetSpend: payload.cpc_bid_ceiling_micros\n ? { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) }\n : {},\n };\n updateMask = payload.cpc_bid_ceiling_micros ? 'target_spend.cpc_bid_ceiling_micros' : 'target_spend';\n if (payload.cpc_bid_ceiling_micros) details.push(`ceiling ${payload.cpc_bid_ceiling_micros} micros`);\n break;\n case 'MAXIMIZE_CONVERSIONS':\n strategyField = {\n maximizeConversions: payload.target_cpa_micros\n ? { targetCpaMicros: String(payload.target_cpa_micros) }\n : {},\n };\n updateMask = payload.target_cpa_micros ? 'maximize_conversions.target_cpa_micros' : 'maximize_conversions';\n if (payload.target_cpa_micros) details.push(`target CPA ${payload.target_cpa_micros} micros`);\n break;\n case 'MAXIMIZE_CONVERSION_VALUE':\n strategyField = {\n maximizeConversionValue: payload.target_roas ? { targetRoas: payload.target_roas } : {},\n };\n updateMask = payload.target_roas ? 'maximize_conversion_value.target_roas' : 'maximize_conversion_value';\n if (payload.target_roas) details.push(`target ROAS ${payload.target_roas}`);\n break;\n }\n return {\n summary:\n `Switch \"${current.name}\" bidding ${current.strategyType} → ${payload.strategy}` +\n (details.length > 0 ? ` (${details.join(', ')})` : ''),\n changes: [`~ campaign ${payload.campaign_id} bidding_strategy ${current.strategyType} → ${payload.strategy}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaigns',\n [{ update: { resourceName, ...strategyField }, updateMask }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async lookupCampaignBidding(\n cid: string,\n campaignId: string,\n ): Promise<{ name: string; strategyType: string; cpcBidCeilingMicros?: number }> {\n const rows = await this.client.search(\n cid,\n `SELECT campaign.name, campaign.bidding_strategy_type, campaign.target_spend.cpc_bid_ceiling_micros, campaign.target_impression_share.cpc_bid_ceiling_micros FROM campaign WHERE campaign.id = ${Number(campaignId)} LIMIT 1`,\n { maxRows: 1 },\n );\n const row = rows[0];\n if (!row) {\n throw new AdportError('PROVIDER_ERROR', `google: campaign ${campaignId} not found in account ${cid}`);\n }\n const campaign = row.campaign as {\n name?: string;\n biddingStrategyType?: string;\n targetSpend?: { cpcBidCeilingMicros?: string };\n targetImpressionShare?: { cpcBidCeilingMicros?: string };\n };\n const ceiling =\n campaign.targetSpend?.cpcBidCeilingMicros ?? campaign.targetImpressionShare?.cpcBidCeilingMicros;\n return {\n name: campaign.name ?? campaignId,\n strategyType: campaign.biddingStrategyType ?? 'UNKNOWN',\n cpcBidCeilingMicros: ceiling !== undefined ? Number(ceiling) : undefined,\n };\n }\n\n private async lookupCampaign(\n cid: string,\n campaignId: string,\n ): Promise<{ name: string; status: string; budgetResource: string; budgetMicros: number; budgetShared: boolean }> {\n const rows = await this.client.search(\n cid,\n `SELECT campaign.name, campaign.status, campaign.campaign_budget, campaign_budget.resource_name, campaign_budget.amount_micros, campaign_budget.explicitly_shared FROM campaign WHERE campaign.id = ${Number(campaignId)} LIMIT 1`,\n { maxRows: 1 },\n );\n const row = rows[0];\n if (!row) {\n throw new AdportError('PROVIDER_ERROR', `google: campaign ${campaignId} not found in account ${cid}`);\n }\n const campaign = row.campaign as { name?: string; status?: string };\n const budget = (row.campaignBudget ?? {}) as {\n resourceName?: string;\n amountMicros?: string;\n explicitlyShared?: boolean;\n };\n return {\n name: campaign.name ?? campaignId,\n status: campaign.status ?? 'UNKNOWN',\n budgetResource: budget.resourceName ?? '',\n budgetMicros: Number(budget.amountMicros ?? 0),\n budgetShared: budget.explicitlyShared ?? false,\n };\n }\n}\n\nfunction validateRsa(payload: { headlines: string[]; descriptions: string[]; final_urls: string[] }): void {\n const problems: string[] = [];\n if (payload.headlines.length < 3 || payload.headlines.length > 15) problems.push('3–15 headlines required');\n if (payload.descriptions.length < 2 || payload.descriptions.length > 4) problems.push('2–4 descriptions required');\n if (payload.final_urls.length < 1) problems.push('at least one final_url required');\n for (const h of payload.headlines) if (h.length > 30) problems.push(`headline over 30 chars: \"${h}\"`);\n for (const d of payload.descriptions) if (d.length > 90) problems.push(`description over 90 chars: \"${d}\"`);\n if (problems.length > 0) {\n throw new AdportError('INVALID_INPUT', `Responsive search ad invalid: ${problems.join('; ')}`);\n }\n}\n\nfunction validateGoogleService(service: string): string {\n if (!/^[a-z][A-Za-z0-9]{1,80}$/.test(service)) {\n throw new AdportError('INVALID_INPUT', `google: invalid mutate service \"${service}\"`);\n }\n return service;\n}\n\nfunction validateCustomerReferences(value: unknown, customerId: string): void {\n if (Array.isArray(value)) {\n value.forEach((item) => validateCustomerReferences(item, customerId));\n return;\n }\n if (!value || typeof value !== 'object') {\n if (typeof value === 'string') {\n const match = /^customers\\/(\\d+)/.exec(value);\n if (match && match[1] !== customerId) {\n throw new AdportError('INVALID_INPUT', `google: resource reference belongs to customer ${match[1]}, not ${customerId}`);\n }\n }\n return;\n }\n Object.values(value as Record<string, unknown>).forEach((item) => validateCustomerReferences(item, customerId));\n}\n\nfunction collectGoogleBudgetCreates(creates: Array<Record<string, unknown>>): WritePreview['budgetDeltas'] {\n return creates.map((create, index) => {\n const amount = Number(create.amountMicros);\n if (!Number.isSafeInteger(amount) || amount <= 0) {\n throw new AdportError(\n 'INVALID_INPUT',\n `google: campaignBudgets create ${index} must include a positive integer amountMicros for policy checks`,\n );\n }\n return { target: `new campaign budget ${String(create.name ?? index)}`, toMicros: amount };\n });\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { GoogleAdsProvider } from './provider.js';\n\nconst statusSchema = z.enum(['ENABLED', 'PAUSED']);\nconst matchTypeSchema = z.enum(['EXACT', 'PHRASE', 'BROAD']);\n\nexport function googleTools(provider: GoogleAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'google_gaql',\n namespace: 'google',\n description:\n 'Run a structured Google Ads (GAQL) query: pick a resource (campaign, ad_group, keyword_view, change_event, ...), ' +\n 'fields (e.g. campaign.name, metrics.clicks, segments.date), optional conditions and ordering. ' +\n \"Conditions are raw GAQL predicates, e.g. \\\"segments.date DURING LAST_7_DAYS\\\" or \\\"campaign.status = 'ENABLED'\\\".\",\n input: z.object({\n customer_id: z.string().describe('10-digit account id, dashes ok'),\n resource: z.string().min(1),\n fields: z.array(z.string()).min(1),\n conditions: z.array(z.string()).optional(),\n order_by: z.array(z.string()).optional(),\n limit: z.number().int().positive().max(10_000).default(200),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const rows = await provider.gaqlSearch(input);\n return { rows, row_count: rows.length };\n },\n }),\n guardedWriteTool({\n name: 'google_create_campaign',\n namespace: 'google',\n description:\n 'Create a Google Ads campaign with its own (non-shared) daily budget, atomically. Manual CPC bidding; refine later.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n name: z.string().min(1),\n daily_budget_micros: z.number().int().positive().describe('1 currency unit = 1,000,000 micros'),\n channel_type: z.enum(['SEARCH', 'DISPLAY', 'SHOPPING', 'VIDEO', 'PERFORMANCE_MAX']).default('SEARCH'),\n status: statusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_campaign_status',\n namespace: 'google',\n description: 'Enable or pause a campaign.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_set_budget',\n namespace: 'google',\n description: \"Change a campaign's daily budget. The preview warns when the budget is shared across campaigns.\",\n provider: 'google',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), daily_budget_micros: z.number().int().positive() }),\n }),\n guardedWriteTool({\n name: 'google_set_bid_ceiling',\n namespace: 'google',\n description:\n \"Set a campaign's max CPC bid ceiling (micros). Works for MAXIMIZE_CLICKS (target spend) and \" +\n 'TARGET_IMPRESSION_SHARE strategies; fails with guidance for others.',\n provider: 'google',\n kind: 'update',\n payload: z.object({\n campaign_id: z.string(),\n cpc_bid_ceiling_micros: z.number().int().positive().describe('1 currency unit = 1,000,000 micros'),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_bidding_strategy',\n namespace: 'google',\n description:\n \"Switch a campaign's bidding strategy: MANUAL_CPC, MAXIMIZE_CLICKS (optional cpc_bid_ceiling_micros), \" +\n 'MAXIMIZE_CONVERSIONS (optional target_cpa_micros), MAXIMIZE_CONVERSION_VALUE (optional target_roas). ' +\n 'Also updates the target of the current strategy when the strategy stays the same.',\n provider: 'google',\n kind: 'update',\n payload: z.object({\n campaign_id: z.string(),\n strategy: z.enum(['MANUAL_CPC', 'MAXIMIZE_CLICKS', 'MAXIMIZE_CONVERSIONS', 'MAXIMIZE_CONVERSION_VALUE']),\n target_cpa_micros: z.number().int().positive().optional(),\n target_roas: z.number().positive().optional().describe('e.g. 3.5 = 350% return on ad spend'),\n cpc_bid_ceiling_micros: z.number().int().positive().optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_create_ad_group',\n namespace: 'google',\n description: 'Create a SEARCH_STANDARD ad group in a campaign.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n campaign_id: z.string(),\n name: z.string().min(1),\n cpc_bid_micros: z.number().int().positive().optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_ad_group_status',\n namespace: 'google',\n description: 'Enable or pause an ad group.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ ad_group_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_add_keywords',\n namespace: 'google',\n description: 'Add keywords (or negative keywords) to an ad group.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n ad_group_id: z.string(),\n keywords: z.array(z.object({ text: z.string().min(1), match_type: matchTypeSchema })).min(1),\n negative: z.boolean().default(false),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_keyword_status',\n namespace: 'google',\n description: 'Enable or pause a keyword criterion.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ ad_group_id: z.string(), criterion_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_remove_keywords',\n namespace: 'google',\n description: 'PERMANENTLY remove keyword criteria from an ad group. Prefer google_set_keyword_status to pause.',\n provider: 'google',\n kind: 'remove',\n destructive: true,\n payload: z.object({ ad_group_id: z.string(), criterion_ids: z.array(z.string()).min(1) }),\n }),\n guardedWriteTool({\n name: 'google_create_responsive_search_ad',\n namespace: 'google',\n description: 'Create a responsive search ad (3–15 headlines ≤30 chars, 2–4 descriptions ≤90 chars).',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n ad_group_id: z.string(),\n headlines: z.array(z.string()).min(3).max(15),\n descriptions: z.array(z.string()).min(2).max(4),\n final_urls: z.array(z.string().url()).min(1),\n path1: z.string().max(15).optional(),\n path2: z.string().max(15).optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_api_create',\n namespace: 'google',\n description:\n 'Create resources through any Google Ads v25 mutate service using API-shaped create objects. Campaign status is forced to PAUSED and campaign-budget amounts are policy-checked.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n service: z.string().min(2).describe('REST mutate service, e.g. adGroupAds, assets, campaignCriteria'),\n creates: z.array(z.record(z.string(), z.unknown())).min(1).max(100),\n }),\n }),\n guardedWriteTool({\n name: 'google_api_update',\n namespace: 'google',\n description:\n 'Update non-budget resources through any Google Ads v25 mutate service. Budget updates must use google_set_budget.',\n provider: 'google',\n kind: 'update',\n payload: z.object({\n service: z.string().min(2),\n updates: z.array(z.object({\n update: z.record(z.string(), z.unknown()),\n update_mask: z.string().min(1),\n })).min(1).max(100),\n }),\n }),\n guardedWriteTool({\n name: 'google_api_remove',\n namespace: 'google',\n description: 'Permanently remove resources through any Google Ads v25 mutate service.',\n provider: 'google',\n kind: 'remove',\n destructive: true,\n payload: z.object({\n service: z.string().min(2),\n resource_names: z.array(z.string().min(1)).min(1).max(100),\n }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { GoogleAdsRestClient, type GoogleCredentials } from './client.js';\nimport { GoogleAdsProvider } from './provider.js';\nimport { googleTools } from './tools.js';\n\nexport { GoogleAdsRestClient, DEFAULT_API_VERSION, normalizeCustomerId, formatGoogleAdsError, type GoogleCredentials } from './client.js';\nexport { GoogleAdsProvider } from './provider.js';\nexport { googleTools } from './tools.js';\n\n/**\n * Resolve Google credentials: adport credential store first, then the\n * google-ads.yaml-style GOOGLE_ADS_* environment variables.\n */\nexport async function resolveGoogleCredentials(store: CredentialStore): Promise<GoogleCredentials | undefined> {\n const record = await store.get('google');\n if (record) {\n const { developer_token, client_id, client_secret, refresh_token, login_customer_id } = record.data;\n if (developer_token && client_id && client_secret && refresh_token) {\n return {\n developerToken: developer_token,\n clientId: client_id,\n clientSecret: client_secret,\n refreshToken: refresh_token,\n loginCustomerId: login_customer_id || undefined,\n };\n }\n }\n const env = process.env;\n if (env.GOOGLE_ADS_DEVELOPER_TOKEN && env.GOOGLE_ADS_CLIENT_ID && env.GOOGLE_ADS_CLIENT_SECRET && env.GOOGLE_ADS_REFRESH_TOKEN) {\n return {\n developerToken: env.GOOGLE_ADS_DEVELOPER_TOKEN,\n clientId: env.GOOGLE_ADS_CLIENT_ID,\n clientSecret: env.GOOGLE_ADS_CLIENT_SECRET,\n refreshToken: env.GOOGLE_ADS_REFRESH_TOKEN,\n loginCustomerId: env.GOOGLE_ADS_LOGIN_CUSTOMER_ID || undefined,\n };\n }\n return undefined;\n}\n\n/** Provider module for createContext(); undefined when Google isn't connected. */\nexport async function createGoogleModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveGoogleCredentials(store);\n if (!credentials) return undefined;\n const provider = new GoogleAdsProvider(new GoogleAdsRestClient(credentials));\n return { provider, tools: googleTools(provider) };\n}\n"],"mappings":"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","names":["AdportError","AdportError"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adport/provider-google",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Google Ads provider for adport (REST interface, no heavy SDK)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "zod": "^4.4.3",
20
- "@adport/core": "0.5.1"
20
+ "@adport/core": "0.6.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^26.1.2",