Skip to content

Keep framework runtime tools out of delegated app calls - #8140

Merged
3mdistal merged 14 commits into
mainfrom
3mdistal/changes-1792557595
Oct 27, 2026
Merged

3mdistal merged 14 commits into
mainfrom
3mdistal/changes-1792557595

Conversation

@3mdistal

@3mdistal 3mdistal commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Connected agents can ask an app to perform work through ask_app or A2A. Those delegated runs currently receive internal framework tools even when direct MCP dispatch excludes them. Several automation management paths also let outside calls create or change jobs with ordinary unattended authority.

This PR gives delegated runs the app's own actions and a bounded static documentation reader, while excluding the framework runtime. The covered framework and app automation actions require outside approval and record provenance; marked runs require approval for each write.

What changed

  • Filter both delegated registries after request-scoped actions merge and before initial tools, tool search, and mid-run expansion are built. Approval replay uses the same boundary. Reuse Restrict direct MCP runtime tools and enforce automation approval #8070's runtime exclusions, including code execution, framework email/fetch/database/browser tools, cross-agent delegation, workspace-file tools, and legacy manage-jobs.
  • Preserve template actions available to the app's agent, including mcpTool: false and server-guarded raw SQL. Reapply agentTool and uiOnly restrictions after request-scoped merges so hidden entries cannot be dispatched by name, searched, or replayed. Analytics' warehouse actions remain available. Keep app definitions on name collisions, including a custom docs-search; use eager skill content when that name is occupied. Required db-*, provider-api-*, and seed-* exclusions remain. The served-registry report lists removals by template and name.
  • Preserve caller: "tool" in delegated execution and approval replay. Carry MCP/A2A network provenance separately into approval contexts and isOutsideAgentCall, so existing template tool behavior stays intact.
  • Keep only the resolved read-only static docs wrapper for bundled instructions, docs and codebase skills. Delegated runs no longer have framework resource/memory tools. Remove generated guidance for unavailable tools; in-app prompt defaults stay unchanged.
  • Require outside approval on the framework automation management actions, Factory's prompt-automation create/save/restore/run actions, and Analytics' index scheduler. Covered creation and edit paths stamp mcpManaged from server context; Factory restore retains the current marker. Factory creation uses bounded conditional inserts without overwriting an existing definition. Single-line automation settings now reject line breaks. Creation stamping in factory-scheduler-job.ts uses the same strict normalization boundary as save and restore. Trusted, unmarked saves and restores of headerless text retain legacy behavior without constructing headers; prompts starting with a header block still undergo frontmatter/provenance validation. Outside or marked writes with incomplete settings return 400 invalid_request before history or persistence; previews remain lenient.
  • Keep template actions such as build-data-index in marked automation runs, while excluding framework runtime tools. Each prepared write still requires approval, including writes without their own gate; persistent grants do not satisfy it. Ordinary in-app definitions remain unmarked; unmarked runs retain their runtime registry and use each action's approval predicate.

Scope and release order

This PR follows merged #8070, reusing its framework markers and prepared-write approval boundary. The branch includes normal merges of main at e9d5ffad and 798991fd, preserving reviewed history. The release order remains #8070, then this PR in the same release, with no core publish between them; #8070 alone leaves delegated calls unchanged.

Through the automation actions covered here, A2A peers need approval to create or change definitions, and marked prompt runs need approval for each write. Unattended authority must come from the person rather than a peer agent. Framework automation management, Factory saves and Analytics index-schedule changes retain enclosing outside-call provenance through isOutsideAutomationOrigin; the shared isOutsideAgentCall helper is unchanged. Existing template approval predicates otherwise remain in place.

Not in this PR

A store-level guarantee that every write path preserves and stamps outside provenance is planned as a separate follow-up. This PR covers the actions listed above.

CLI dispatch and embed-session gating remain in their owning PRs. Direct MCP eligibility and its allowlist are unchanged. This PR does not change Mail's connector catalog, Design's instructions, or catalog selectors/toolsets.

The CRM-to-Clips setup probe on main 4472c549 failed before any automation was created, so delivery was not observed there. A separate app-created personal automation reproduced the missing-org delivery gap. Outcome truthfulness, org-bound personal runs and the setup handoff are separate follow-ups and do not gate this stack.

Verification

The nested-origin repair at 3c51e63f3d3f passes 587 tests: 338 Factory owner tests across eight suites, 20 Analytics scheduler tests, and 229 Core approval/origin/export tests across eight suites. The new cases use real enclosing MCP/A2A frames with trusted inner contexts, require approval before any effect, reject changed inputs, and verify a single approved Factory write retains provenance through the real core parser. The gate controls first reproduced eight intended failures; independently reverting only the Factory provenance change reproduced four marker failures. Core build, Core/Factory/Analytics typechecks, repository lint/format, strict compiler coverage and seven named guards pass. Both shared caller-helper blobs are unchanged. Sol xhigh reviewed the bounded delta and supplied integration snippets with no P1/P2 findings; this was a source-packet review, not independent test execution. The latest Builder review confirms the two nested-origin fixes. The marked-registry comment was dispositioned under the documented delegated policy; all review threads are resolved. All nine required hosted checks are satisfied on this head: six pass and three are not applicable/skipped. Michael Wang's approving review remains in place.

The following verification was recorded on the preceding main-merge snapshot:

The latest main update is 89ce39024fb3, merging 798991fd without manual conflict edits. It passes 402 focused tests: 180 delegated/registry owners, 215 direct-MCP and durable A2A approval tests, two marked/ordinary runner controls, and five public-SDK/Analytics inventory cases. All 41 approval snapshots matched; the committed snapshot comments are preserved. Core build and typecheck, repository lint/format, strict compiler coverage, six named guards and seven positive execution-lease models pass. The shared caller-helper blobs are unchanged. Dependency linking completed; workspace postinstall still stops on the previously observed Toolkit React-type diagnostics, with no Toolkit or lockfile workaround. All nine required hosted checks pass on 89ce39024fb3; the final bounded review also examined the supplied integration snippets.

The following verification was recorded before this latest main merge:

The latest visibility repair passes 180 tests across the complete delegated-runtime, additional-actions, filter and eligibility suites; both marked/ordinary runner controls and all three ordinary Analytics inventory checks also pass. The new regressions reproduced four intended failures on the preceding implementation. Core typecheck, strict compiler coverage, repository lint/format, focused guards and the four positive execution-lease models pass. The final bounded Sol review examined the supplied integration snippets alongside the nested-origin repair.

  • The latest cleanup preserves Factory's ES2020 target with checked indexing; no compiler configuration changes are included. Its normal typecheck, compiler strictness, all 334 Factory owner tests, formatting and repository lint pass. Two new public-SDK approval tests also pass; removing the helper export first produced two intended failures. Mutation-owner discovery now finds all owners, including the public entrypoint. This is not a mutation-score claim, and the mutation runner is unchanged.

  • Main-update checks pass: 199 Core approval/origin tests across four suites, 137 direct-MCP tests across three suites, all 334 Factory tests across eight suites, and all 16 Analytics scheduler tests. The delegated-entry smoke and the marked/unmarked runtime controls also pass individually. This is focused validation, not a full Core-suite pass.

  • Repository formatting and lint pass. The compiler strictness check passes on all three affected projects; the named MCP, source-reading, escape-hatch, assertion, strict-lint and localization guards pass. Execution-lease verification passes its four positive model configurations with all 44 coverage entries intact, without updating coverage.

  • Core build and typecheck passed on the main-merge snapshot before the final type/fixture cleanup. The broader local dependency prebuild stops on a Toolkit React-type identity mismatch; no Toolkit or lockfile workaround is included. All nine required hosted checks pass on 3a227a0477c5; the later bounded Sol review examined the supplied integration snippets.

  • The initial combined Core run was stopped after three failures. The entry smoke passes in isolation, and the two registry cases pass after moving their real dependency loading into setup while retaining the 30-second case budgets and all assertions. Those selected runner checks emitted database-close teardown warnings; that limitation is retained with the local results.

Earlier validation below predates this main update:

  • Before the Factory save/restore repair, the rebased stack passed 808 tests across 23 core suites and 585 tests across 11 MCP suites. These cover catalog modes and aliases, initial/search/expanded registries, real loop dispatch, self/remote routing, durable A2A approvals, exact-input replay, caller semantics and static-reader privacy. That snapshot also passed Core/Toolkit builds, core typecheck, all 50 workspace typechecks, repository lint and all 93 guards.
  • The Factory refusal snapshot passed 308 tests across eight suites after reproducing 105 intended failures with 150 controls passing. The preceding typed 400-response delta passed 219 tests across four affected suites after 86 intended fail-first failures; Factory typecheck, focused lint/format, localization/source-reading guards and LF/diff checks pass on that delta. The saved-metadata control passed before production changes. Analytics' unchanged schedule owner previously passed 16, and the ordinary Analytics registry test passed all three cases.
  • The headerless compatibility repair passes 334 tests across eight Factory suites after reproducing 30 intended failures with 135 controls passing. Ten compatibility cases also execute pinned-main owner modules and match their stored bytes/history/CAS behavior; this is an owner-harness comparison, not deployed-main testing. Factory typecheck, focused lint/format, both i18n guards and source-reading/LF/diff checks pass. A fresh Opus 5.5 xhigh review approves this six-file delta with no P1/P2 findings.
  • The actual-main registry comparison is complete: 17 isolated main processes and the final composite companion capture show no unexpected app-binding losses or identity/route changes, within the report's stated limits.
  • Final targeted staged MCP/localization/source-reading guards pass without skips. Focused review of the final typed-response delta and corrected claims reports no P1/P2 findings; the preceding review held the marker, compatibility, saved-metadata and CAS behavior.

The initial Fast tests lane 2 job on 3c51e63f3d3f reported one Content editor stable-retry assertion (changed instead of prepared), with 7,284 other Content tests passing. The test, editor and flush participant are byte-identical to the merged main base. The exact case passes in isolation and the complete unchanged owner file passes 49/49 locally. Lane 2 passes on its single rerun. No source or assertion changes were made for it.

An additional unchanged scripts/docs/framework-search.spec.ts case fails locally on Windows because its existing URL.pathname path handling cannot find package docs. Standalone reproduction and byte-identical baseline evidence are retained; the required suites above pass. No source or assertion workaround was added; Linux CI remains authoritative for that extra suite.

The changed-Core mutation job on 3c51e63f3d3f repeats the initial-test failure seen on the preceding heads: the relocated Core-only sandbox resolves the template inventory's repository path to packages/core/templates, which is absent there. Stryker instruments 283 candidates but runs no mutants and produces no mutation score. This is the runtime-filesystem selection gap also observed on #8070; the mutation runner and inventory assertions remain unchanged, and the ordinary Analytics inventory passes locally.

Earlier checks on the stacked base encountered the database mock omission fixed by #8120 and the standalone Chat approval/queue race fixed by #8115. The main update includes both fixes; all nine required hosted checks pass on 3a227a0477c5.

The repair ledger preserves exact expectation changes and their reasons. Local specs exercise real registries, agent-loop dispatch, self/remote routing, durable A2A approvals, single-use replay, static-reader privacy, ordinary in-app controls and failing-first regressions. A model asking for approval in prose before issuing a tool call remains separate from the server approval lifecycle and is not claimed fixed here.

Test expectation changes
Fixture / expectation Old → new Reason
automations/service.spec.ts: already-applied change Actor omits caller → caller: "frontend"; exact changed === false remains. Unknown origin now adds provenance; this fixture represents an in-app no-op.
Same spec: lift pause during an active run Omitted caller → frontend; exact enabled/running/error/pause assertions remain. The fixture tests an ordinary edit, while outside takeover of a running definition is refused.
Same spec: concurrent pause/no-op Omitted caller → frontend; exact no-change and disabled-state assertions remain. A provenance change would make an outside call a real edit.
Same spec: retry after resource type changes Omitted caller → frontend; exact 400 and one attempted write remain. The retry tests definition revalidation under in-app authority.
Same spec: retry after app ownership changes Omitted caller → frontend; exact 403 and one attempted write remain. The fixture tests app-access revalidation, without an additional provenance transition.
Same spec: repeated concurrent changes Omitted caller → frontend; exact 409 and no unconditional write remain. The fixture must reach edit retry exhaustion under its intended authority.
audit/attribution.spec.ts: copied approved context Null protocol/id → a2a/task; caller remains tool; no child approval grant. A copied context retains origin without inheriting the one-call approval.
Same spec: context retained after invocation Null protocol/id → a2a/task; exact expired-grant assertions remain. Approval expiry removes authority rather than network provenance.
Same spec: defined child through a raw bridge Null protocol/id → a2a/task; exact missing child grant remains. The bridge preserves origin and isolates approvals.
Same spec: raw child through a raw bridge Null protocol/id → a2a/task; caller remains tool and no child grant appears. Raw dispatch has the same origin and approval-isolation contract.
action-filters-a2a.spec.ts: replay context Caller a2a → tool; require exact A2A protocol/task, bound grant and isOutsideAgentCall(ctx) === true. Tool semantics and outside origin are separate dimensions.
Same spec: replay audit row Audit caller a2a → tool; exact protocol/task/lease/grant fields remain. The audit record must match the actual app-tool invocation.
agent-chat-plugin.surface.spec.ts: native actions Source-slice assertions → real-plugin A2A/MCP behavior in dev and production. Entry identity, initial native schema/prompt and absence of CLI guidance prove the property across registry composition.
agent-chat-plugin.a2a.spec.ts: initial automation tool Two source-spread occurrences → actual registry, initial tools and available tools contain manage-automations. Behavioral coverage proves that composition and filtering retain the tool.
delegated-runtime.spec.ts: ten inline-MCP catalog cases Unknown or unavailable tool: manage-automations → exact Error: Unknown tool "manage-automations"; approval-false/no-definition checks remain. The real loop uses this reply for the intentionally absent inline tool; A2A separately requires a real approval event.
Same spec: dev in-app approval control inAppRun() → inAppRun(true) for nativeActionsInDev; exact result/caller/no-protocol checks remain. The native probe must be registered; default dev CLI controls retain their setup.
Real automation approval-route fixture Guessed tables/missing leases → real organization and run-store migrations. The durable owner/org/input/call/hash/pending/one-replay/duplicate-409 assertions require a real execution fence.
App-collision fixture lifecycle Shared task identifier → separate dev/production task identifiers. A completed task cannot supply the next run lease; registry and replay assertions remain exact.
Factory create/run/save bootstrap First-use initialization inside cases → awaited real module/store setup in beforeAll. Cold initialization finishes before each unchanged 30-second action case; one-effect assertions remain exact.
Factory save dispatch fixture Identity action wrapper → real framework defineAction. The test exercises actual approval binding before persistence effects.
Factory history in-app fixtures Missing context → explicit frontend context. These fixtures represent ordinary in-app work; missing origin retains its conservative classification.
Factory history cleanup fixture Missing cleanup persistence leaf → observable returning/cleanup leaf. The existing orphan-history cleanup on CAS failure and write errors remains asserted.
Factory resource fixture typing Widened creator string → literal "system" as const. This matches the existing resource type without changing the runtime expected value.
Factory typed-refusal fixtures Raw Zod error → real framework error with status 400, invalid_request and affected field. The original fake failure helper discarded status options; real failure transport and all no-write/history assertions are now checked.
Template inventory fixture isolation All templates in one shared process → Analytics default plus explicit isolated template captures. Package action registration is process-global; isolation prevents cross-template contamination without resetting production registries.
Factory header-validation fixtures Untyped throw-only checks → typed 400 invalid_request, plus no history lookup or conditional write. Refusal is limited to outside/marked writes; added trusted, unmarked headerless save/restore cases preserve pinned-main bytes.
New automation-origin owner spec MCP-only no-frame result false → broader outside-origin result true; nested MCP/trusted-frame controls retained and tool-over-MCP/A2A controls added. The companion already classifies an unknown origin conservatively and keeps tool semantics separate from network provenance.
Strict compiler coverage Optional context properties explicitly accept undefined; first-call and header reads are narrowed. These type proofs retain the same context, canonical-marker, metadata and CAS expectations.
Runner registry fixture startup Real imports inside the action cases → real imports in a bounded setup hook; the 30-second case budgets and assertions stay unchanged. Cold dependency loading is separated from the behavior being checked; both marked and ordinary controls pass.
Public SDK approval contract No public-entrypoint coverage → real SDK-backed approval decisions for MCP/A2A with an ordinary-tool control. This protects the helper export independently of its unchanged direct-module unit tests.
Scoped action visibility Keep agentTool: false / uiOnly entries in the final delegated map → reapply app-agent visibility after the merge. Advertising alone does not prevent a model-generated call by name; mcpTool: false and raw-input app controls remain callable.
Scoped-call replay fixture Fixed organization context → the captured run's organization context, retaining the previous default when absent. A caller-bound MCP action must be replayed under its actual request scope; all existing assertions remain.
Scheduler service mock Full trigger-module replacement → partial mock retaining the real origin classifier. The shared Factory normalizer now uses that classifier; the two existing service mocks and every owner assertion remain unchanged.

No assertion was deleted or relaxed.

An app-provided docs-search entry is preserved on both delegated loops instead of being overwritten by the framework reader. The bounded static reader is installed only as a fallback; when the app owns that name, skills are included eagerly and the exact app entry remains callable.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Here's a visual recap of what changed:

Visual recap

Open the full interactive recap

builder-io-integration[bot]

This comment was marked as outdated.

builder-io-integration[bot]

This comment was marked as outdated.

3mdistal added a commit that referenced this pull request Oct 27, 2026
)

Restrict direct MCP runtime exposure and gate the framework automation actions. Retain MCP provenance on definitions and require per-write approval for marked runs.

Delegated containment follows in #8140 in the same release, with no Core publish between them.
Base automatically changed from mcp/close-framework-tool-exposure to main October 27, 2026 01:31
@3mdistal
3mdistal changed the base branch from mcp/close-framework-tool-exposure to main October 27, 2026 01:31
builder-io-integration[bot]

This comment was marked as outdated.

builder-io-integration[bot]

This comment was marked as outdated.

builder-io-integration[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

The latest review confirms the hidden-action fixes in 3a227a0477c5 and reports no additional confirmed issue. The headerless-content thread has the intentional pinned-main compatibility disposition; it is resolved. The two nested-origin approval threads remain open for the pending focused review. No additional source change is needed for this incremental review.

builder-io-integration[bot]

This comment was marked as outdated.

@builder-io-integration builder-io-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Builder reviewed your changes and found 1 potential issue 🟡

Review Details

Code Review Summary

This update continues PR #8140’s effort to keep framework runtime tools out of delegated runs and to carry outside-caller provenance through automation management and approval replay. The approval ancestry changes in Factory save and Analytics schedule now use the shared origin-aware check, and the new replay/context coverage is a solid safeguard. The review threads for those two previously reported nested-approval issues were verified fixed and resolved.

Risk assessment: High — this PR changes approval and delegated execution boundaries. Both independent review passes identified a confirmed regression in the MCP-managed background automation action filter: it replaces the external-agent eligibility policy with only agentTool/uiOnly checks, so explicitly MCP-hidden actions and raw-query inputs can be exposed to unattended automation reads. The new runner spec exercises a mcpTool: false SQL action completing successfully, confirming the widened capability. This should be corrected before merge by retaining the external-agent/MCP eligibility checks while also excluding framework runtime actions.

The review environment could not run tests: setup failed during the toolkit TypeScript build. 🧪 Browser testing: Skipped — PR changes backend/action logic, prompts, and tests, with no user-facing UI impact.

@3mdistal
3mdistal merged commit 3069880 into main Oct 27, 2026
101 of 107 checks passed
@3mdistal
3mdistal deleted the 3mdistal/changes-1792557595 branch October 27, 2026 12:55
3mdistal added a commit that referenced this pull request Oct 27, 2026
#8140 landed the same isOutsideAgentCall helper and its server-entry
export, with its own changeset. What's left here for Core is the
browser-entry export.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3mdistal added a commit that referenced this pull request Oct 27, 2026
3mdistal added a commit that referenced this pull request Oct 27, 2026
#8071 made the A2A approval route read the owner's session with
getBrowserCookieSession. The automation-approval spec from #8140, which
merged first, still mocked only getSession, so on main the route found
no session and its review request returned 401.

The spec now mocks getBrowserCookieSession with the same session. This
is the same hunk #8294 carries, so the two merge cleanly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3mdistal added a commit that referenced this pull request Oct 27, 2026
…ers (#8147)

* Respect recording Trash and comment settings

* Apply recording policies to comment reads

* Apply Trash access to comment cleanup

* Apply comment visibility settings to recording history

* Let Content document readers play and comment on Clips uploaded to the document

* Expire Content playback grants in five minutes and close review gaps

* Test Content Clips workspace resolution, token time fields and lifetime edges

* Reset core's workspace caches between Content Clips tests

* Give each Content Clips workspace test its own reader

* Accept only the Content-Clips peer secret and make signed-out host access opt-in

Clips' Content routes now verify tokens with CONTENT_CLIPS_PEER_SECRET
alone, a core config field read through resolveContentClipsPeerSecret.
Tokens signed with the deployment A2A_SECRET or a workspace secret are
refused, and an unset or unusable secret answers a typed 503
(content_clips_not_configured) without reading or writing anything.
Media grants are signed with a key derived from the peer secret, through
a new signingKey option on core's short-lived tokens.

Host access resolvers are asked about signed-out requests only when they
register with { includeSignedOut: true }; Clips is the only one that does.

* Play Clips inside Content pages behind content.clip-embed

A Clips link pasted into a video block's Link tab becomes a Clip block that
plays in Content's own player, with the Clip's comments as markers on the
scrubber. Content asks Clips for playback and comments over the peer
contract, signed with the Content-Clips peer secret, and checks document
access first, so signed-out readers of public pages can watch too.

Registry blocks now keep their source on the clipboard, so a copied Clip
pastes with its data instead of dropping out of the save.

* Hide Clip blocks from the slash menu, add their parity row, and type unreadable Clips bodies

* Evaluate the Clip flag for a signed-out reader in the page's org

* Let a Clip's loading and error card fill the player's width

* Keep the text toolbar off a selected registry block

* Hold a registry block's place on a public page before its editor mounts, instead of showing its MDX source

* Check every pasted non-Clips link in the browser, and keep a Clip's retry and comment failures visible

* Read at most 500 comment threads from Clips, say when there are more, and keep Clip reads out of the browser's saved cache

* Name each block on a public page before it loads, keep a Clip's place through a failed source, and check pasted blocks

- The public page's first paint parses the saved page and shows each
  registry block by its saved title or registry label; a Clip is a link to
  its recording on Clips, built from the block and deployment config only.
- A source swapped out before its metadata loaded no longer leaves its
  restore behind, and the next source restores the reader's place.
- The toolkit reads a pasted block only through its data attributes, checks
  the type and id format, and keeps a pasted source only when the app reads
  it back. Content keeps one only when it is exactly one registered block of
  the declared type, and view-screen quotes only ids and types Content writes.

* Keep a Clip's grant to its page, cap what Content reads from Clips, count only opened Clips against the rate limit, and drop pasted blocks with no source

- The Clip block's player is keyed by page and recording, so a block that
  ends up under another page never keeps the last page's grant.
- Content reads each Clips answer through a byte cap sized from the
  contract. Clips now cuts a listed comment's text to 280 characters and its
  author's name and email to 320, so the longest list has a known size.
- Clips counts a request against its rate limit only after it has opened the
  recording, so made-up recording ids make no buckets.
- With readPastedSource set, a pasted registry block with no source is
  dropped instead of saving as nothing. Changeset and Content changelog
  updated.

* Limit made-up Clip ids per reader and page, and cut Clip titles

Clips now counts requests whose recording doesn't open against a bucket
per reader and page, and refuses them before any database read once it
is spent, unless the Clip already opened for that reader. A comment
list with no limit gets the most threads the contract allows, and the
playback answer's title is cut to a length both contract files declare.

* Count Clip misses per Clip, so one reader can't block another's first open

A Clip's misses now share its own key, and a Clip is refused when either
its opened bucket or its miss bucket is full. A Clip that opened once and
then went away stays bounded, and made-up ids spend only their own misses.

* Keep a playing Clip going when Clips misses a refresh, and bring it back on focus where the reader left off

* Show unavailable state for missing shared recordings

* Keep unavailable recordings out of share metadata

* Explain replacement of share retry assertions

* Respect recording lifecycle in bug report context

* A full Clip rate-limit map drops its oldest bucket instead of refusing new readers

* Keep recording reactions independent of comments

* Apply recording Trash policy to activity recipients

* Narrow recording context rows before lifecycle checks

* Preserve comment cleanup and recording search availability

* Read optional recording rows consistently across compiler checks

* Format the recording search changelog entry

* Align recording expiry checks across shared access

* Match expiry timestamp precision across recording readers

* Share expiry access rules with agent reads and Slack unfurls

* Apply recording expiry across Clips readers

* Check recording expiry when content completes

* Enforce recording expiry at mutation boundaries

* Revalidate recording expiry before delayed writes

* Preserve expiry checks through queued and locked writes

* Honor linked recording permissions in meeting reads

* Remove unused meeting visibility test import

* Recheck meeting editor access after finalization work

* Meet main's strict-lint and compiler-strictness guards on Content Clips lines

* Add #8129's attachment rule to the delegated-prompt snapshot #8140 landed without it

* Name the prompt behavior the snapshot update records

* Answer a blocked media URL under a Content grant with 502, not the expired-grant 403

* Meet main's strict-lint and compiler-strictness guards on the Clip block's lines

* Answer a storage provider's 403 under a Content grant with 502

* Send a signed-out page reader's Clip comments without commenter emails

* Let a comment author delete through a document whatever case their email was stored in

* Declare what the three Clip actions in Content return

* Set the mixed-case author without a non-null assertion

* Count a Clip request against its limits from the check, so a burst can't all pass it before the first opens the Clip
3mdistal added a commit that referenced this pull request Oct 27, 2026
* Expose Mail draft review to connected agents

* Require fresh approval for draft deletion

* Limit draft deletion approval to outside agents

* Guard delegated draft deletes and preserve existing drafts

* Align caller helper with shared approval policy

* Cover core's isOutsideAgentCall re-export with an export-surface spec

The changed-lines mutation check needs a spec named after every Core file
a branch adds lines to, and index.ts had none. The spec also guards the
export Mail's draft approval check imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Clean up a new Gmail draft when the Mail draft write throws

Creating a draft saves it to Gmail first, then stores the Mail draft with
a compare-and-set. A collision already removed the new Gmail draft, but a
write that threw left it behind with nothing pointing at it.

Now a throw rereads the key. Our own draft there means the write
committed, so create succeeds. Anything else means it didn't, so the
Gmail draft is removed and the original error is rethrown. If the reread
fails too, the Gmail draft stays, since Mail may still point at it.
Cleanup failures are logged instead of replacing the error that matters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Export isOutsideAgentCall from core's browser entry too

Browser builds resolve @agent-native/core to index.browser, which mirrors
the action helpers but didn't have the new check, so a browser bundle
importing it would miss the named export. action-caller has only a type
import, so the browser entry can carry it. The export-surface spec now
checks both entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Give the agent-loop draft spec the compare-and-set create now uses

The spec arrived from main with #8226 and mocks Core's app state without
compareAndSetAppState, so the retried create threw instead of saving.
The mock now inserts only when the key is empty, like the other spec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Narrow the changeset to the browser export main didn't ship

#8140 landed the same isOutsideAgentCall helper and its server-entry
export, with its own changeset. What's left here for Core is the
browser-entry export.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add the attachment rule to the prompt-surface snapshot

#8129 added the "Read the whole attachment" rule to the shared prompt
and wrote it into agent-chat-plugin.surface.spec.ts.snap, which no spec
reads. The spec agent-chat-plugin-surface.spec.ts checks
agent-chat-plugin-surface.spec.ts.snap, so its full-prompt snapshot
fails on main. This adds the one line to the snapshot that spec reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Say a kept Gmail draft exists when a draft's storage can't be confirmed

When both the draft write and its reread fail, create keeps the Gmail
draft it saved. Rethrowing the bare write error hid that, so a direct
caller retrying could make a duplicate. The error now names the kept
Gmail draft and asks the caller to check before creating again, with
the write error as its cause. Core's agent loop already stops on this
case because manage-draft isn't retry-safe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Explain the prompt-surface snapshot change for the oracle guard

The no-oracle-loosening guard flags any changed snapshot body without a
reason. The full-prompt snapshot changed only because #8129's new
attachment rule never reached this snapshot file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reread a thrown draft write for every create, not only Gmail-backed ones

A create with no Gmail draft rethrew a failed compare-and-set without
checking whether it had committed, so a stored draft could be reported
as failed, and a retry with a generated ID would make a duplicate. The
store-and-reread now lives in storeNewComposeDraft and covers every
create. A Gmail draft ID decides ownership when there is one; otherwise
only the exact draft this call wrote counts. An unreadable outcome names
the draft to check either way.

Mail's ES2020 lib has no Error options, so the uncertainty error sets
cause directly instead of through new Error's second argument.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Report an unconfirmed draft store as a typed Mail error

The no-bare-error-in-actions guard requires actions to fail through
fail() rather than a bare Error. When the draft write throws and the
reread also fails, create now fails with draft_store_unconfirmed (503)
and details naming the draft and any Gmail draft it kept. The original
write error is logged, since ES2020's Error has no cause option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Name the Gmail draft left behind when its cleanup fails

When create can't store its draft and removing the Gmail draft it saved
fails too, the caller had no way to learn that an unsent draft stayed in
Gmail. The cleanup helper now reports whether it removed the draft.

A collision keeps its draft_already_exists (409) and adds the leftover
Gmail draft's ID to the message and details. A failed write that leaves
a Gmail draft fails with draft_store_failed (503) naming it, and logs
the original write error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Prove a forged frontend header can't delete every Mail draft

manage-draft lets in-app calls delete drafts without approval, and the
action route used to take a client-set x-agent-native-frontend header as
proof of an in-app call. #8071 made the route require the UI's own proof.

This spec sends delete-all through the real action route with that
header. Without UI proof the route refuses it (403
ui_capability_required); with a bearer token it needs approval (409
approval_required). Either way no draft is read or deleted. Against
core built before #8071, both requests deleted the drafts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Mock the browser session the A2A approval route now reads

#8071 made the A2A approval route read the owner's session with
getBrowserCookieSession. The automation-approval spec from #8140, which
merged first, still mocked only getSession, so on main the route found
no session and its review request returned 401.

The spec now mocks getBrowserCookieSession with the same session. This
is the same hunk #8294 carries, so the two merge cleanly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
meefs pushed a commit to meefs/agent-native that referenced this pull request Oct 27, 2026
…e document (BuilderIO#8090)

* Respect recording Trash and comment settings

* Apply recording policies to comment reads

* Apply Trash access to comment cleanup

* Apply comment visibility settings to recording history

* Let Content document readers play and comment on Clips uploaded to the document

* Expire Content playback grants in five minutes and close review gaps

* Test Content Clips workspace resolution, token time fields and lifetime edges

* Reset core's workspace caches between Content Clips tests

* Give each Content Clips workspace test its own reader

* Accept only the Content-Clips peer secret and make signed-out host access opt-in

Clips' Content routes now verify tokens with CONTENT_CLIPS_PEER_SECRET
alone, a core config field read through resolveContentClipsPeerSecret.
Tokens signed with the deployment A2A_SECRET or a workspace secret are
refused, and an unset or unusable secret answers a typed 503
(content_clips_not_configured) without reading or writing anything.
Media grants are signed with a key derived from the peer secret, through
a new signingKey option on core's short-lived tokens.

Host access resolvers are asked about signed-out requests only when they
register with { includeSignedOut: true }; Clips is the only one that does.

* Show unavailable state for missing shared recordings

* Keep unavailable recordings out of share metadata

* Explain replacement of share retry assertions

* Respect recording lifecycle in bug report context

* Keep recording reactions independent of comments

* Apply recording Trash policy to activity recipients

* Narrow recording context rows before lifecycle checks

* Preserve comment cleanup and recording search availability

* Read optional recording rows consistently across compiler checks

* Format the recording search changelog entry

* Align recording expiry checks across shared access

* Match expiry timestamp precision across recording readers

* Share expiry access rules with agent reads and Slack unfurls

* Apply recording expiry across Clips readers

* Check recording expiry when content completes

* Enforce recording expiry at mutation boundaries

* Revalidate recording expiry before delayed writes

* Preserve expiry checks through queued and locked writes

* Honor linked recording permissions in meeting reads

* Remove unused meeting visibility test import

* Recheck meeting editor access after finalization work

* Meet main's strict-lint and compiler-strictness guards on Content Clips lines

* Add BuilderIO#8129's attachment rule to the delegated-prompt snapshot BuilderIO#8140 landed without it

* Name the prompt behavior the snapshot update records

* Answer a blocked media URL under a Content grant with 502, not the expired-grant 403

* Answer a storage provider's 403 under a Content grant with 502
3mdistal added a commit that referenced this pull request Oct 27, 2026
…e page (#8182)

* Respect recording Trash and comment settings

* Apply recording policies to comment reads

* Apply Trash access to comment cleanup

* Apply comment visibility settings to recording history

* Let Content document readers play and comment on Clips uploaded to the document

* Expire Content playback grants in five minutes and close review gaps

* Test Content Clips workspace resolution, token time fields and lifetime edges

* Reset core's workspace caches between Content Clips tests

* Give each Content Clips workspace test its own reader

* Accept only the Content-Clips peer secret and make signed-out host access opt-in

Clips' Content routes now verify tokens with CONTENT_CLIPS_PEER_SECRET
alone, a core config field read through resolveContentClipsPeerSecret.
Tokens signed with the deployment A2A_SECRET or a workspace secret are
refused, and an unset or unusable secret answers a typed 503
(content_clips_not_configured) without reading or writing anything.
Media grants are signed with a key derived from the peer secret, through
a new signingKey option on core's short-lived tokens.

Host access resolvers are asked about signed-out requests only when they
register with { includeSignedOut: true }; Clips is the only one that does.

* Play Clips inside Content pages behind content.clip-embed

A Clips link pasted into a video block's Link tab becomes a Clip block that
plays in Content's own player, with the Clip's comments as markers on the
scrubber. Content asks Clips for playback and comments over the peer
contract, signed with the Content-Clips peer secret, and checks document
access first, so signed-out readers of public pages can watch too.

Registry blocks now keep their source on the clipboard, so a copied Clip
pastes with its data instead of dropping out of the save.

* Hide Clip blocks from the slash menu, add their parity row, and type unreadable Clips bodies

* Evaluate the Clip flag for a signed-out reader in the page's org

* Let a Clip's loading and error card fill the player's width

* Keep the text toolbar off a selected registry block

* Hold a registry block's place on a public page before its editor mounts, instead of showing its MDX source

* Check every pasted non-Clips link in the browser, and keep a Clip's retry and comment failures visible

* Read at most 500 comment threads from Clips, say when there are more, and keep Clip reads out of the browser's saved cache

* Name each block on a public page before it loads, keep a Clip's place through a failed source, and check pasted blocks

- The public page's first paint parses the saved page and shows each
  registry block by its saved title or registry label; a Clip is a link to
  its recording on Clips, built from the block and deployment config only.
- A source swapped out before its metadata loaded no longer leaves its
  restore behind, and the next source restores the reader's place.
- The toolkit reads a pasted block only through its data attributes, checks
  the type and id format, and keeps a pasted source only when the app reads
  it back. Content keeps one only when it is exactly one registered block of
  the declared type, and view-screen quotes only ids and types Content writes.

* Keep a Clip's grant to its page, cap what Content reads from Clips, count only opened Clips against the rate limit, and drop pasted blocks with no source

- The Clip block's player is keyed by page and recording, so a block that
  ends up under another page never keeps the last page's grant.
- Content reads each Clips answer through a byte cap sized from the
  contract. Clips now cuts a listed comment's text to 280 characters and its
  author's name and email to 320, so the longest list has a known size.
- Clips counts a request against its rate limit only after it has opened the
  recording, so made-up recording ids make no buckets.
- With readPastedSource set, a pasted registry block with no source is
  dropped instead of saving as nothing. Changeset and Content changelog
  updated.

* Limit made-up Clip ids per reader and page, and cut Clip titles

Clips now counts requests whose recording doesn't open against a bucket
per reader and page, and refuses them before any database read once it
is spent, unless the Clip already opened for that reader. A comment
list with no limit gets the most threads the contract allows, and the
playback answer's title is cut to a length both contract files declare.

* Count Clip misses per Clip, so one reader can't block another's first open

A Clip's misses now share its own key, and a Clip is refused when either
its opened bucket or its miss bucket is full. A Clip that opened once and
then went away stays bounded, and made-up ids spend only their own misses.

* Make a Content page's upload a private Clip bound to the page, take its chunks from the paired Content, and list it under From Content instead of Library

* Tell a Content upload start that is too large or not JSON apart from one that fails the schema, and document Content uploads in the Clips sharing skill

* Answer a preflight to the Content chunk route for the paired Content alone, before the auth guard can allow other origins and credentials

* Let a Content page cancel an upload it gave up, and list an uploaded Clip in the folder it was moved into

* Keep a playing Clip going when Clips misses a refresh, and bring it back on focus where the reader left off

* Tell a Content page whether its cancel stopped the upload or Clips finished it first, and treat an upload whose page is in Trash as inactive

* Answer a start Content repeats for one picked file with the upload it made, and leave an upload whose final chunk Clips took to finish when the page cancels

* Keep every ticket a page's overlapping repeats hand out working, refuse a repeat for another file, settle a final chunk against a cancel with one claim on both chunk paths, and ask a repeat that beats its recording to try again

* Show unavailable state for missing shared recordings

* Keep unavailable recordings out of share metadata

* Explain replacement of share retry assertions

* Respect recording lifecycle in bug report context

* A full Clip rate-limit map drops its oldest bucket instead of refusing new readers

* Keep recording reactions independent of comments

* Read where a Content upload stands in one place, so a cancel never changes a final chunk in flight, a parked upload cancels, and an abandoned claim expires

* Drop an unused import from the Content upload tests

* Answer a final chunk sent again with where the upload stands, never take a live claim over, and keep a failed start's binding for the next repeat to start again with the same recording

* Say in the comments that a failed start is taken over only when it left no recording, and that an ended key's file starts again under a new key

* Renew a Content upload's lease before its final chunk claims it, so the lease decides between the chunk and the reaper, and drop the reaper's phase check

* Apply recording Trash policy to activity recipients

* Hide a Content upload whose page is in Trash from folders too, and ask a start repeated during the final chunk to try again rather than say the file is in

* Leave a Content upload whose page is in Trash out of every Clips list and folder count, spaces and Shared included

* Refuse a Content upload's ticket on the agent chunk route, so its chunks always pass the page check and the final chunk's claim

* Narrow recording context rows before lifecycle checks

* Preserve comment cleanup and recording search availability

* Read optional recording rows consistently across compiler checks

* Format the recording search changelog entry

* fix(clips): clean up a failed start's binding only as that start left it

* Align recording expiry checks across shared access

* Match expiry timestamp precision across recording readers

* fix(clips): refuse a page's upload on every chunk route but Content's, and release only the ticket a failed start made

* Share expiry access rules with agent reads and Slack unfurls

* fix(clips): drop a final chunk's outcome when a resumed upload takes chunks again

* Apply recording expiry across Clips readers

* Check recording expiry when content completes

* Enforce recording expiry at mutation boundaries

* Revalidate recording expiry before delayed writes

* Preserve expiry checks through queued and locked writes

* Honor linked recording permissions in meeting reads

* Remove unused meeting visibility test import

* Recheck meeting editor access after finalization work

* Meet main's strict-lint and compiler-strictness guards on Content Clips lines

* Add #8129's attachment rule to the delegated-prompt snapshot #8140 landed without it

* Name the prompt behavior the snapshot update records

* Answer a blocked media URL under a Content grant with 502, not the expired-grant 403

* Meet main's strict-lint and compiler-strictness guards on the Clip block's lines

* Answer a storage provider's 403 under a Content grant with 502

* Send a signed-out page reader's Clip comments without commenter emails

* Let a comment author delete through a document whatever case their email was stored in

* Declare what the three Clip actions in Content return

* Set the mixed-case author without a non-null assertion

* Count a Clip request against its limits from the check, so a burst can't all pass it before the first opens the Clip

* Number the upload binding migrations 96 to 98, after main's 94 and 95

* Clear Clips strict-lint and strict-compiler findings on the Content upload path

Type the upload tests' mocks to the real route and action signatures, default destructured rows to null, omit undefined optional fields, keep stored-ticket token parsing defensive, and create the bindings table the expiry test's list query now reads.
3mdistal added a commit that referenced this pull request Oct 27, 2026
* Respect recording Trash and comment settings

* Apply recording policies to comment reads

* Apply Trash access to comment cleanup

* Apply comment visibility settings to recording history

* Let Content document readers play and comment on Clips uploaded to the document

* Expire Content playback grants in five minutes and close review gaps

* Test Content Clips workspace resolution, token time fields and lifetime edges

* Reset core's workspace caches between Content Clips tests

* Give each Content Clips workspace test its own reader

* Accept only the Content-Clips peer secret and make signed-out host access opt-in

Clips' Content routes now verify tokens with CONTENT_CLIPS_PEER_SECRET
alone, a core config field read through resolveContentClipsPeerSecret.
Tokens signed with the deployment A2A_SECRET or a workspace secret are
refused, and an unset or unusable secret answers a typed 503
(content_clips_not_configured) without reading or writing anything.
Media grants are signed with a key derived from the peer secret, through
a new signingKey option on core's short-lived tokens.

Host access resolvers are asked about signed-out requests only when they
register with { includeSignedOut: true }; Clips is the only one that does.

* Play Clips inside Content pages behind content.clip-embed

A Clips link pasted into a video block's Link tab becomes a Clip block that
plays in Content's own player, with the Clip's comments as markers on the
scrubber. Content asks Clips for playback and comments over the peer
contract, signed with the Content-Clips peer secret, and checks document
access first, so signed-out readers of public pages can watch too.

Registry blocks now keep their source on the clipboard, so a copied Clip
pastes with its data instead of dropping out of the save.

* Hide Clip blocks from the slash menu, add their parity row, and type unreadable Clips bodies

* Evaluate the Clip flag for a signed-out reader in the page's org

* Let a Clip's loading and error card fill the player's width

* Keep the text toolbar off a selected registry block

* Hold a registry block's place on a public page before its editor mounts, instead of showing its MDX source

* Check every pasted non-Clips link in the browser, and keep a Clip's retry and comment failures visible

* Read at most 500 comment threads from Clips, say when there are more, and keep Clip reads out of the browser's saved cache

* Name each block on a public page before it loads, keep a Clip's place through a failed source, and check pasted blocks

- The public page's first paint parses the saved page and shows each
  registry block by its saved title or registry label; a Clip is a link to
  its recording on Clips, built from the block and deployment config only.
- A source swapped out before its metadata loaded no longer leaves its
  restore behind, and the next source restores the reader's place.
- The toolkit reads a pasted block only through its data attributes, checks
  the type and id format, and keeps a pasted source only when the app reads
  it back. Content keeps one only when it is exactly one registered block of
  the declared type, and view-screen quotes only ids and types Content writes.

* Keep a Clip's grant to its page, cap what Content reads from Clips, count only opened Clips against the rate limit, and drop pasted blocks with no source

- The Clip block's player is keyed by page and recording, so a block that
  ends up under another page never keeps the last page's grant.
- Content reads each Clips answer through a byte cap sized from the
  contract. Clips now cuts a listed comment's text to 280 characters and its
  author's name and email to 320, so the longest list has a known size.
- Clips counts a request against its rate limit only after it has opened the
  recording, so made-up recording ids make no buckets.
- With readPastedSource set, a pasted registry block with no source is
  dropped instead of saving as nothing. Changeset and Content changelog
  updated.

* Limit made-up Clip ids per reader and page, and cut Clip titles

Clips now counts requests whose recording doesn't open against a bucket
per reader and page, and refuses them before any database read once it
is spent, unless the Clip already opened for that reader. A comment
list with no limit gets the most threads the contract allows, and the
playback answer's title is cut to a length both contract files declare.

* Count Clip misses per Clip, so one reader can't block another's first open

A Clip's misses now share its own key, and a Clip is refused when either
its opened bucket or its miss bucket is full. A Clip that opened once and
then went away stays bounded, and made-up ids spend only their own misses.

* Make a Content page's upload a private Clip bound to the page, take its chunks from the paired Content, and list it under From Content instead of Library

* Tell a Content upload start that is too large or not JSON apart from one that fails the schema, and document Content uploads in the Clips sharing skill

* Answer a preflight to the Content chunk route for the paired Content alone, before the auth guard can allow other origins and credentials

* Send a video uploaded into a Content page to Clips as a Clip bound to the page, hold it in the block until Clips can take it, and show its progress, making and unfinished states

* Ask Clips for upload storage with GET when the editor checks again

* Keep a Clip block's hooks in order from upload to player, show a failed check while a Clip is made, count a lost last-chunk answer as sent, and tell the agent when Clips couldn't make an uploaded Clip

* Tell an upload Clips refused or answered unreadably apart from Clips not answering

* Refresh the Upload tab's free clips left once an upload starts

* List the Clip upload actions in Content's parity matrix

* Let a Content page cancel an upload it gave up, and list an uploaded Clip in the folder it was moved into

* Keep a playing Clip going when Clips misses a refresh, and bring it back on focus where the reader left off

* Cancel an upload the page gives up, slow and stop checks on a Clip being made, and refuse video types Clips won't take

* Tell a Content page whether its cancel stopped the upload or Clips finished it first, and treat an upload whose page is in Trash as inactive

* Say when Clips kept an upload a page gave up, forget uploads whose block is gone, and upload nothing for a file removed while the gate checks

* Answer a start Content repeats for one picked file with the upload it made, and leave an upload whose final chunk Clips took to finish when the page cancels

* Keep every ticket a page's overlapping repeats hand out working, refuse a repeat for another file, settle a final chunk against a cancel with one claim on both chunk paths, and ask a repeat that beats its recording to try again

* Show unavailable state for missing shared recordings

* Send one upload key with every start of a picked file and place a Clip Clips already has, keep a removed block's five seconds running after the editor closes, and ask again while Clips is still making the first start's Clip

* Keep unavailable recordings out of share metadata

* Explain replacement of share retry assertions

* Respect recording lifecycle in bug report context

* A full Clip rate-limit map drops its oldest bucket instead of refusing new readers

* Keep recording reactions independent of comments

* Read where a Content upload stands in one place, so a cancel never changes a final chunk in flight, a parked upload cancels, and an abandoned claim expires

* Drop an unused import from the Content upload tests

* Retry a Content upload's start, chunks and cancel with the same key when Clips doesn't answer or is still finishing, and show the upload hint only when it's true

* Answer a final chunk sent again with where the upload stands, never take a live claim over, and keep a failed start's binding for the next repeat to start again with the same recording

* Say in the comments that a failed start is taken over only when it left no recording, and that an ended key's file starts again under a new key

* Start a Content upload under a new key once Clips says its key's upload ended, hold the file when Clips stays busy, time out each cancel, cancel a parked upload whose block goes, and forget a closed page's uploads

* Renew a Content upload's lease before its final chunk claims it, so the lease decides between the chunk and the reaper, and drop the reaper's phase check

* Forget a Content upload that ends its sending after its page closed, retry a failed start only when nothing readable came back, and refuse an empty file before starting

* Apply recording Trash policy to activity recipients

* Hide a Content upload whose page is in Trash from folders too, and ask a start repeated during the final chunk to try again rather than say the file is in

* Say in Content's copy of the Clips contract that a start repeated during the final chunk is asked to try again

* Leave a Content upload whose page is in Trash out of every Clips list and folder count, spaces and Shared included

* Refuse a Content upload's ticket on the agent chunk route, so its chunks always pass the page check and the final chunk's claim

* Narrow recording context rows before lifecycle checks

* Preserve comment cleanup and recording search availability

* Read optional recording rows consistently across compiler checks

* Format the recording search changelog entry

* Remove the block made for a dropped or pasted video when Clips refuses its file outright

* fix(clips): clean up a failed start's binding only as that start left it

* Align recording expiry checks across shared access

* Match expiry timestamp precision across recording readers

* fix(clips): refuse a page's upload on every chunk route but Content's, and release only the ticket a failed start made

* Share expiry access rules with agent reads and Slack unfurls

* fix(clips): drop a final chunk's outcome when a resumed upload takes chunks again

* Apply recording expiry across Clips readers

* Check recording expiry when content completes

* Enforce recording expiry at mutation boundaries

* Revalidate recording expiry before delayed writes

* Preserve expiry checks through queued and locked writes

* Honor linked recording permissions in meeting reads

* Remove unused meeting visibility test import

* Recheck meeting editor access after finalization work

* Meet main's strict-lint and compiler-strictness guards on Content Clips lines

* Add #8129's attachment rule to the delegated-prompt snapshot #8140 landed without it

* Name the prompt behavior the snapshot update records

* Answer a blocked media URL under a Content grant with 502, not the expired-grant 403

* Meet main's strict-lint and compiler-strictness guards on the Clip block's lines

* Answer a storage provider's 403 under a Content grant with 502

* Send a signed-out page reader's Clip comments without commenter emails

* Let a comment author delete through a document whatever case their email was stored in

* Declare what the three Clip actions in Content return

* Set the mixed-case author without a non-null assertion

* Count a Clip request against its limits from the check, so a burst can't all pass it before the first opens the Clip

* Number the upload binding migrations 96 to 98, after main's 94 and 95

* Clear Clips strict-lint and strict-compiler findings on the Content upload path

Type the upload tests' mocks to the real route and action signatures, default destructured rows to null, omit undefined optional fields, keep stored-ticket token parsing defensive, and create the bindings table the expiry test's list query now reads.

* Give the media controls test a QueryClient now that the video block holds upload hooks

Main's video comment controls test renders VideoBlock, which now mounts the Clip upload mutation and storage query. Keep Clip uploads off there and wrap the render in a QueryClientProvider.

* Clear Content strict-lint, strict-compiler and output-schema findings on the Clip upload path

The upload-started callback now carries the page id the gate already
checked, so VideoBlock no longer asserts it. create-clip-upload and
get-clip-upload-status declare an outputSchema for the answers they
already returned. Chunk retries walk the delay list with an iterator, and
the tests read through defined() instead of non-null assertions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants