Skip to content

Commit 3ee98d5

Browse files
committed
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.
1 parent f1607bc commit 3ee98d5

4 files changed

Lines changed: 12 additions & 2 deletions

File tree

packages/agent-core/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ so the agent does not rediscover, convert, or screenshot the same file before fi
177177

178178
Composio REST tool discovery and execution responses are byte-bounded before
179179
parsing, then projected into bounded, valid JSON before entering model context.
180+
The durable tool executor binds each checkpointed tool-call identity into the
181+
request context before Mastra execution. Composio quota charging derives its
182+
idempotency key from that explicit context value instead of relying on
183+
compatibility-layer execution fields that Mastra does not preserve.
180184
Toolkit names use the shared open-slug contract from
181185
`@cheatcode/types/integrations` across API, context, and tool boundaries.
182186
Discovery first uses Composio's full-text query. Because that query can return zero for an

packages/agent-core/src/mastra/context.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const CONTEXT = {
2727
runIntent: "runIntent",
2828
selectedSkill: "selectedSkill",
2929
selectedTool: "selectedTool",
30+
toolCallId: "toolCallId",
3031
userSkillCreator: "userSkillCreator",
3132
userSkillLoader: "userSkillLoader",
3233
userSkills: "userSkills",

packages/agent-core/src/mastra/durable-agent-step.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { RequestContext } from "@mastra/core/request-context";
22
import { type JSONValue, type ModelMessage, modelMessageSchema } from "ai";
33
import { z } from "zod";
4+
import { CONTEXT } from "./context";
45
import { mastra } from "./index";
56
import { cheatcodeTools } from "./tool-defs/tool-set";
67

@@ -122,6 +123,10 @@ export async function executeGeneralAgentTool(
122123
if (!tool?.execute) {
123124
throw new Error(`Unknown or non-executable agent tool: ${options.toolName}`);
124125
}
126+
// Mastra's execution wrapper does not preserve arbitrary top-level compatibility
127+
// fields. Bind the durable call identity explicitly so request-scoped tools can
128+
// derive stable idempotency keys without serializing them into model arguments.
129+
options.requestContext.set(CONTEXT.toolCallId, options.toolCallId);
125130
return tool.execute(options.input, {
126131
...(options.abortSignal ? { abortSignal: options.abortSignal } : {}),
127132
// Repository tools use requestContext for their runtime dependencies and

packages/agent-core/src/mastra/tool-defs/composio-tool.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,8 +612,8 @@ export const mastraComposioExecute = createTool({
612612
});
613613

614614
function composioQuotaEventId(context: unknown): string {
615-
const record = typeof context === "object" && context !== null ? context : {};
616-
const candidate = (record as Record<string, unknown>)["toolCallId"];
615+
const requestContext = requestContextFromToolContext(context);
616+
const candidate = requestContext.get(CONTEXT.toolCallId);
617617
if (typeof candidate !== "string" || candidate.length === 0 || candidate.length > 180) {
618618
throw new Error("Composio execution requires a bounded tool-call id.");
619619
}

0 commit comments

Comments
 (0)