Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(agents): withApprovalContext preserves the Tool prototype it wraps

merged#2729CedarCopilot

CedarCopilot wants to merge 1 commit into staging from fix/master-surface-tool-prototype-clone

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Sep 8, 2026, 1:47 PM
  2. Live on staging
    Sep 8, 2026, 1:53 PM
  3. Pipelines steady after this deploy
    Sep 8, 2026, 1:53 PM
  4. Merged
    Sep 8, 2026, 1:53 PM
  5. Live on prod
    Sep 8, 2026, 1:53 PM
  6. Observed 2 days
    Sep 8, 2026, 1:53 PM
  7. Pipelines steady after this deploy
    Sep 8, 2026, 1:53 PM
  8. Sep 8, 2026, 1:57 PM
  9. Unobserved

    Live on prod, no production signal yet

    Sep 11, 2026, 12:07 AM

Behaviors Libra is checking

Master-family tools wrapped with approval context retain the Mastra Tool prototype so every invocation receives the real tool context, including userId, orgId, and runId.Inconclusivehigh confidence

No positive runtime evidence was found for the intended master-family behavior in staging after 2026-09-08T20:53:20Z. Exact OTEL queries confirmed 0 spans for find-crm-conversations, get-calendar-availability, and save-draft; the post-deploy execute_tool inventory contained.

staging, checked Sep 10, 2026, 6:47 PM
Master-family tools wrapped with approval context retain the Mastra Tool prototype so the dispatcher passes the full tool context, including requestContext user and organization identity, on every invocation.Not checked
prod, checked Sep 11, 2026, 12:07 AM

Failures attributed to this change

No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 8, 2026, 2:04 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 8, 2026, 2:30 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 5 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 8, 2026, 2:39 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 3 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 8, 2026, 4:15 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 8, 2026, 4:30 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 2 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 8, 2026, 5:00 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 1 hit · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 8, 2026, 10:00 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 27 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Sep 9, 2026, 3:15 AM

Libra has verdicts on 0 of 1 tracked behaviors on prod; 1 is still being checked. Libra checks hourly for 3 days after each deploy.

What was wrong

withApprovalContext (master-surface.ts, added in 31fa01668, 2026-09-07) returned { ...tool, execute: wrapped } , a plain-object spread that drops the tool's prototype. Mastra's own dispatcher (isVercelTool in @mastra/core) decides how to call a tool by instanceof Tool; a tool that fails that check gets invoked with (args, execOptions) instead of (args, toolContext), and only the latter carries requestContext.

Every tool in buildMasterFamilyToolset() , find-crm-conversations, get-calendar-availability, save-draft, resolve-and-attach, query-conversations, and every other family/orchestration tool , silently lost requestContext this way, for every account on the cedar-master-agent-families flag. Symptom: "Missing userId in runtime context" on effectively every tool call in chat (except load-skill, which is a constructor tool on the agent and never reaches this wrapper).

This is the identical bug PR #2701 (bde571e7c) fixed one day earlier in a sibling wrapper, gateToolExecute (tool-grant.ts) , reintroduced here because withApprovalContext was written fresh instead of reusing the shared cloneToolWithExecute() helper that fix extracted for exactly this reason. It shipped in the 2026-09-08 19:10 UTC deploy and was live for at least the following hour: traced via Sherlock across 3 unrelated orgs (Corgi, Pirros, Strac) and 12+ tool failures.

What changed

  • master-surface.ts: withApprovalContext now returns cloneToolWithExecute(tool, wrapped) instead of the raw spread , same pattern already used by gateToolExecute and instrumentToolExecute.
  • master-surface.test.ts: added an instanceof Tool assertion over the live buildMasterFamilyToolset() registry (mirrors the regression test PR #2701 added for gateToolExecute), so any future family/orchestration tool is covered automatically.
  • no-raw-tool-spread.test.ts (new): a repo-wide static scan of every non-test file under apps/server/src/mastra/ for the { ...tool, execute: } shape. There's no live registry of "every function that overrides a tool's execute" the way FAMILY_TOOLS is a registry of tools themselves, so a source-text scan is the only place to catch a new wrapper before it's ever wired into anything and shipped , this is the guard against a third instance of the same mistake.

How to test

  • pnpm --filter @zero/server exec vitest run src/mastra/tools/__tests__/master-surface.test.ts src/mastra/tools/__tests__/tool-grant.test.ts src/mastra/tools/__tests__/no-raw-tool-spread.test.ts , all pass.
  • Verified the new guard actually catches the regression: reintroducing the raw spread in master-surface.ts fails no-raw-tool-spread.test.ts with a clear message pointing at cloneToolWithExecute; restoring the fix passes again.
  • pnpm --filter @zero/server run types , clean.
  • Post-merge: confirm no new "Missing userId in runtime context" / "No userId in context." rows in chat_messages for accounts on cedar-master-agent-families after this deploys.

🤖 Generated with Claude Code

https://claude.ai/code/session_011jWjgeVGqiM5Fibxe89oT5

Greptile Summary

This PR repairs the family-tool wrapper so wrapped Mastra tools retain their prototype and therefore continue receiving request context. It also adds behavioral and source-level regression coverage.

  • Replaces the prototype-dropping object spread with cloneToolWithExecute.
  • Verifies every tool in the live master-family registry remains a Tool.
  • Adds a repository scan for the historical raw-spread wrapper pattern, though the scan does not yet cover equivalent object literals with intervening properties.

Confidence Score: 4/5

The production fix appears safe to merge; the remaining concern is a non-blocking limitation in the new static regression guard.

The shared cloning helper preserves the Mastr

Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/server/src/mastra/tools/__tests__/master-surface.test.tsno production surface mapped
  • apps/server/src/mastra/tools/__tests__/no-raw-tool-spread.test.tsno production surface mapped
  • apps/server/src/mastra/tools/master-surface.tsno production surface mapped