adloop 0.26.0__tar.gz → 0.26.1__tar.gz

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.
Files changed (70) hide show
  1. {adloop-0.26.0 → adloop-0.26.1}/PKG-INFO +19 -2
  2. {adloop-0.26.0 → adloop-0.26.1}/README.md +18 -1
  3. {adloop-0.26.0 → adloop-0.26.1}/pyproject.toml +1 -1
  4. {adloop-0.26.0 → adloop-0.26.1}/pyproject.toml.orig +1 -1
  5. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/adloop.md +1 -1
  6. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/server.py +9 -5
  7. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/__init__.py +0 -0
  8. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/__main__.py +0 -0
  9. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/_mcp_patches.py +0 -0
  10. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/__init__.py +0 -0
  11. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/ai_max.py +0 -0
  12. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/brands.py +0 -0
  13. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/client.py +0 -0
  14. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/conversion_actions.py +0 -0
  15. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/currency.py +0 -0
  16. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/custom_conversion_goals.py +0 -0
  17. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/enums.py +0 -0
  18. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/forecast.py +0 -0
  19. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/gaql.py +0 -0
  20. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/image_fetch.py +0 -0
  21. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/pmax.py +0 -0
  22. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/read.py +0 -0
  23. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/validate_only.py +0 -0
  24. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/write.py +0 -0
  25. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/auth.py +0 -0
  26. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/cli.py +0 -0
  27. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/config.py +0 -0
  28. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/crossref.py +0 -0
  29. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/diagnostics.py +0 -0
  30. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/__init__.py +0 -0
  31. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/client.py +0 -0
  32. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/reports.py +0 -0
  33. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/tracking.py +0 -0
  34. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/write.py +0 -0
  35. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gsc/__init__.py +0 -0
  36. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gsc/client.py +0 -0
  37. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gsc/reports.py +0 -0
  38. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/__init__.py +0 -0
  39. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/client.py +0 -0
  40. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/entities.py +0 -0
  41. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/read.py +0 -0
  42. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/write.py +0 -0
  43. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/merchant/__init__.py +0 -0
  44. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/merchant/client.py +0 -0
  45. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/merchant/read.py +0 -0
  46. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/pagespeed.py +0 -0
  47. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/__init__.py +0 -0
  48. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/auth.py +0 -0
  49. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/client.py +0 -0
  50. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/read.py +0 -0
  51. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/schedule.py +0 -0
  52. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/write.py +0 -0
  53. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/__init__.py +0 -0
  54. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/analyze-performance.md +0 -0
  55. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/budget-plan.md +0 -0
  56. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/create-ad.md +0 -0
  57. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/create-campaign.md +0 -0
  58. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
  59. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/optimize-campaign.md +0 -0
  60. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules_install.py +0 -0
  61. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/runtime.py +0 -0
  62. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/__init__.py +0 -0
  63. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/approval.py +0 -0
  64. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/approval_card.html +0 -0
  65. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/audit.py +0 -0
  66. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/guards.py +0 -0
  67. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/preview.py +0 -0
  68. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/tracking.py +0 -0
  69. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/upload_card.html +0 -0
  70. {adloop-0.26.0 → adloop-0.26.1}/src/adloop/uploads.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: adloop
3
- Version: 0.26.0
3
+ Version: 0.26.1
4
4
  Summary: The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.
5
5
  Keywords: mcp,google-ads,reddit-ads,google-analytics,ga4,cursor,marketing
6
6
  Author: Daniel Klose
@@ -262,6 +262,19 @@ A second ad platform, same safety model. Reddit is a separate connection: its ow
262
262
 
263
263
  > **Setup for Reddit Ads tools** — In Reddit Ads Manager open **Business Manager → Developer Application → Create app** (business admins only; no approval wait). Register the redirect URL exactly as `http://localhost:8765/callback`, then run `adloop init` and complete the Reddit Ads step: it opens Reddit's consent page (scopes `adsread` + `adsedit`, permanent grant), stores the refresh token at `~/.adloop/reddit_token.json`, and lets you pick the default ad account. Reddit rate-limits per user (reporting: 60 requests/min) and requires a descriptive User-Agent, which AdLoop builds from your app id and Reddit username. Reddit has no validate-only mode, so `confirm_and_apply(dry_run=true)` re-reads the target and re-checks the safety caps instead.
264
264
 
265
+ ### Image Uploads
266
+
267
+ An image on your computer, or one you attached in the chat, has no public URL, and an AI app cannot hand the file to a tool. `upload_images` gives it a way in.
268
+
269
+ <p align="center"><img src="https://raw.githubusercontent.com/kLOsk/adloop/main/assets/upload-card.gif" alt="An AdLoop upload card in an AI chat: the user drags a banner onto it, the card shows the upload id, posts it into the chat, and the AI drafts the image asset" width="760"></p>
270
+
271
+ | Tool | What It Does |
272
+ |------|-------------|
273
+ | `upload_images` | Shows an upload card in apps with MCP Apps (drop PNG, JPEG or GIF up to 5 MB); on AdLoop Cloud in other apps it returns a one-time upload link, which a coding agent can also use with `curl -T` |
274
+ | `list_uploads` | The uploads that have not expired yet (kept 24 hours), with id, dimensions and size |
275
+
276
+ The upload ids go into `draft_image_assets(upload_ids=[...])`, or as `upload:<id>` into the image fields of `draft_reddit_ad` on AdLoop Cloud.
277
+
265
278
  ### Planning Tools
266
279
 
267
280
  | Tool | What It Does |
@@ -282,7 +295,7 @@ All write operations follow a **draft → preview → confirm** workflow. Nothin
282
295
  | `draft_responsive_search_ad` | Create RSA preview (3-15 headlines ≤30 chars, 2-4 descriptions ≤90 chars). Warns if headline/description count is below best practice. |
283
296
  | `draft_callouts` | Create campaign callout assets from 1-25 character text snippets. |
284
297
  | `draft_structured_snippets` | Create campaign structured snippet assets using official header values and 3-10 snippet values. |
285
- | `draft_image_assets` | Create campaign image assets from local files or public image URLs (PNG, JPEG, or GIF). |
298
+ | `draft_image_assets` | Create campaign image assets from local files, public image URLs or uploads (PNG, JPEG, or GIF). |
286
299
  | `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy — flags BROAD match on campaigns without Smart Bidding as dangerous in the preview. |
287
300
  | `add_negative_keywords` | Propose negative keywords directly on a campaign |
288
301
  | `add_negative_locations` | Propose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets |
@@ -337,6 +350,10 @@ AdLoop includes pre-built slash commands in `.claude/commands/` for common workf
337
350
 
338
351
  AdLoop manages real ad spend, so safety is not optional.
339
352
 
353
+ <p align="center"><img src="https://raw.githubusercontent.com/kLOsk/adloop/main/assets/approval-card.gif" alt="An AdLoop approval card in an AI chat: the user clicks Test run, Google validates the change, then Apply makes it live and the card reports back to the chat" width="760"></p>
354
+
355
+ - **Approval cards.** In apps that show MCP Apps (Claude, ChatGPT, VS Code and others), every change appears as a card with Test run and Apply. Only the click applies it: the AI's own `confirm_and_apply(dry_run=false)` is refused with `APPROVE_ON_CARD`, whatever it was told in the chat.
356
+ - **Confirmation dialogs.** Apps without cards that support elicitation (Claude Code, for example) get a dialog from AdLoop itself before every real change. The AI cannot answer it; declining changes nothing.
340
357
  - **Two-step writes.** Every mutation returns a preview first. A separate `confirm_and_apply` call is required to execute.
341
358
  - **Dry-run by default.** Even `confirm_and_apply` defaults to `dry_run=true`. Real changes require explicit `dry_run=false`.
342
359
  - **Two-phase apply (optional).** With `safety.two_phase_apply: true`, `confirm_and_apply` refuses `dry_run=false` until the plan has completed one dry-run pass — preview-then-apply becomes server-enforced instead of a convention.
@@ -234,6 +234,19 @@ A second ad platform, same safety model. Reddit is a separate connection: its ow
234
234
 
235
235
  > **Setup for Reddit Ads tools** — In Reddit Ads Manager open **Business Manager → Developer Application → Create app** (business admins only; no approval wait). Register the redirect URL exactly as `http://localhost:8765/callback`, then run `adloop init` and complete the Reddit Ads step: it opens Reddit's consent page (scopes `adsread` + `adsedit`, permanent grant), stores the refresh token at `~/.adloop/reddit_token.json`, and lets you pick the default ad account. Reddit rate-limits per user (reporting: 60 requests/min) and requires a descriptive User-Agent, which AdLoop builds from your app id and Reddit username. Reddit has no validate-only mode, so `confirm_and_apply(dry_run=true)` re-reads the target and re-checks the safety caps instead.
236
236
 
237
+ ### Image Uploads
238
+
239
+ An image on your computer, or one you attached in the chat, has no public URL, and an AI app cannot hand the file to a tool. `upload_images` gives it a way in.
240
+
241
+ <p align="center"><img src="https://raw.githubusercontent.com/kLOsk/adloop/main/assets/upload-card.gif" alt="An AdLoop upload card in an AI chat: the user drags a banner onto it, the card shows the upload id, posts it into the chat, and the AI drafts the image asset" width="760"></p>
242
+
243
+ | Tool | What It Does |
244
+ |------|-------------|
245
+ | `upload_images` | Shows an upload card in apps with MCP Apps (drop PNG, JPEG or GIF up to 5 MB); on AdLoop Cloud in other apps it returns a one-time upload link, which a coding agent can also use with `curl -T` |
246
+ | `list_uploads` | The uploads that have not expired yet (kept 24 hours), with id, dimensions and size |
247
+
248
+ The upload ids go into `draft_image_assets(upload_ids=[...])`, or as `upload:<id>` into the image fields of `draft_reddit_ad` on AdLoop Cloud.
249
+
237
250
  ### Planning Tools
238
251
 
239
252
  | Tool | What It Does |
@@ -254,7 +267,7 @@ All write operations follow a **draft → preview → confirm** workflow. Nothin
254
267
  | `draft_responsive_search_ad` | Create RSA preview (3-15 headlines ≤30 chars, 2-4 descriptions ≤90 chars). Warns if headline/description count is below best practice. |
255
268
  | `draft_callouts` | Create campaign callout assets from 1-25 character text snippets. |
256
269
  | `draft_structured_snippets` | Create campaign structured snippet assets using official header values and 3-10 snippet values. |
257
- | `draft_image_assets` | Create campaign image assets from local files or public image URLs (PNG, JPEG, or GIF). |
270
+ | `draft_image_assets` | Create campaign image assets from local files, public image URLs or uploads (PNG, JPEG, or GIF). |
258
271
  | `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy — flags BROAD match on campaigns without Smart Bidding as dangerous in the preview. |
259
272
  | `add_negative_keywords` | Propose negative keywords directly on a campaign |
260
273
  | `add_negative_locations` | Propose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets |
@@ -309,6 +322,10 @@ AdLoop includes pre-built slash commands in `.claude/commands/` for common workf
309
322
 
310
323
  AdLoop manages real ad spend, so safety is not optional.
311
324
 
325
+ <p align="center"><img src="https://raw.githubusercontent.com/kLOsk/adloop/main/assets/approval-card.gif" alt="An AdLoop approval card in an AI chat: the user clicks Test run, Google validates the change, then Apply makes it live and the card reports back to the chat" width="760"></p>
326
+
327
+ - **Approval cards.** In apps that show MCP Apps (Claude, ChatGPT, VS Code and others), every change appears as a card with Test run and Apply. Only the click applies it: the AI's own `confirm_and_apply(dry_run=false)` is refused with `APPROVE_ON_CARD`, whatever it was told in the chat.
328
+ - **Confirmation dialogs.** Apps without cards that support elicitation (Claude Code, for example) get a dialog from AdLoop itself before every real change. The AI cannot answer it; declining changes nothing.
312
329
  - **Two-step writes.** Every mutation returns a preview first. A separate `confirm_and_apply` call is required to execute.
313
330
  - **Dry-run by default.** Even `confirm_and_apply` defaults to `dry_run=true`. Real changes require explicit `dry_run=false`.
314
331
  - **Two-phase apply (optional).** With `safety.two_phase_apply: true`, `confirm_and_apply` refuses `dry_run=false` until the plan has completed one dry-run pass — preview-then-apply becomes server-enforced instead of a convention.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "adloop"
3
- version = "0.26.0"
3
+ version = "0.26.1"
4
4
  description = "The AI command center for Google Ads, Reddit Ads, GA4, and tracking code."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "adloop"
3
- version = "0.26.0"
3
+ version = "0.26.1"
4
4
  description = "The AI command center for Google Ads, Reddit Ads, GA4, and tracking code."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -227,7 +227,7 @@ Reddit is a second ad platform with its own connection (own OAuth app, no develo
227
227
 
228
228
  ### Image Uploads
229
229
 
230
- For images that have no public URL: a file on the user's computer, or an image they attached in the chat (you can see it, but you cannot pass its bytes to a tool).
230
+ For images that have no public URL: a file on the user's computer, or an image they attached in the chat. **Attaching an image to the chat does not get it into an ad:** you can see it, but no tool can receive it. Never suggest attaching as the way in; offer `upload_images` (card or link) or a public image URL, and when the user already attached one, ask them to drop the same file on the upload card.
231
231
 
232
232
  | Tool | When to Use | Key Parameters |
233
233
  |------|-------------|----------------|
@@ -146,9 +146,12 @@ def _build_orchestration_instructions() -> str:
146
146
  "come back as `row_errors` with DRY_RUN_SUCCESS, and the apply would "
147
147
  "carry out the other rows.\n"
148
148
  "- One change at a time — don't batch unrelated writes.\n"
149
- "- Images without a public URL (a file on the user's computer, an image "
149
+ "- Images without a public URL (a file on the user's computer, or one "
150
150
  "attached in the chat): call `upload_images`, which shows an upload card "
151
- "or returns an upload link, then use the upload ids it yields.\n\n"
151
+ "or returns an upload link, then use the upload ids it yields. An image "
152
+ "attached to the chat cannot be passed to any tool, so don't suggest "
153
+ "attaching as the way to get it into an ad; offer the upload or a public "
154
+ "image URL.\n\n"
152
155
  "PRE-WRITE CHECKS (before any `draft_*`):\n"
153
156
  "- BROAD match keywords require Smart Bidding (MAXIMIZE_CONVERSIONS, "
154
157
  "tCPA, tROAS). Refuse BROAD on MANUAL_CPC. This is the #1 cause of "
@@ -4039,9 +4042,10 @@ def upload_images() -> dict:
4039
4042
  """Let the user upload images (PNG, JPEG, GIF up to 5 MB) for ad assets.
4040
4043
 
4041
4044
  For images without a public URL, such as a file on the user's computer
4042
- or an image attached in the chat. Apps that show AdLoop's cards display
4043
- an upload card where the user drops the files; the card then posts the
4044
- upload ids into the chat. On AdLoop Cloud in other apps the result
4045
+ or an image attached in the chat. An attached image cannot reach a tool,
4046
+ so the user drops the same file on the card. Apps that show AdLoop's
4047
+ cards display an upload card where the user drops the files; the card
4048
+ then posts the upload ids into the chat. On AdLoop Cloud in other apps the result
4045
4049
  carries an upload link for the user (or a coding agent, with curl).
4046
4050
  Uploads expire after a day. The ids go into upload_ids of
4047
4051
  draft_image_assets, or as "upload:<id>" into the image fields of
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes