11import { defineDynamic , defineTool } from "eve/tools" ;
2- import { withoutApprovalSummary } from "../../src/internal/approvals/utils/approvalSummary.js" ;
3- import { settledWriteResult } from "../../src/internal/approvals/utils/settledWriteResult.js" ;
42import { callAutumnMcpTool } from "../../src/internal/autumnMcp/rpcClient.js" ;
3+ import { withApprovalDescriptionSchema } from "./approvalDescriptionSchema.js" ;
54import { approvalSets } from "./approvalSets.js" ;
6- import { withApprovalSummarySchema } from "./approvalSummarySchema.js" ;
75import {
86 type LeafPrincipalAttributes ,
97 mintCachedAutumnToken ,
@@ -12,6 +10,27 @@ import { leafMcpBaseUrl, serverToolMetadata } from "./autumnToolMetadata.js";
1210import { type LeafAgentConnection , toolAllowlists } from "./toolAllowlists.js" ;
1311import { slimToolSchema } from "./toolSchemaSlim.js" ;
1412
13+ // Billing decisions need payment method + plans up front; strict validation
14+ // means the expand must live inside `request`, not beside it.
15+ const DEFAULT_CUSTOMER_EXPAND = [ "payment_method" , "subscriptions.plan" ] ;
16+
17+ export const withCustomerExpand = ( input : Record < string , unknown > ) => {
18+ const request = input . request ;
19+ if ( ! request || typeof request !== "object" || Array . isArray ( request ) ) {
20+ return input ;
21+ }
22+ const fields = request as Record < string , unknown > ;
23+ if ( fields . expand !== undefined ) return input ;
24+ return { ...input , request : { ...fields , expand : DEFAULT_CUSTOMER_EXPAND } } ;
25+ } ;
26+
27+ /** What a gated write returns to the model. The card is the user's decision
28+ * point, so the turn ends here rather than waiting on it. */
29+ const RECORDED_FOR_APPROVAL =
30+ "Recorded for approval. The user sees an approval card with the exact " +
31+ "change and applies it from there. Do not call this write again, and do " +
32+ "not tell the user it has been applied." ;
33+
1534/** Pre-registers the agent's allowlisted Autumn tools on every step with the
1635 * exact server schemas, so no discovery round trip is needed. */
1736export const autumnDirectTools = ( {
@@ -40,36 +59,28 @@ export const autumnDirectTools = ({
4059 const qualified = `autumn__${ tool . name } ` ;
4160 const toolName = tool . name ;
4261 const requiresApproval = approvalToolNames . has ( toolName ) ;
43- const gatedBillingWrite = agent === "billing" && requiresApproval ;
4462 const inputSchema = slimToolSchema ( tool . inputSchema ) ;
4563 entries [ qualified ] = defineTool ( {
46- approval : ( ) =>
47- requiresApproval ? "user- approval" : "not-applicable" ,
64+ // Gated writes record and end the turn; leaf applies on approval.
65+ approval : ( ) => "not-applicable" ,
4866 description : tool . description ,
4967 execute : async ( input , toolCtx ) => {
50- if ( requiresApproval ) {
51- const settled = await settledWriteResult ( {
52- input : input as Record < string , unknown > ,
53- sessionId : toolCtx . session . id ,
54- toolName,
55- } ) ;
56- if ( settled !== undefined ) return settled ;
57- }
68+ if ( requiresApproval ) return RECORDED_FOR_APPROVAL ;
5869 const minted = await mintCachedAutumnToken (
5970 toolCtx . session . auth . current ?. attributes ,
6071 ) ;
72+ const args = input as Record < string , unknown > ;
6173 return callAutumnMcpTool ( {
62- args : gatedBillingWrite
63- ? withoutApprovalSummary ( input as Record < string , unknown > )
64- : ( input as Record < string , unknown > ) ,
74+ args :
75+ toolName === "getCustomer" ? withCustomerExpand ( args ) : args ,
6576 baseUrl : leafMcpBaseUrl ( ) ,
6677 env : minted . appEnv ,
6778 token : minted . accessToken ,
6879 toolName,
6980 } ) ;
7081 } ,
71- inputSchema : gatedBillingWrite
72- ? withApprovalSummarySchema ( inputSchema )
82+ inputSchema : requiresApproval
83+ ? withApprovalDescriptionSchema ( inputSchema )
7384 : inputSchema ,
7485 } ) ;
7586 }
0 commit comments