diff --git a/.changeset/expose-tool-call-id.md b/.changeset/expose-tool-call-id.md new file mode 100644 index 000000000..ae30b0240 --- /dev/null +++ b/.changeset/expose-tool-call-id.md @@ -0,0 +1,5 @@ +--- +"eve": patch +--- + +`ToolContext` and `ApprovalContext` now expose `callId`, the tool call id carried by the call's stream events, so approval-gated tools can key records to one identity across proposal, rejection, and execution. diff --git a/docs/tools/human-in-the-loop.md b/docs/tools/human-in-the-loop.md index 250c45a8a..3d0e1db4f 100644 --- a/docs/tools/human-in-the-loop.md +++ b/docs/tools/human-in-the-loop.md @@ -38,7 +38,7 @@ export default defineTool({ By default, omitted `approval` behaves like `never()`, so tool calls may execute without human approval. Require human approval or other safeguards for sensitive, irreversible, regulated, financial, healthcare, employment, housing, legal, safety-impacting, user-impacting, or external side-effecting actions. -When the decision depends on the input, pass your own policy instead of a helper. It receives the same session context as tool execution, plus `{ toolName, toolInput, approvedTools }`, and returns an AI SDK 7 approval status synchronously or as a promise. Use `ctx.session.auth.current` to guard by the caller of the current turn and `ctx.session.auth.initiator` to guard by the caller that created the session. Return `"user-approval"` to pause for a person or `"not-applicable"` to continue without a prompt. `toolInput` can be undefined, so guard the access. This policy denies cross-tenant calls, then requires approval only when an amount crosses a threshold: +When the decision depends on the input, pass your own policy instead of a helper. It receives the same session context as tool execution, plus `{ toolName, toolInput, approvedTools, callId }`, and returns an AI SDK 7 approval status synchronously or as a promise. Use `ctx.session.auth.current` to guard by the caller of the current turn and `ctx.session.auth.initiator` to guard by the caller that created the session. Return `"user-approval"` to pause for a person or `"not-applicable"` to continue without a prompt. `toolInput` can be undefined, so guard the access. This policy denies cross-tenant calls, then requires approval only when an amount crosses a threshold: ```ts approval: ({ session, toolInput }) => { diff --git a/docs/tools/overview.mdx b/docs/tools/overview.mdx index ba48b2d66..82c1b72c1 100644 --- a/docs/tools/overview.mdx +++ b/docs/tools/overview.mdx @@ -37,6 +37,7 @@ When a tool returns structured data, add an optional `outputSchema`. With Zod or `execute` gets a `ctx` carrying the runtime accessors: - `ctx.session`: session metadata, turn, auth, parent lineage. +- `ctx.callId`: the id of the current tool call, carried by the call's [stream events](/docs/concepts/sessions-runs-and-streaming) and approval context. - `ctx.abortSignal`: aborts when the active turn is cancelled. Pass it to cancellation-aware work; sandbox sessions from `ctx.getSandbox()` are already bound to it. - `ctx.getSandbox()`: the live [sandbox](/docs/sandbox) handle. - `ctx.getSkill(id)`: read a packaged [skill](/docs/skills)'s metadata and files. diff --git a/packages/eve/src/context/build-base-tool-context.integration.test.ts b/packages/eve/src/context/build-base-tool-context.integration.test.ts index dbd5582e6..e745a41ab 100644 --- a/packages/eve/src/context/build-base-tool-context.integration.test.ts +++ b/packages/eve/src/context/build-base-tool-context.integration.test.ts @@ -17,7 +17,7 @@ describe("buildBaseToolContext – getSandbox abort binding", () => { const controller = new AbortController(); await runtime.runAsSession({ sandbox }, async () => { - const ctx = buildBaseToolContext(controller.signal); + const ctx = buildBaseToolContext({ abortSignal: controller.signal, toolCallId: "call_1" }); const live = await ctx.getSandbox(); await live.run({ command: "echo ready" }); }); @@ -36,7 +36,7 @@ describe("buildBaseToolContext – getSandbox abort binding", () => { const runtime = createTestRuntime(); await runtime.runAsSession({ sandbox }, async () => { - const ctx = buildBaseToolContext(undefined); + const ctx = buildBaseToolContext({ toolCallId: "call_1" }); const live = await ctx.getSandbox(); await live.run({ command: "echo ready" }); }); diff --git a/packages/eve/src/context/build-base-tool-context.ts b/packages/eve/src/context/build-base-tool-context.ts index a12f63c0d..139cf23d5 100644 --- a/packages/eve/src/context/build-base-tool-context.ts +++ b/packages/eve/src/context/build-base-tool-context.ts @@ -1,20 +1,25 @@ import { buildCallbackContext } from "#context/build-callback-context.js"; import type { SessionContext } from "#public/definitions/callback-context.js"; import { bindSandboxAbortSignal } from "#execution/sandbox/abort-bound-session.js"; +import type { ToolExecuteOptions } from "#shared/tool-definition.js"; /** Base context shared by tool executors. */ export type BaseToolContext = SessionContext & { readonly abortSignal: AbortSignal; + readonly callId: string; }; /** Builds the base context for one tool execution. */ -export function buildBaseToolContext(abortSignal: AbortSignal | undefined): BaseToolContext { +export function buildBaseToolContext( + options: Pick, +): BaseToolContext { const callbackContext = buildCallbackContext(); - const signal = abortSignal ?? new AbortController().signal; + const signal = options.abortSignal ?? new AbortController().signal; return { ...callbackContext, abortSignal: signal, + callId: options.toolCallId, getSandbox: async () => bindSandboxAbortSignal(await callbackContext.getSandbox(), signal), }; } diff --git a/packages/eve/src/context/build-dynamic-tools.ts b/packages/eve/src/context/build-dynamic-tools.ts index 4dbac5bd1..25be17b58 100644 --- a/packages/eve/src/context/build-dynamic-tools.ts +++ b/packages/eve/src/context/build-dynamic-tools.ts @@ -51,7 +51,7 @@ function replayTools(metadata: readonly DurableDynamicToolMetadata[]): HarnessTo tools.push({ description: m.description, execute: (input: unknown, options) => - stepFn(m.closureVars, input, buildBaseToolContext(options?.abortSignal)), + stepFn(m.closureVars, input, buildBaseToolContext(options)), inputSchema: jsonSchema(m.inputSchema), name: m.name, approval: buildReplayedApproval(m), diff --git a/packages/eve/src/context/dynamic-tool-lifecycle.test.ts b/packages/eve/src/context/dynamic-tool-lifecycle.test.ts index 975535583..ffa66cbc1 100644 --- a/packages/eve/src/context/dynamic-tool-lifecycle.test.ts +++ b/packages/eve/src/context/dynamic-tool-lifecycle.test.ts @@ -50,6 +50,8 @@ function qualifyDynamicToolNames( return result; } +const executeOptions = { messages: [], toolCallId: "call_1" }; + const stubEntry = defineTool({ description: "test", inputSchema: { type: "object" }, @@ -278,7 +280,7 @@ describe("replayDynamicSessionTools", () => { // Execute the replayed tool — mock provides the callback context const tool = tools[0]!; - tool.execute!({ query: "test" }); + tool.execute!({ query: "test" }, executeOptions); expect(stepFn).toHaveBeenCalledWith( { apiUrl: "https://api.example.com", tenantName: "Acme" }, { query: "test" }, @@ -319,11 +321,11 @@ describe("replayDynamicSessionTools", () => { const tools = replayDynamicSessionTools(metadata, []); const tool = tools[0]!; - tool.execute!({}); + tool.execute!({}, executeOptions); // Mutating the metadata object after replay should NOT affect calls closureVars.counter = 999; - tool.execute!({}); + tool.execute!({}, executeOptions); // Both calls get the same closure vars reference from metadata. // This documents current behavior: replay passes by reference. @@ -451,6 +453,7 @@ function createApprovalContext(input: { }): ApprovalContext { return { approvedTools: new Set(), + callId: "call_1", getSandbox: vi.fn(), getSkill: vi.fn(), session: { @@ -794,7 +797,7 @@ describe("framework dynamic tools (no bundler transform)", () => { expect(replayedTools[0]!.name).toBe("search"); // Execute the replayed tool — the original closure is invoked - await replayedTools[0]!.execute!({ query: "test" }); + await replayedTools[0]!.execute!({ query: "test" }, executeOptions); expect(executeFn).toHaveBeenCalledWith({ query: "test" }); }); @@ -822,7 +825,7 @@ describe("framework dynamic tools (no bundler transform)", () => { expect(tools).toHaveLength(1); expect(tools[0]!.name).toBe("assist"); - await tools[0]!.execute!({ action: "help" }); + await tools[0]!.execute!({ action: "help" }, executeOptions); expect(executeFn).toHaveBeenCalledWith({ action: "help" }); }); @@ -1006,7 +1009,7 @@ describe("framework dynamic tools (no bundler transform)", () => { ctx.clearVirtualContext(); let tools = buildDynamicTools(ctx); - const result1 = await tools[0]!.execute!({}); + const result1 = await tools[0]!.execute!({}, executeOptions); expect(result1).toEqual({ version: 1 }); // Re-dispatch overwrites the resolver's slot @@ -1020,7 +1023,7 @@ describe("framework dynamic tools (no bundler transform)", () => { ctx.clearVirtualContext(); tools = buildDynamicTools(ctx); expect(tools[0]!.description).toBe("v2"); - const result2 = await tools[0]!.execute!({}); + const result2 = await tools[0]!.execute!({}, executeOptions); expect(result2).toEqual({ version: 2 }); }); }); diff --git a/packages/eve/src/context/dynamic-tool-lifecycle.ts b/packages/eve/src/context/dynamic-tool-lifecycle.ts index 3ee9e25cd..8fc5315ec 100644 --- a/packages/eve/src/context/dynamic-tool-lifecycle.ts +++ b/packages/eve/src/context/dynamic-tool-lifecycle.ts @@ -33,7 +33,7 @@ function toHarnessToolDefinition(name: string, entry: DynamicToolEntry): Harness return { description: entry.description, execute: (input: unknown, options) => - entry.execute(input as Record, buildBaseToolContext(options?.abortSignal)), + entry.execute(input as Record, buildBaseToolContext(options)), inputSchema: convertInputSchema(entry.inputSchema), name, approval: entry.approval, @@ -119,7 +119,7 @@ export function replayDynamicSessionTools( tools.push({ description: m.description, execute: (input: unknown, options) => - stepFn(m.closureVars, input, buildBaseToolContext(options?.abortSignal)), + stepFn(m.closureVars, input, buildBaseToolContext(options)), inputSchema: jsonSchema(m.inputSchema), name: m.name, outputSchema: m.outputSchema === undefined ? undefined : jsonSchema(m.outputSchema), diff --git a/packages/eve/src/execution/tool-auth.integration.test.ts b/packages/eve/src/execution/tool-auth.integration.test.ts index 38eac7777..53b931859 100644 --- a/packages/eve/src/execution/tool-auth.integration.test.ts +++ b/packages/eve/src/execution/tool-auth.integration.test.ts @@ -98,6 +98,21 @@ describe("tool-hosted authorization", () => { expect(calls).toBe(1); }); + it("exposes the tool call id on the authored context", async () => { + const tool = authoredTool({ + name: "observe_call_id", + execute(_input, ctx) { + return { callId: ctx.callId }; + }, + }); + const runtime = createTestRuntime({ tools: [tool] }); + + const result = await runtime.runAsSession(undefined, async () => runtime.executeTool(tool, {})); + + // The test harness dispatches every executeTool call as "call_test". + expect(result).toEqual({ callId: "call_test" }); + }); + it("resolves and caches an inline provider on a plain tool", async () => { let calls = 0; const inlineAuth: AuthorizationDefinition = { diff --git a/packages/eve/src/execution/tool-auth.ts b/packages/eve/src/execution/tool-auth.ts index d031f3c79..e0d6e2864 100644 --- a/packages/eve/src/execution/tool-auth.ts +++ b/packages/eve/src/execution/tool-auth.ts @@ -54,19 +54,19 @@ import type { ToolExecuteOptions } from "#shared/tool-definition.js"; export function createToolExecuteWithAuth(input: { readonly scope: string; readonly execute: (toolInput: unknown, ctx: unknown) => unknown; -}): (toolInput: unknown, options?: ToolExecuteOptions) => Promise { +}): (toolInput: unknown, options: ToolExecuteOptions) => Promise { const { scope, execute } = input; - return async (toolInput: unknown, options?: ToolExecuteOptions): Promise => { + return async (toolInput: unknown, options: ToolExecuteOptions): Promise => { const justAuthorizedScopes = new Set(); try { return await execute( toolInput, buildToolContext({ - abortSignal: options?.abortSignal, inlineAuthState: {}, justAuthorizedScopes, + options, scope, }), ); @@ -81,13 +81,13 @@ export function createToolExecuteWithAuth(input: { } function buildToolContext(input: { - readonly abortSignal: AbortSignal | undefined; + readonly options: ToolExecuteOptions; readonly scope: string; readonly justAuthorizedScopes: Set; readonly inlineAuthState: InlineAuthState; }): ToolContext { const { scope, justAuthorizedScopes, inlineAuthState } = input; - const base = buildBaseToolContext(input.abortSignal); + const base = buildBaseToolContext(input.options); return { ...base, async getToken(provider?: ToolAuthProvider, options?: ToolAuthOptions): Promise { diff --git a/packages/eve/src/harness/execute-tool.ts b/packages/eve/src/harness/execute-tool.ts index eb7192d4d..ad92b038e 100644 --- a/packages/eve/src/harness/execute-tool.ts +++ b/packages/eve/src/harness/execute-tool.ts @@ -22,7 +22,7 @@ export type HarnessRuntimeActionDefinition = { export interface HarnessToolDefinition { readonly approvalKey?: (toolInput: Readonly>) => string; readonly description: string; - readonly execute?: (input: any, options?: ToolExecuteOptions) => any; + readonly execute?: (input: any, options: ToolExecuteOptions) => any; readonly inputSchema: FlexibleSchema; readonly name: string; readonly approval?: Approval; diff --git a/packages/eve/src/harness/tools.test.ts b/packages/eve/src/harness/tools.test.ts index d112e4bcf..e2d665d42 100644 --- a/packages/eve/src/harness/tools.test.ts +++ b/packages/eve/src/harness/tools.test.ts @@ -198,6 +198,40 @@ describe("buildToolSet", () => { expect(receivedSignal?.aborted).toBe(false); }); + it("passes the AI SDK toolCallId to the authored tool context as callId", async () => { + let receivedCallId: string | undefined; + const tools: HarnessToolMap = new Map([ + [ + "observe_call_id", + { + description: "Observe the tool call id.", + execute: createToolExecuteWithAuth({ + execute(_input, ctx) { + receivedCallId = (ctx as ToolContext).callId; + return { ok: true }; + }, + scope: "observe_call_id", + }), + inputSchema: jsonSchema({ type: "object" }), + name: "observe_call_id", + }, + ], + ]); + const ctx = new ContextContainer(); + ctx.set(SessionKey, { + auth: { current: null, initiator: null }, + sessionId: "session-1", + turn: { id: "turn-1", sequence: 0 }, + }); + + const result = buildToolSet({ tools }); + await contextStorage.run(ctx, () => + executeSdkTool({ tool: result.observe_call_id, toolCallId: "call_observe" }), + ); + + expect(receivedCallId).toBe("call_observe"); + }); + it("passes through the input schema to the SDK tool", () => { const schema = { properties: { city: { type: "string" } }, @@ -815,6 +849,30 @@ describe("buildToolSet", () => { expect(capturedInput).toEqual(toolInput); }); + it("passes the callId from the AI SDK into approval", async () => { + let capturedCallId: string | undefined; + const tools: HarnessToolMap = new Map([ + [ + "vercel__list_projects", + { + description: "List projects in the team.", + execute: async () => "ok", + inputSchema: jsonSchema({}), + name: "vercel__list_projects", + approval: (ctx) => { + capturedCallId = ctx.callId; + return "user-approval"; + }, + }, + ], + ]); + + const result = buildToolSet({ tools }); + await resolveApproval(result, "vercel__list_projects", {}); + + expect(capturedCallId).toBe("call_1"); + }); + it("passes the active caller and session context into approval", async () => { let capturedCtx: Parameters>[0] | undefined; const tools: HarnessToolMap = new Map([ diff --git a/packages/eve/src/harness/tools.ts b/packages/eve/src/harness/tools.ts index a99188741..4431365e9 100644 --- a/packages/eve/src/harness/tools.ts +++ b/packages/eve/src/harness/tools.ts @@ -34,7 +34,10 @@ type ToolModelOutputValue = type NativeApprovalStatus = Exclude; -const toolApprovals = new WeakMap Promise>(); +const toolApprovals = new WeakMap< + object, + (toolInput: unknown, callId: string) => Promise +>(); /** * Builds an AI SDK `ToolSet` from unified harness tool definitions. @@ -302,8 +305,8 @@ export async function buildToolSetWithProviderTools(input: { function buildApprovalFn( definition: HarnessToolDefinition, input: { readonly approvedTools?: ReadonlySet }, -): (toolInput: unknown) => Promise { - return async (toolInput: unknown) => { +): (toolInput: unknown, callId: string) => Promise { + return async (toolInput: unknown, callId: string) => { if (definition.approval === undefined) return undefined; const toolInputRecord = isObject(toolInput) ? toolInput : undefined; @@ -311,6 +314,7 @@ function buildApprovalFn( const status = await definition.approval({ ...buildCallbackContext(), approvedTools: input.approvedTools ?? new Set(), + callId, toolInput: toolInputRecord, toolName: definition.name, }); @@ -327,6 +331,6 @@ export function buildToolApproval( if (toolDefinition === undefined) return undefined; const approval = toolApprovals.get(toolDefinition); - return (await approval?.(toolCall.input)) as ToolApprovalStatus; + return (await approval?.(toolCall.input, toolCall.toolCallId)) as ToolApprovalStatus; }; } diff --git a/packages/eve/src/internal/testing/app-harness.ts b/packages/eve/src/internal/testing/app-harness.ts index 7958c600d..f10f75951 100644 --- a/packages/eve/src/internal/testing/app-harness.ts +++ b/packages/eve/src/internal/testing/app-harness.ts @@ -224,7 +224,7 @@ export function createTestRuntime(descriptor: TestAppDescriptor = {}): TestRunti throw new Error(`Tool "${tool.name}" is not executable.`); } - return await execute(input); + return await execute(input, { messages: [], toolCallId: "call_test" }); } return { diff --git a/packages/eve/src/internal/testing/scenario-apps/tool-overrides.ts b/packages/eve/src/internal/testing/scenario-apps/tool-overrides.ts index 03689ee6b..a27a3b977 100644 --- a/packages/eve/src/internal/testing/scenario-apps/tool-overrides.ts +++ b/packages/eve/src/internal/testing/scenario-apps/tool-overrides.ts @@ -31,8 +31,8 @@ export default defineTool({ ...bash, description: "Run a vetted shell command in the project sandbox.", approval: always(), - async execute(input) { - return bash.execute(input); + async execute(input, ctx) { + return bash.execute(input, ctx); }, }); `, diff --git a/packages/eve/src/public/definitions/approval.ts b/packages/eve/src/public/definitions/approval.ts index ba35d258d..b008eee5d 100644 --- a/packages/eve/src/public/definitions/approval.ts +++ b/packages/eve/src/public/definitions/approval.ts @@ -11,10 +11,13 @@ type ApprovalToolInput = TInput extends object ? Readonly : TInp * `approvedTools` is the set of tool names (or compound approval keys) * already approved at least once in the current session. `toolName` is the * runtime name of the tool being evaluated. `toolInput` is the raw input the - * model passed, available for input-aware decisions. + * model passed, available for input-aware decisions. `callId` is the id of + * the call being evaluated — the same `callId` carried by the call's stream + * events and its `execute` context. */ export interface ApprovalContext> extends SessionContext { readonly approvedTools: ReadonlySet; + readonly callId: string; readonly toolInput?: ApprovalToolInput; readonly toolName: string; } diff --git a/packages/eve/src/public/definitions/tool.ts b/packages/eve/src/public/definitions/tool.ts index c6870acf0..0399a29f2 100644 --- a/packages/eve/src/public/definitions/tool.ts +++ b/packages/eve/src/public/definitions/tool.ts @@ -74,6 +74,11 @@ export interface ToolAuthOptions { export type ToolContext = SessionContext & { /** Aborts when the active turn is cancelled. */ readonly abortSignal: AbortSignal; + /** + * Id of the current tool call — the same `callId` carried by the call's + * stream events and its {@link ApprovalContext}. + */ + readonly callId: string; /** * Resolves the bearer token for an inline provider. This accepts the same * auth shapes as a connection's `auth` field, including `connect("...")` diff --git a/packages/eve/src/public/tools/internal.ts b/packages/eve/src/public/tools/internal.ts index 6867ccf49..3a9cfbcfa 100644 --- a/packages/eve/src/public/tools/internal.ts +++ b/packages/eve/src/public/tools/internal.ts @@ -8,9 +8,10 @@ import type { ToolDefinition } from "#public/definitions/tool.js"; * {@link ResolvedToolDefinition} so it can be re-exported as a public * {@link ToolDefinition}. * - * Framework tools have the internal `(input) => output` signature. + * Framework tools have the internal `(input, options) => output` signature. * The public {@link ToolDefinition.execute} expects `(input, ctx)`. - * This wrapper bridges the gap — `ctx` is trailing and omitted. + * This wrapper bridges the gap — the public `ctx` is mapped back onto the + * internal execute options. */ export function toPublicToolDefinition(definition: ResolvedToolDefinition): ToolDefinition { if (!definition.execute) { @@ -21,7 +22,14 @@ export function toPublicToolDefinition(definition: ResolvedToolDefinition): Tool const inputSchema = definition.inputSchema; const publicDefinition: ToolDefinition = { description: definition.description, - execute: (input) => internalExecute(input), + execute: (input, ctx) => + internalExecute(input, { + abortSignal: ctx.abortSignal, + // The public context carries no model history, so the bridged + // options cannot reproduce the AI SDK's `messages`. + messages: [], + toolCallId: ctx.callId, + }), inputSchema: (inputSchema ?? {}) as unknown as StandardJSONSchemaV1, outputSchema: definition.outputSchema, }; diff --git a/packages/eve/src/runtime/framework-tools/skill.test.ts b/packages/eve/src/runtime/framework-tools/skill.test.ts index 96d23a3e4..be7ebb141 100644 --- a/packages/eve/src/runtime/framework-tools/skill.test.ts +++ b/packages/eve/src/runtime/framework-tools/skill.test.ts @@ -36,7 +36,9 @@ describe("load_skill executor", () => { if (execute === undefined) throw new Error("load_skill tool is missing an execute function"); await expect( - contextStorage.run(ctx, () => execute({ skill: "talk-like-a-dog" })), + contextStorage.run(ctx, () => + execute({ skill: "talk-like-a-dog" }, { messages: [], toolCallId: "call_1" }), + ), ).rejects.toThrow("Available skills: custom__bark, custom__talk-like-a-dog."); }); @@ -57,7 +59,11 @@ describe("load_skill executor", () => { const execute = SKILL_TOOL_DEFINITION.execute; if (execute === undefined) throw new Error("load_skill tool is missing an execute function"); - await expect(contextStorage.run(ctx, () => execute({ skill: "linear" }))).rejects.toThrow( + await expect( + contextStorage.run(ctx, () => + execute({ skill: "linear" }, { messages: [], toolCallId: "call_1" }), + ), + ).rejects.toThrow( '"linear" is an installed connection, not a skill. Use connection_search with connection "linear" to find its tools.', ); }); diff --git a/packages/eve/src/shared/tool-definition.ts b/packages/eve/src/shared/tool-definition.ts index d89e63d34..468fb4ba9 100644 --- a/packages/eve/src/shared/tool-definition.ts +++ b/packages/eve/src/shared/tool-definition.ts @@ -10,7 +10,7 @@ export type ToolExecuteOptions = Omit, "context">; export type ToolExecuteFn = ( input: TInput, - options?: ToolExecuteOptions, + options: ToolExecuteOptions, ) => Promise | TOutput; interface ToolDefinitionBase { diff --git a/packages/eve/test/resolve-agent.test.ts b/packages/eve/test/resolve-agent.test.ts index 1af4c4906..924eebeb9 100644 --- a/packages/eve/test/resolve-agent.test.ts +++ b/packages/eve/test/resolve-agent.test.ts @@ -269,7 +269,9 @@ describe("resolveAgent", () => { sourceId: "tools/get-weather.mjs", sourceKind: "module", }); - expect(resolved.tools[0]?.execute?.({ city: "Brooklyn" })).toEqual({ + expect( + resolved.tools[0]?.execute?.({ city: "Brooklyn" }, { messages: [], toolCallId: "call_1" }), + ).toEqual({ city: "Brooklyn", }); }); diff --git a/packages/eve/test/scenarios/runtime-loaders.scenario.test.ts b/packages/eve/test/scenarios/runtime-loaders.scenario.test.ts index 6572f49d2..8b110a7dc 100644 --- a/packages/eve/test/scenarios/runtime-loaders.scenario.test.ts +++ b/packages/eve/test/scenarios/runtime-loaders.scenario.test.ts @@ -423,7 +423,12 @@ describe("runtime compiled artifact loaders", () => { } ).default.description, ).toBe("Get the weather."); - await expect(resolvedAgent.tools[0]?.execute?.({ city: "Brooklyn" })).resolves.toEqual({ + await expect( + resolvedAgent.tools[0]?.execute?.( + { city: "Brooklyn" }, + { messages: [], toolCallId: "call_1" }, + ), + ).resolves.toEqual({ city: "Brooklyn", source: "lib", }); @@ -547,7 +552,12 @@ describe("runtime compiled artifact loaders", () => { (tool as { name?: string }).name?.endsWith("_sandbox"), ), ).toBe(false); - await expect(researcherNode?.agent.tools[0]?.execute?.({ query: "climate" })).resolves.toEqual({ + await expect( + researcherNode?.agent.tools[0]?.execute?.( + { query: "climate" }, + { messages: [], toolCallId: "call_1" }, + ), + ).resolves.toEqual({ query: "climate", source: "subagent-lib", }); @@ -576,7 +586,9 @@ describe("runtime compiled artifact loaders", () => { throw new Error("Expected the get_weather tool to be available."); } - await expect(getWeatherTool.execute?.({ city: "Brooklyn" })).resolves.toEqual({ + await expect( + getWeatherTool.execute?.({ city: "Brooklyn" }, { messages: [], toolCallId: "call_1" }), + ).resolves.toEqual({ city: "Brooklyn", route: "@/ path alias", source: "alias-lib", @@ -605,7 +617,9 @@ describe("runtime compiled artifact loaders", () => { throw new Error("Expected one compiled tool before the source update."); } - await expect(firstTool.execute?.({ city: "Brooklyn" })).resolves.toEqual({ + await expect( + firstTool.execute?.({ city: "Brooklyn" }, { messages: [], toolCallId: "call_1" }), + ).resolves.toEqual({ city: "Brooklyn", source: "lib", }); @@ -631,7 +645,9 @@ describe("runtime compiled artifact loaders", () => { throw new Error("Expected one compiled tool after the source update."); } - await expect(secondTool.execute?.({ city: "Brooklyn" })).resolves.toEqual({ + await expect( + secondTool.execute?.({ city: "Brooklyn" }, { messages: [], toolCallId: "call_1" }), + ).resolves.toEqual({ city: "Brooklyn", source: "updated-lib", });