This release includes breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Topics
+11 more
Summary
AI summaryUpdates Key fixes, Highlights, and internal across a mixed release.
Full changelog
Highlights
- Meta video creatives, end to end.
meta_ads_creatives_createnow
builds a video creative as well as an image one: passvideo_idplus a
thumbnail and the tool emitsobject_story_spec.video_datainstead of
link_data. Two new read-only tools close the loop —
meta_ads_videos_getreturns the raw nested processing status so an
agent can poll until an upload is ready, andmeta_ads_videos_thumbnails
lists the auto-generated thumbnails to pick one from. Meta tool count
86 -> 88 (205 total). - No more stale "today". Nothing in the stack ever told the agent the
current date, so it inferred one from STATE.json and/daily-checkcould
short-circuit with "today's data is already fetched" against a date days
in the past.mureo_state_getandmureo_strategy_getnow both return
server_now— the server's clock as ISO 8601 with a UTC offset — so
a skill can establish the date from whichever file it reads first, and a
Bash-less headless host never needs to shell out todate. - Date discipline rolled out to 15 skills. Every date-sensitive skill
gains an explicit step-0 "establish today fromserver_now", adapted to
what it computes: reporting windows, goal deadlines, experiment
start/end and the no-peeking gate, and theobservation_duedates that
seven skills write forward from today.generated_atin each persisted
report summary comes from the same value. - Allocation & learning-state diagnostic discipline. The pro-diagnosis
catalogue gains three principles that hold identically on Meta and
Google Ads — judge by marginal efficiency, not averages; respect
learning states (learning-phase entities are Watch, not Action-needed);
and don't hand-optimize inside an auto-allocated unit. Guard notes
pointing back at the section were added acrossdaily-check,
budget-rebalance,audience-review,ad-fatigue-checkand
search-term-cleanup. - Video uploads no longer buffer the whole file in memory.
upload_ad_video_filestreams off disk, so a 1 GB upload no longer
allocates 1 GB of RSS, and the shared retry loop rewinds every seekable
multipart part before each attempt so a 429 provably re-sends an
identical, non-empty body.
Behavior change worth knowing
No breaking changes in this release, but one server-side behavior change:
mureo_state_action_log_append now stamps the entry timestamp from
mureo's own clock. A value supplied by the caller is still accepted by
the schema — the property could not be removed without breaking existing
callers under additionalProperties: false — but is now ignored, and
timestamp is no longer a required input. This closes the loop that let a
drifted model date be persisted and read back later as evidence of the
current date. The other action_log writers (native status toggles, plugin
mutations, creative-studio audits, rollback reversals) and both Creative
Studio manifest.json writers moved onto the same clock, so every entry
now carries a consistent offset-bearing local timestamp.
Key fixes
- Meta video file upload rebuilt on the shared client machinery. It
used to open its ownhttpx.AsyncClientand call a bare
raise_for_status(), so Meta's error JSON (message /error_subcode/
fbtrace_id) was discarded and the caller only saw httpx's generic
400 Bad Request; retries, 429 handling and rate-limit monitoring did
not apply either. It now routes through_post/_request, declares the
real container MIME per extension, moves auth to the Bearer header, and
raises the per-request timeout from 120s to 600s. - Meta video size cap raised from 100 MB to 1 GB, matching Graph's
documented ceiling for the non-resumable upload, and both video upload
tool descriptions now state the real format list and cap instead of the
inaccurate "MP4, MOV. Recommended max 4 GB". - Meta Ads skill Tool Summary table completed. Five shipped tools were
missing from the numbered table, so it listed 81 of 86 tools. It now
covers all 88 and is renumbered sequentially. The### videossection
also documented adescriptionparameter that neither tool accepts.
Added
-
Meta video creatives, end to end.
meta_ads_creatives_createnow builds
a video creative as well as an image one: passvideo_idplus exactly one of
video_thumbnail_image_hash/video_thumbnail_image_urland the tool emits
object_story_spec.video_data(thumbnail,title,message,
link_description, and the destination link nested under
call_to_action.value.link) instead oflink_data. Video and image
parameters are mutually exclusive. Two parameters that are optional for an
image creative are enforced as hard requirements in video mode: a thumbnail
(Meta requires one) andcall_to_action—video_datahas nolinkfield,
so the CTA is the only carrier of the destination and accepting a CTA-less
video would silently discard the requiredlink_url.link_urlis injected
intocall_to_action.value.linkautomatically; callers pass only the button
label. Two read-only tools complete the flow:
meta_ads_videos_getreturns the raw nested processingstatus
(video_status/processing_phase) so an agent can poll until the upload is
ready before creating a creative, andmeta_ads_videos_thumbnailslists
the auto-generated thumbnails (id,uri,is_preferred,height,
width) to pick one from. Both are node-level Graph reads, not
act_-scoped. Meta tool count 86 -> 88 (205 total). -
Cross-platform allocation & learning-state diagnostic discipline. The
pro-diagnosis framework catalogue (skills/_mureo-pro-diagnosis) gains an
Allocation & Learning-State Discipline section covering three principles
that hold identically on Meta and Google Ads: judge by marginal efficiency,
not averages (a bad average is a candidate, not a verdict — saturated
winners scale badly and suppressed losers look worse than they are);
respect learning states (a Meta ad set in its learning phase or a Google
Ads bid strategy in "Learning" is Watch-only, its cost per result is not
steady-state, and edits that reset the clock need a reason worth the reset —
prefer target steps of ~20% or less); and don't hand-optimize inside an
auto-allocated unit (per-breakdown rows are outcomes of the platform's own
allocation, not levers — Smart Bidding ignores manual bid adjustments except
a −100% device opt-out, so cutting an expensive-looking row shrinks the
optimizer's inventory rather than redirecting its budget). Each principle
ships with per-platform manifestations and "what to say instead" phrasing for
recommendations. Guard notes pointing back at the section were added across
the operational skills:daily-check(learning-state entities are Watch, not
Action-needed),budget-rebalance(every reallocation reason must be
marginal, not an average ranking),audience-review(no breakdown-average
exclusions; validate via/experimentand act at the platform's optimization
unit),ad-fatigue-check(learning-phase instability is not creative
fatigue), andsearch-term-cleanup(systematic waste vs low-volume terms with
a bad average)./experimentnow advertises breakdown-exclusion and
reallocation hypotheses as things it validates.
Changed
-
Meta video upload no longer buffers the whole file in memory.
upload_ad_video_filehands httpx the open file handle instead of reading
the file into abytesobject first, so a 1 GB upload streams off disk
rather than allocating 1 GB of RSS. The handle is kept open for the whole
request, and the shared_requestretry loop now rewinds every seekable
part of afilesmapping to byte 0 before each attempt — so a 429 or
transport retry provably re-sends an identical, non-empty body. Behaviour on
the wire is unchanged (sourcepart, real filename, real container MIME,
600s timeout), and the image path (base64bytesform body) is untouched.
Resumable/chunked upload for files above the 1 GB Graph ceiling remains
unimplemented; oversized files still fail validation locally. -
Internal: Meta AdVideo operations split into their own mixin. The video
upload/read machinery (upload_ad_video,upload_ad_video_file,
get_ad_video,list_ad_video_thumbnailsand their constants/MIME map)
moves frommureo/meta_ads/_creatives.pyto a new sibling
mureo/meta_ads/_videos.py(VideosMixin, registered on
MetaAdsApiClient), bringing_creatives.pyfrom 939 lines back under the
800-line budget. The split follows the Graph node each module talks to:
/advideos+/{video-id}here,/adcreativesthere — so the
video-creative build helpers stay withcreate_ad_creativeand neither
module imports the other. Pure move, no behaviour change; every public
client method keeps its name and signature. -
Configure-UI asset split:
auth_wizards_meta.js(internal). The Meta
halves of the auth wizards — the hosted-connector setup card, the
connection-method chooser and the system-user token card — moved out of
auth_wizards.js(1248 lines, over the 800-line house budget) into a new
mureo/_data/web/auth_wizards_meta.js, loaded immediately before it and
reached throughwindow.MUREO_AUTH_META, the same plain-global sharing
pattern the other configure-UI assets already use. A pure move: no renamed
functions, i18n keys, DOM classes or logic, and no behavior change for
operators. -
action_logtimestamps are stamped server-side (#460).
mureo_state_action_log_appendnow writes the entrytimestampfrom
mureo's own clock (ISO 8601 with UTC offset); a value supplied by the caller
is still accepted by the schema — the property could not be removed without
breaking existing callers underadditionalProperties: false— but is
ignored, andtimestampis no longer a required input. This closes the
loop that let a drifted model date be persisted and read back later as
evidence of the current date. The other action_log writers (native status
toggles, plugin mutations, creative-studio audits, rollback reversals) moved
onto the same clock, so every entry now carries a consistent offset-bearing
local timestamp instead of a UTC one. -
Creative Studio generation manifests use the shared clock too (#460).
Thecreated_atfield of bothmanifest.jsonwriters
(creative_studio_generate_visualandcreative_studio_compose) was the
lastdatetime.now(timezone.utc)left in the tree — a second clock in the
same module whose audit entries already usedmureo.core.clock, and one
that rolled the day at the wrong moment for an operator on a positive UTC
offset (an evening run filed under tomorrow). Both now stamp
server_now_iso(), so the manifest and its audit entry agree and the value
is reachable by the same test seam. The only consumer, the read-only
creative gallery, passescreated_atthrough as a display string and orders
runs by run id, so the format change is presentational.
Fixed
-
/daily-checkno longer runs on a stale notion of "today" (#460).
Nothing in the stack ever told the agent the current date, so it inferred
one from STATE.json —reports.daily.period,last_synced_at,action_log
timestamps — and sometimes short-circuited with "today's data is already
fetched, re-displaying" against a date days in the past. The two context
read tools now returnserver_now, the server's clock as ISO 8601 with
a UTC offset (2026-07-28T10:12:33+09:00, host-local because ad operations
reason in local days):mureo_state_getandmureo_strategy_getboth carry
it, so a skill can establish the date from whichever file it reads first —
and a Bash-less headless host never needs to shell out todate.
server_nowis a response-envelope field only: the STATE.json parser
ignores unknown top-level keys, so a copy echoed back into the file is
dropped by the next mureo write instead of fossilising a "today".
daily-check,sync-stateandbudget-pacinggain an explicit first step
that makesserver_nowthe only source of the current date, forbids the
already-fetched short-circuit (a daily check always fetches fresh platform
data for the period ending onserver_now's date), and — for
budget-pacing— derives the pacing month and the elapsed / remaining day
counts from it. -
The same date discipline now covers every date-sensitive skill (#460).
The first three skills were the ones the bug was observed in; twelve more
derive a date from "today" and were equally exposed. Each gains the same
step-0 "establish today fromserver_now", adapted to what it actually
computes:weekly-reportends its 7-day window onserver_now's date
instead of "7 days from today";monthly-reportnames the previous full
calendar month, the MTD cut-off and the explicitYYYY-MM-DD..YYYY-MM-DD
month-before-last range from the clock;goal-reviewmeasures days-remaining
against each Goal'sDeadlinefrom it (a drifted date silently flips
on-track / at-risk / off-track);experimentderivesstart_time,
end_timeand the "has the window closed?" gate from it, so the no-peeking
rule cannot be defeated by an early evaluation; andincident-postmortem
only scores an action whoseobservation_duehas genuinely passed. The seven
skills that write anobservation_duecounted forward from today —
budget-rebalance,creative-refresh,search-term-cleanup,
audience-review,ad-fatigue-check,rescue,tracking-health— now
count it fromserver_now's date, so an outcome review is never scheduled
for the wrong day.generated_atin each persisted report summary comes from
the same value.ad-fatigue-checkadditionally records that its
week-over-week windows are platform-side period tokens (last_7d/
LAST_7_DAYS), resolved by the platform's own clock, and must not be
hand-built as date ranges. -
Meta video file upload rebuilt on the shared client machinery.
meta_ads_videos_upload_fileused to open its ownhttpx.AsyncClientand
call a bareraise_for_status(), so Meta's error JSON (message /
error_subcode/fbtrace_id) was discarded and the caller only saw httpx's
generic400 Bad Request; retries, 429 handling and rate-limit monitoring
did not apply either. The upload now routes through_post/_request(which
regained optionalfiles=multipart support), surfacing Meta's diagnostics
for free. The multipart part keeps its documentedsourcefield name and the
real filename but now declares the real container MIME per extension
(video/mp4,video/quicktime,video/x-msvideo,video/x-ms-wmv,
video/x-matroska) instead ofapplication/octet-stream; the file bytes are
materialized so a retry re-sends the full body; auth moved from an
access_tokenform field to the Bearer header; and the per-request timeout
went from 120s to 600s. -
Meta video size cap raised from 100 MB to 1 GB, matching Graph's
documented ceiling for the non-resumable upload. Both video upload tool
descriptions now state the truth — supported formats MP4 / MOV / AVI / WMV /
MKV, up to 1 GB via these tools, larger files needing resumable upload (not
yet supported) — replacing the inaccurate "MP4, MOV. Recommended max 4 GB". -
.flvno longer accepted by the video upload handler. The MCP handler's
extension tuple allowed.flvwhile the client's allow-list did not, so an
.flvfile passed the handler gate only to be rejected one layer deeper with
a different message. The client set is now the single source of truth. -
Meta Ads skill docs corrected for the video tools: the
### videos
section documented adescriptionparameter that neither tool accepts, and
stated neither the real format list nor the size cap. Thecreatives.create
entry also understated its required parameters (page_id/link_url). -
Meta Ads skill Tool Summary table completed. Five shipped tools were
missing from the numbered table —meta_ads_pages_list,
meta_ads_pages_upload_photo,meta_ads_pixels_create,
meta_ads_targeting_searchandmeta_ads_targeting_categories— so it
listed 81 of 86 tools. The table now covers all 88 and is renumbered
sequentially.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About logly/mureo
Framework for AI agents (Claude Code, Cursor, Codex, Gemini) to operate Google Ads, Meta Ads, and Search Console. Grounded in a local STRATEGY.md — not metric-chasing. Defense-in-depth security, local-first. Apache 2.0.
Related context
Related tools
Beta — feedback welcome: [email protected]