From 3ee98d5d613a7c3456584c2ff1d5d2989def4963 Mon Sep 17 00:00:00 2001 From: iamjr15 Date: Thu, 13 Aug 2026 17:53:01 +0530 Subject: [PATCH] fix(agent): bind connected-app quota identity Persist the durable tool-call ID in Mastra request context. Connected-app execution now derives a stable quota key from a field Mastra preserves. --- packages/agent-core/README.md | 4 ++++ packages/agent-core/src/mastra/context.ts | 1 + packages/agent-core/src/mastra/durable-agent-step.ts | 5 +++++ packages/agent-core/src/mastra/tool-defs/composio-tool.ts | 4 ++-- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/agent-core/README.md b/packages/agent-core/README.md index b2162cc9..cb49e0e7 100644 --- a/packages/agent-core/README.md +++ b/packages/agent-core/README.md @@ -177,6 +177,10 @@ so the agent does not rediscover, convert, or screenshot the same file before fi Composio REST tool discovery and execution responses are byte-bounded before parsing, then projected into bounded, valid JSON before entering model context. +The durable tool executor binds each checkpointed tool-call identity into the +request context before Mastra execution. Composio quota charging derives its +idempotency key from that explicit context value instead of relying on +compatibility-layer execution fields that Mastra does not preserve. Toolkit names use the shared open-slug contract from `@cheatcode/types/integrations` across API, context, and tool boundaries. Discovery first uses Composio's full-text query. Because that query can return zero for an diff --git a/packages/agent-core/src/mastra/context.ts b/packages/agent-core/src/mastra/context.ts index 26578f29..9607a02d 100644 --- a/packages/agent-core/src/mastra/context.ts +++ b/packages/agent-core/src/mastra/context.ts @@ -27,6 +27,7 @@ export const CONTEXT = { runIntent: "runIntent", selectedSkill: "selectedSkill", selectedTool: "selectedTool", + toolCallId: "toolCallId", userSkillCreator: "userSkillCreator", userSkillLoader: "userSkillLoader", userSkills: "userSkills", diff --git a/packages/agent-core/src/mastra/durable-agent-step.ts b/packages/agent-core/src/mastra/durable-agent-step.ts index c52ac280..55590f7e 100644 --- a/packages/agent-core/src/mastra/durable-agent-step.ts +++ b/packages/agent-core/src/mastra/durable-agent-step.ts @@ -1,6 +1,7 @@ import type { RequestContext } from "@mastra/core/request-context"; import { type JSONValue, type ModelMessage, modelMessageSchema } from "ai"; import { z } from "zod"; +import { CONTEXT } from "./context"; import { mastra } from "./index"; import { cheatcodeTools } from "./tool-defs/tool-set"; @@ -122,6 +123,10 @@ export async function executeGeneralAgentTool( if (!tool?.execute) { throw new Error(`Unknown or non-executable agent tool: ${options.toolName}`); } + // Mastra's execution wrapper does not preserve arbitrary top-level compatibility + // fields. Bind the durable call identity explicitly so request-scoped tools can + // derive stable idempotency keys without serializing them into model arguments. + options.requestContext.set(CONTEXT.toolCallId, options.toolCallId); return tool.execute(options.input, { ...(options.abortSignal ? { abortSignal: options.abortSignal } : {}), // Repository tools use requestContext for their runtime dependencies and diff --git a/packages/agent-core/src/mastra/tool-defs/composio-tool.ts b/packages/agent-core/src/mastra/tool-defs/composio-tool.ts index 66a52c21..8fabfe28 100644 --- a/packages/agent-core/src/mastra/tool-defs/composio-tool.ts +++ b/packages/agent-core/src/mastra/tool-defs/composio-tool.ts @@ -612,8 +612,8 @@ export const mastraComposioExecute = createTool({ }); function composioQuotaEventId(context: unknown): string { - const record = typeof context === "object" && context !== null ? context : {}; - const candidate = (record as Record)["toolCallId"]; + const requestContext = requestContextFromToolContext(context); + const candidate = requestContext.get(CONTEXT.toolCallId); if (typeof candidate !== "string" || candidate.length === 0 || candidate.length > 180) { throw new Error("Composio execution requires a bounded tool-call id."); }