@agentfield/sdk 0.1.45 → 0.1.46-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import express from 'express';
2
2
  import http from 'node:http';
3
+ import * as _ai_sdk_provider from '@ai-sdk/provider';
3
4
  import { z } from 'zod';
5
+ import { ToolSet } from 'ai';
4
6
 
5
7
  type ZodSchema<T> = z.Schema<T, z.ZodTypeDef, any>;
6
8
  interface AIRequestOptions {
@@ -34,6 +36,11 @@ declare class AIClient {
34
36
  stream(prompt: string, options?: AIRequestOptions): Promise<AIStream>;
35
37
  embed(value: string, options?: AIEmbeddingOptions): Promise<number[]>;
36
38
  embedMany(values: string[], options?: AIEmbeddingOptions): Promise<number[][]>;
39
+ /**
40
+ * Build and return the AI model instance for a given set of options.
41
+ * Exposed for use by the tool-calling loop.
42
+ */
43
+ getModel(options?: AIRequestOptions): _ai_sdk_provider.LanguageModelV2;
37
44
  private buildModel;
38
45
  private buildEmbeddingModel;
39
46
  private getRateLimiter;
@@ -415,6 +422,89 @@ declare class DidInterface {
415
422
  exportAuditTrail(filters?: AuditTrailFilters): Promise<AuditTrailExport>;
416
423
  }
417
424
 
425
+ /**
426
+ * Tool calling support for AgentField agents.
427
+ *
428
+ * Converts discovered capabilities into Vercel AI SDK tool definitions and provides
429
+ * an automatic tool-call execution loop that dispatches calls via agent.call().
430
+ */
431
+
432
+ interface ToolCallConfig {
433
+ /** Maximum number of LLM turns in the tool-call loop (default: 10). */
434
+ maxTurns?: number;
435
+ /** Maximum total tool calls allowed (default: 25). */
436
+ maxToolCalls?: number;
437
+ /** Maximum candidate tools to present to the LLM. */
438
+ maxCandidateTools?: number;
439
+ /** Maximum tools to hydrate with full schemas (lazy mode). */
440
+ maxHydratedTools?: number;
441
+ /** Schema hydration mode: "eager" includes full schemas, "lazy" sends metadata first. */
442
+ schemaHydration?: 'eager' | 'lazy';
443
+ /** Whether to broaden discovery if no tools match (default: false). */
444
+ fallbackBroadening?: boolean;
445
+ /** Filter by tags during discovery. */
446
+ tags?: string[];
447
+ /** Filter by agent IDs during discovery. */
448
+ agentIds?: string[];
449
+ /** Filter by health status (default: "healthy"). */
450
+ healthStatus?: string;
451
+ }
452
+ interface ToolCallRecord {
453
+ toolName: string;
454
+ arguments: Record<string, any>;
455
+ result?: any;
456
+ error?: string;
457
+ latencyMs: number;
458
+ turn: number;
459
+ }
460
+ interface ToolCallTrace {
461
+ calls: ToolCallRecord[];
462
+ totalTurns: number;
463
+ totalToolCalls: number;
464
+ finalResponse?: string;
465
+ }
466
+ /**
467
+ * Options for tool-calling in ctx.ai().
468
+ *
469
+ * Accepts multiple forms:
470
+ * - "discover": auto-discover all tools from control plane
471
+ * - ToolCallConfig: discover with filtering/progressive options
472
+ * - DiscoveryResult: use pre-fetched discovery results
473
+ * - AgentCapability[]: convert capability list directly
474
+ * - ToolSet: raw Vercel AI SDK tool map
475
+ */
476
+ type ToolsOption = 'discover' | ToolCallConfig | DiscoveryResult | AgentCapability[] | (ReasonerCapability | SkillCapability)[] | ToolSet;
477
+ interface AIToolRequestOptions extends AIRequestOptions {
478
+ /** Tool definitions for LLM tool calling. */
479
+ tools?: ToolsOption;
480
+ /** Maximum LLM turns in the tool-call loop. */
481
+ maxTurns?: number;
482
+ /** Maximum total tool calls allowed. */
483
+ maxToolCalls?: number;
484
+ }
485
+ /**
486
+ * Convert a single ReasonerCapability or SkillCapability to a Vercel AI SDK tool.
487
+ */
488
+ declare function capabilityToTool(cap: ReasonerCapability | SkillCapability, agent: Agent): ToolSet[string];
489
+ /**
490
+ * Convert a single capability to a metadata-only tool (no full schema).
491
+ * Used for progressive/lazy discovery.
492
+ */
493
+ declare function capabilityToMetadataTool(cap: ReasonerCapability | SkillCapability, agent: Agent): ToolSet[string];
494
+ /**
495
+ * Convert a list of capabilities into a Vercel AI SDK tool map.
496
+ */
497
+ declare function capabilitiesToTools(capabilities: (AgentCapability | ReasonerCapability | SkillCapability)[], agent: Agent, metadataOnly?: boolean): ToolSet;
498
+ declare function buildToolConfig(toolsParam: ToolsOption, agent: Agent): Promise<{
499
+ tools: ToolSet;
500
+ config: ToolCallConfig;
501
+ needsLazyHydration: boolean;
502
+ }>;
503
+ declare function executeToolCallLoop(agent: Agent, prompt: string, toolMap: ToolSet, config: ToolCallConfig, needsLazyHydration: boolean, buildModel: () => any, options?: AIRequestOptions): Promise<{
504
+ text: string;
505
+ trace: ToolCallTrace;
506
+ }>;
507
+
418
508
  declare class ReasonerContext<TInput = any> {
419
509
  readonly input: TInput;
420
510
  readonly executionId: string;
@@ -455,7 +545,19 @@ declare class ReasonerContext<TInput = any> {
455
545
  ai<T>(prompt: string, options: AIRequestOptions & {
456
546
  schema: ZodSchema<T>;
457
547
  }): Promise<T>;
458
- ai(prompt: string, options?: AIRequestOptions): Promise<string>;
548
+ ai(prompt: string, options?: AIToolRequestOptions): Promise<string>;
549
+ /**
550
+ * AI call with automatic tool calling via discover -> ai -> call loop.
551
+ *
552
+ * Discovers available capabilities, presents them as tools to the LLM,
553
+ * dispatches tool calls via agent.call(), and iterates until a final response.
554
+ *
555
+ * @returns Object with `text` (final response) and `trace` (observability data).
556
+ */
557
+ aiWithTools(prompt: string, options?: AIToolRequestOptions): Promise<{
558
+ text: string;
559
+ trace: ToolCallTrace;
560
+ }>;
459
561
  aiStream(prompt: string, options?: AIRequestOptions): Promise<AIStream>;
460
562
  call(target: string, input: any): Promise<any>;
461
563
  discover(options?: DiscoveryOptions): Promise<DiscoveryResult>;
@@ -1186,4 +1288,4 @@ declare class ApprovalClient {
1186
1288
  waitForApproval(executionId: string, opts?: WaitForApprovalOptions): Promise<ApprovalStatusResponse>;
1187
1289
  }
1188
1290
 
1189
- export { ACTIVE_STATUSES, AIClient, type AIConfig, type AIEmbeddingOptions, type AIRequestOptions, type AIStream, Agent, type AgentCapability, type AgentConfig, type AgentHandler, AgentRouter, type AgentRouterOptions, type AgentState, ApprovalClient, type ApprovalRequestResponse, type ApprovalStatusResponse, type AuditTrailExport, type AuditTrailFilters, type Awaitable, CANONICAL_STATUSES, type CompactCapability, type CompactDiscoveryResponse, DIDAuthenticator, type DIDIdentity, type DIDIdentityPackage, type DIDRegistrationRequest, type DIDRegistrationResponse, type DeploymentType, DidClient, DidInterface, DidManager, type DiscoveryFormat, type DiscoveryOptions, type DiscoveryPagination, type DiscoveryResponse, type DiscoveryResult, ExecutionContext, type ExecutionCredential, type ExecutionMetadata, ExecutionStatus, type ExecutionStatusValue, type GenerateCredentialOptions, type GenerateCredentialParams, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, type HarnessConfig, type HarnessOptions, type HarnessProvider, type HarnessResult, HarnessRunner, type HealthStatus, MCPClient, MCPClientRegistry, type MCPConfig, type MCPHealthSummary, type MCPServerConfig, type MCPTool, MCPToolRegistrar, type MCPToolRegistrarOptions, type MCPToolRegistration, type MemoryChangeEvent, MemoryClient, type MemoryConfig, MemoryEventClient, type MemoryEventHandler, MemoryInterface, type MemoryRequestMetadata, type MemoryRequestOptions, type MemoryScope, type MemoryWatchHandler, type Metrics, RateLimitError, type RateLimiterOptions, type RawResult, type ReasonerCapability, ReasonerContext, type ReasonerDefinition, type ReasonerHandler, type ReasonerOptions, type RequestApprovalPayload, SUPPORTED_PROVIDERS, type ServerlessAdapter, type ServerlessEvent, type ServerlessResponse, type SkillCapability, SkillContext, type SkillDefinition, type SkillHandler, type SkillOptions, StatelessRateLimiter, TERMINAL_STATUSES, type VectorSearchOptions, type VectorSearchResult, type WaitForApprovalOptions, type WorkflowCredential, type WorkflowMetadata, type WorkflowProgressOptions, WorkflowReporter, type ZodSchema, buildProvider, createHarnessResult, createMetrics, createRawResult, getCurrentContext, getCurrentSkillContext, isActive, isTerminal, normalizeStatus };
1291
+ export { ACTIVE_STATUSES, AIClient, type AIConfig, type AIEmbeddingOptions, type AIRequestOptions, type AIStream, type AIToolRequestOptions, Agent, type AgentCapability, type AgentConfig, type AgentHandler, AgentRouter, type AgentRouterOptions, type AgentState, ApprovalClient, type ApprovalRequestResponse, type ApprovalStatusResponse, type AuditTrailExport, type AuditTrailFilters, type Awaitable, CANONICAL_STATUSES, type CompactCapability, type CompactDiscoveryResponse, DIDAuthenticator, type DIDIdentity, type DIDIdentityPackage, type DIDRegistrationRequest, type DIDRegistrationResponse, type DeploymentType, DidClient, DidInterface, DidManager, type DiscoveryFormat, type DiscoveryOptions, type DiscoveryPagination, type DiscoveryResponse, type DiscoveryResult, ExecutionContext, type ExecutionCredential, type ExecutionMetadata, ExecutionStatus, type ExecutionStatusValue, type GenerateCredentialOptions, type GenerateCredentialParams, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, type HarnessConfig, type HarnessOptions, type HarnessProvider, type HarnessResult, HarnessRunner, type HealthStatus, MCPClient, MCPClientRegistry, type MCPConfig, type MCPHealthSummary, type MCPServerConfig, type MCPTool, MCPToolRegistrar, type MCPToolRegistrarOptions, type MCPToolRegistration, type MemoryChangeEvent, MemoryClient, type MemoryConfig, MemoryEventClient, type MemoryEventHandler, MemoryInterface, type MemoryRequestMetadata, type MemoryRequestOptions, type MemoryScope, type MemoryWatchHandler, type Metrics, RateLimitError, type RateLimiterOptions, type RawResult, type ReasonerCapability, ReasonerContext, type ReasonerDefinition, type ReasonerHandler, type ReasonerOptions, type RequestApprovalPayload, SUPPORTED_PROVIDERS, type ServerlessAdapter, type ServerlessEvent, type ServerlessResponse, type SkillCapability, SkillContext, type SkillDefinition, type SkillHandler, type SkillOptions, StatelessRateLimiter, TERMINAL_STATUSES, type ToolCallConfig, type ToolCallRecord, type ToolCallTrace, type ToolsOption, type VectorSearchOptions, type VectorSearchResult, type WaitForApprovalOptions, type WorkflowCredential, type WorkflowMetadata, type WorkflowProgressOptions, WorkflowReporter, type ZodSchema, buildProvider, buildToolConfig, capabilitiesToTools, capabilityToMetadataTool, capabilityToTool, createHarnessResult, createMetrics, createRawResult, executeToolCallLoop, getCurrentContext, getCurrentSkillContext, isActive, isTerminal, normalizeStatus };
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import express from 'express';
6
6
  import rateLimit from 'express-rate-limit';
7
7
  import crypto2, { randomUUID, createHash } from 'crypto';
8
8
  import { AsyncLocalStorage } from 'async_hooks';
9
- import { generateObject, generateText, streamText, embed, embedMany } from 'ai';
9
+ import { tool, jsonSchema, generateText, stepCountIs, generateObject, streamText, embed, embedMany } from 'ai';
10
10
  import { createOpenAI } from '@ai-sdk/openai';
11
11
  import { createAnthropic } from '@ai-sdk/anthropic';
12
12
  import { createGoogleGenerativeAI } from '@ai-sdk/google';
@@ -99,8 +99,8 @@ function isLargeSchema(schemaJson) {
99
99
  return estimateTokens(schemaJson) > LARGE_SCHEMA_TOKEN_THRESHOLD;
100
100
  }
101
101
  function buildPromptSuffix(schema, cwd) {
102
- const jsonSchema = schemaToJsonSchema(schema);
103
- const schemaJson = JSON.stringify(jsonSchema, null, 2);
102
+ const jsonSchema2 = schemaToJsonSchema(schema);
103
+ const schemaJson = JSON.stringify(jsonSchema2, null, 2);
104
104
  const outputPath = getOutputPath(cwd);
105
105
  if (isLargeSchema(schemaJson)) {
106
106
  const schemaPath = writeSchemaFile(schemaJson, cwd);
@@ -883,6 +883,299 @@ var ExecutionContext = class {
883
883
  return store.getStore();
884
884
  }
885
885
  };
886
+ var DEFAULT_MAX_TURNS = 10;
887
+ var DEFAULT_MAX_TOOL_CALLS = 25;
888
+ var DEFAULT_HEALTH_STATUS = void 0;
889
+ function invocationTargetToCallTarget(invocationTarget) {
890
+ if (invocationTarget.includes(":skill:")) {
891
+ const parts = invocationTarget.split(":skill:");
892
+ return `${parts[0]}.${parts[1]}`;
893
+ }
894
+ if (invocationTarget.includes(":")) {
895
+ const idx = invocationTarget.indexOf(":");
896
+ return `${invocationTarget.substring(0, idx)}.${invocationTarget.substring(idx + 1)}`;
897
+ }
898
+ return invocationTarget;
899
+ }
900
+ function makeExecute(agent, invocationTarget) {
901
+ const callTarget = invocationTargetToCallTarget(invocationTarget);
902
+ return async (args) => agent.call(callTarget, args);
903
+ }
904
+ function sanitizeToolName(invocationTarget) {
905
+ return invocationTarget.replace(/:/g, "__");
906
+ }
907
+ function unsanitizeToolName(sanitizedName) {
908
+ return sanitizedName.replace(/__/g, ":");
909
+ }
910
+ function capabilityToTool(cap, agent) {
911
+ const rawSchema = cap.inputSchema ?? { type: "object", properties: {} };
912
+ const schema = rawSchema.type ? rawSchema : { type: "object", properties: rawSchema };
913
+ return tool({
914
+ description: cap.description ?? `Call ${cap.invocationTarget}`,
915
+ inputSchema: jsonSchema(schema),
916
+ execute: makeExecute(agent, cap.invocationTarget)
917
+ });
918
+ }
919
+ function capabilityToMetadataTool(cap, agent) {
920
+ return tool({
921
+ description: cap.description ?? `Call ${cap.invocationTarget}`,
922
+ inputSchema: jsonSchema({ type: "object", properties: {} }),
923
+ execute: makeExecute(agent, cap.invocationTarget)
924
+ });
925
+ }
926
+ function capabilitiesToTools(capabilities, agent, metadataOnly = false) {
927
+ const tools = {};
928
+ const convert = metadataOnly ? capabilityToMetadataTool : capabilityToTool;
929
+ for (const cap of capabilities) {
930
+ if ("reasoners" in cap && "skills" in cap) {
931
+ const agentCap = cap;
932
+ for (const r of agentCap.reasoners) {
933
+ tools[sanitizeToolName(r.invocationTarget)] = convert(r, agent);
934
+ }
935
+ for (const s of agentCap.skills) {
936
+ tools[sanitizeToolName(s.invocationTarget)] = convert(s, agent);
937
+ }
938
+ } else {
939
+ const c = cap;
940
+ tools[sanitizeToolName(c.invocationTarget)] = convert(c, agent);
941
+ }
942
+ }
943
+ return tools;
944
+ }
945
+ function limitToolSet(tools, max) {
946
+ const entries = Object.entries(tools);
947
+ if (entries.length <= max) return tools;
948
+ const limited = {};
949
+ for (let i = 0; i < max; i++) {
950
+ limited[entries[i][0]] = entries[i][1];
951
+ }
952
+ return limited;
953
+ }
954
+ async function discoverTools(agent, config, hydrateSchemas = true) {
955
+ const discoveryOpts = {
956
+ tags: config.tags,
957
+ agentIds: config.agentIds,
958
+ includeInputSchema: hydrateSchemas,
959
+ includeOutputSchema: false,
960
+ includeDescriptions: true,
961
+ healthStatus: config.healthStatus ?? DEFAULT_HEALTH_STATUS
962
+ };
963
+ const result = await agent.discover(discoveryOpts);
964
+ if (!result.json) return { tools: {}, capabilities: [] };
965
+ const caps = result.json.capabilities;
966
+ let tools = capabilitiesToTools(caps, agent, !hydrateSchemas);
967
+ if (config.maxCandidateTools) {
968
+ tools = limitToolSet(tools, config.maxCandidateTools);
969
+ }
970
+ return { tools, capabilities: caps };
971
+ }
972
+ async function hydrateSelectedTools(agent, config, selectedNames) {
973
+ const discoveryOpts = {
974
+ tags: config.tags,
975
+ agentIds: config.agentIds,
976
+ includeInputSchema: true,
977
+ includeOutputSchema: false,
978
+ includeDescriptions: true,
979
+ healthStatus: config.healthStatus ?? DEFAULT_HEALTH_STATUS
980
+ };
981
+ const result = await agent.discover(discoveryOpts);
982
+ if (!result.json) return {};
983
+ const selectedSet = new Set(selectedNames.map(unsanitizeToolName));
984
+ const tools = {};
985
+ for (const cap of result.json.capabilities) {
986
+ for (const r of cap.reasoners) {
987
+ if (selectedSet.has(r.invocationTarget)) {
988
+ tools[sanitizeToolName(r.invocationTarget)] = capabilityToTool(r, agent);
989
+ }
990
+ }
991
+ for (const s of cap.skills) {
992
+ if (selectedSet.has(s.invocationTarget)) {
993
+ tools[sanitizeToolName(s.invocationTarget)] = capabilityToTool(s, agent);
994
+ }
995
+ }
996
+ }
997
+ if (config.maxHydratedTools) {
998
+ return limitToolSet(tools, config.maxHydratedTools);
999
+ }
1000
+ return tools;
1001
+ }
1002
+ function isToolCallConfig(obj) {
1003
+ const keys = [
1004
+ "maxTurns",
1005
+ "maxToolCalls",
1006
+ "tags",
1007
+ "schemaHydration",
1008
+ "agentIds",
1009
+ "healthStatus",
1010
+ "fallbackBroadening",
1011
+ "maxCandidateTools",
1012
+ "maxHydratedTools"
1013
+ ];
1014
+ return typeof obj === "object" && !Array.isArray(obj) && keys.some((k) => k in obj);
1015
+ }
1016
+ function isDiscoveryResult(obj) {
1017
+ return typeof obj === "object" && !Array.isArray(obj) && "raw" in obj && "format" in obj;
1018
+ }
1019
+ async function buildToolConfig(toolsParam, agent) {
1020
+ const baseConfig = {
1021
+ maxTurns: DEFAULT_MAX_TURNS,
1022
+ maxToolCalls: DEFAULT_MAX_TOOL_CALLS,
1023
+ schemaHydration: "eager",
1024
+ fallbackBroadening: false,
1025
+ healthStatus: DEFAULT_HEALTH_STATUS
1026
+ };
1027
+ if (toolsParam === "discover") {
1028
+ const { tools } = await discoverTools(agent, baseConfig);
1029
+ return { tools, config: baseConfig, needsLazyHydration: false };
1030
+ }
1031
+ if (isToolCallConfig(toolsParam)) {
1032
+ const config = { ...baseConfig, ...toolsParam };
1033
+ const isLazy = config.schemaHydration === "lazy";
1034
+ const { tools } = await discoverTools(agent, config, !isLazy);
1035
+ return { tools, config, needsLazyHydration: isLazy };
1036
+ }
1037
+ if (isDiscoveryResult(toolsParam)) {
1038
+ if (toolsParam.json) {
1039
+ const tools = capabilitiesToTools(toolsParam.json.capabilities, agent);
1040
+ return { tools, config: baseConfig, needsLazyHydration: false };
1041
+ }
1042
+ return { tools: {}, config: baseConfig, needsLazyHydration: false };
1043
+ }
1044
+ if (Array.isArray(toolsParam)) {
1045
+ const tools = capabilitiesToTools(toolsParam, agent);
1046
+ return { tools, config: baseConfig, needsLazyHydration: false };
1047
+ }
1048
+ if (typeof toolsParam === "object") {
1049
+ return { tools: toolsParam, config: baseConfig, needsLazyHydration: false };
1050
+ }
1051
+ throw new Error(
1052
+ `Invalid tools parameter: expected "discover", ToolCallConfig, DiscoveryResult, capability array, or tool map, got ${typeof toolsParam}`
1053
+ );
1054
+ }
1055
+ function wrapToolsWithObservability(toolMap, agent, trace, maxToolCalls, getCurrentTurn) {
1056
+ let totalCalls = 0;
1057
+ const observableTools = {};
1058
+ for (const [name, t] of Object.entries(toolMap)) {
1059
+ const originalTool = t;
1060
+ observableTools[name] = tool({
1061
+ description: originalTool.description ?? "",
1062
+ inputSchema: originalTool.inputSchema,
1063
+ execute: async (args) => {
1064
+ totalCalls++;
1065
+ trace.totalToolCalls = totalCalls;
1066
+ if (totalCalls > maxToolCalls) {
1067
+ const record2 = {
1068
+ toolName: name,
1069
+ arguments: args,
1070
+ error: "Tool call limit reached",
1071
+ latencyMs: 0,
1072
+ turn: getCurrentTurn()
1073
+ };
1074
+ trace.calls.push(record2);
1075
+ return { error: "Tool call limit reached. Please provide a final response." };
1076
+ }
1077
+ const record = {
1078
+ toolName: name,
1079
+ arguments: args,
1080
+ latencyMs: 0,
1081
+ turn: getCurrentTurn()
1082
+ };
1083
+ const invocationTarget = unsanitizeToolName(name);
1084
+ const callTarget = invocationTargetToCallTarget(invocationTarget);
1085
+ const start = Date.now();
1086
+ try {
1087
+ const result = await agent.call(callTarget, args);
1088
+ record.result = result;
1089
+ record.latencyMs = Date.now() - start;
1090
+ trace.calls.push(record);
1091
+ return result;
1092
+ } catch (err) {
1093
+ record.error = err.message ?? String(err);
1094
+ record.latencyMs = Date.now() - start;
1095
+ trace.calls.push(record);
1096
+ return { error: record.error, tool: name };
1097
+ }
1098
+ }
1099
+ });
1100
+ }
1101
+ return { tools: observableTools, getTotalCalls: () => totalCalls };
1102
+ }
1103
+ async function executeToolCallLoop(agent, prompt, toolMap, config, needsLazyHydration, buildModel, options = {}) {
1104
+ const maxTurns = config.maxTurns ?? DEFAULT_MAX_TURNS;
1105
+ const maxToolCalls = config.maxToolCalls ?? DEFAULT_MAX_TOOL_CALLS;
1106
+ const trace = {
1107
+ calls: [],
1108
+ totalTurns: 0,
1109
+ totalToolCalls: 0
1110
+ };
1111
+ let activeTools = toolMap;
1112
+ if (needsLazyHydration) {
1113
+ const selectionTools = {};
1114
+ for (const [name, t] of Object.entries(toolMap)) {
1115
+ const orig = t;
1116
+ selectionTools[name] = tool({
1117
+ description: orig.description ?? "",
1118
+ inputSchema: orig.inputSchema
1119
+ // No execute — AI SDK will stop after LLM selects tools
1120
+ });
1121
+ }
1122
+ const selectionResult = await generateText({
1123
+ model: buildModel(),
1124
+ prompt,
1125
+ system: options.system,
1126
+ temperature: options.temperature,
1127
+ maxOutputTokens: options.maxTokens,
1128
+ tools: selectionTools,
1129
+ stopWhen: stepCountIs(1)
1130
+ // Stop after LLM's first response (tool selection)
1131
+ });
1132
+ const selectedNames = /* @__PURE__ */ new Set();
1133
+ for (const step of selectionResult.steps) {
1134
+ for (const tc of step.toolCalls) {
1135
+ selectedNames.add(tc.toolName);
1136
+ }
1137
+ }
1138
+ if (selectedNames.size > 0) {
1139
+ const hydratedTools = await hydrateSelectedTools(
1140
+ agent,
1141
+ config,
1142
+ Array.from(selectedNames)
1143
+ );
1144
+ if (Object.keys(hydratedTools).length > 0) {
1145
+ activeTools = hydratedTools;
1146
+ }
1147
+ } else {
1148
+ trace.totalTurns = selectionResult.steps.length;
1149
+ trace.finalResponse = selectionResult.text;
1150
+ return { text: selectionResult.text, trace };
1151
+ }
1152
+ }
1153
+ let currentTurn = 0;
1154
+ const { tools: observableTools } = wrapToolsWithObservability(
1155
+ activeTools,
1156
+ agent,
1157
+ trace,
1158
+ maxToolCalls,
1159
+ () => currentTurn
1160
+ );
1161
+ const model = buildModel();
1162
+ const result = await generateText({
1163
+ model,
1164
+ prompt,
1165
+ system: options.system,
1166
+ temperature: options.temperature,
1167
+ maxOutputTokens: options.maxTokens,
1168
+ tools: observableTools,
1169
+ stopWhen: stepCountIs(maxTurns),
1170
+ onStepFinish: () => {
1171
+ currentTurn++;
1172
+ trace.totalTurns = currentTurn;
1173
+ }
1174
+ });
1175
+ trace.finalResponse = result.text;
1176
+ trace.totalTurns = result.steps.length;
1177
+ return { text: result.text, trace };
1178
+ }
886
1179
 
887
1180
  // src/context/ReasonerContext.ts
888
1181
  var ReasonerContext = class {
@@ -923,8 +1216,37 @@ var ReasonerContext = class {
923
1216
  this.did = params.did;
924
1217
  }
925
1218
  ai(prompt, options) {
1219
+ if (options?.tools) {
1220
+ return this.aiWithTools(prompt, options);
1221
+ }
926
1222
  return this.aiClient.generate(prompt, options);
927
1223
  }
1224
+ /**
1225
+ * AI call with automatic tool calling via discover -> ai -> call loop.
1226
+ *
1227
+ * Discovers available capabilities, presents them as tools to the LLM,
1228
+ * dispatches tool calls via agent.call(), and iterates until a final response.
1229
+ *
1230
+ * @returns Object with `text` (final response) and `trace` (observability data).
1231
+ */
1232
+ async aiWithTools(prompt, options = {}) {
1233
+ const toolsParam = options.tools ?? "discover";
1234
+ const { tools, config, needsLazyHydration } = await buildToolConfig(toolsParam, this.agent);
1235
+ const mergedConfig = {
1236
+ ...config,
1237
+ maxTurns: options.maxTurns ?? config.maxTurns ?? 10,
1238
+ maxToolCalls: options.maxToolCalls ?? config.maxToolCalls ?? 25
1239
+ };
1240
+ return executeToolCallLoop(
1241
+ this.agent,
1242
+ prompt,
1243
+ tools,
1244
+ mergedConfig,
1245
+ needsLazyHydration,
1246
+ () => this.aiClient.getModel(options),
1247
+ options
1248
+ );
1249
+ }
928
1250
  aiStream(prompt, options) {
929
1251
  return this.aiClient.stream(prompt, options);
930
1252
  }
@@ -1288,6 +1610,13 @@ var AIClient = class {
1288
1610
  );
1289
1611
  return result.embeddings;
1290
1612
  }
1613
+ /**
1614
+ * Build and return the AI model instance for a given set of options.
1615
+ * Exposed for use by the tool-calling loop.
1616
+ */
1617
+ getModel(options = {}) {
1618
+ return this.buildModel(options);
1619
+ }
1291
1620
  buildModel(options) {
1292
1621
  const provider = options.provider ?? this.config.provider ?? "openai";
1293
1622
  const modelName = options.model ?? this.config.model ?? "gpt-4o";
@@ -1346,7 +1675,7 @@ var AIClient = class {
1346
1675
  apiKey: this.config.apiKey,
1347
1676
  baseURL: this.config.baseUrl ?? "https://openrouter.ai/api/v1"
1348
1677
  });
1349
- return openrouter(modelName);
1678
+ return openrouter.chat(modelName);
1350
1679
  }
1351
1680
  case "ollama": {
1352
1681
  const ollama = createOpenAI({
@@ -1354,7 +1683,7 @@ var AIClient = class {
1354
1683
  // Ollama doesn't need real key
1355
1684
  baseURL: this.config.baseUrl ?? "http://localhost:11434/v1"
1356
1685
  });
1357
- return ollama(modelName);
1686
+ return ollama.chat(modelName);
1358
1687
  }
1359
1688
  case "openai":
1360
1689
  default: {
@@ -2535,16 +2864,16 @@ function toJsonSchema(schema) {
2535
2864
  return {};
2536
2865
  }
2537
2866
  if (isZodSchema(schema)) {
2538
- const jsonSchema = zodToJsonSchema(schema, {
2867
+ const jsonSchema2 = zodToJsonSchema(schema, {
2539
2868
  target: "openApi3",
2540
2869
  $refStrategy: "none"
2541
2870
  // Inline all definitions instead of using $ref
2542
2871
  });
2543
- if (typeof jsonSchema === "object" && jsonSchema !== null) {
2544
- const { $schema, ...rest } = jsonSchema;
2872
+ if (typeof jsonSchema2 === "object" && jsonSchema2 !== null) {
2873
+ const { $schema, ...rest } = jsonSchema2;
2545
2874
  return rest;
2546
2875
  }
2547
- return jsonSchema;
2876
+ return jsonSchema2;
2548
2877
  }
2549
2878
  if (typeof schema === "object") {
2550
2879
  return schema;
@@ -2671,11 +3000,11 @@ var MCPClient = class {
2671
3000
  return this.lastHealthy;
2672
3001
  }
2673
3002
  normalizeTools(tools) {
2674
- return (tools ?? []).map((tool) => ({
2675
- name: tool?.name ?? "unknown",
2676
- description: tool?.description,
2677
- inputSchema: tool?.inputSchema ?? tool?.input_schema,
2678
- input_schema: tool?.input_schema
3003
+ return (tools ?? []).map((tool2) => ({
3004
+ name: tool2?.name ?? "unknown",
3005
+ description: tool2?.description,
3006
+ inputSchema: tool2?.inputSchema ?? tool2?.input_schema,
3007
+ input_schema: tool2?.input_schema
2679
3008
  }));
2680
3009
  }
2681
3010
  };
@@ -2757,9 +3086,9 @@ var MCPToolRegistrar = class {
2757
3086
  continue;
2758
3087
  }
2759
3088
  const tools = await client.listTools();
2760
- for (const tool of tools) {
2761
- if (!tool?.name) continue;
2762
- const skillName = this.buildSkillName(client.alias, tool.name);
3089
+ for (const tool2 of tools) {
3090
+ if (!tool2?.name) continue;
3091
+ const skillName = this.buildSkillName(client.alias, tool2.name);
2763
3092
  if (this.registered.has(skillName) || this.agent.skills.get(skillName)) {
2764
3093
  continue;
2765
3094
  }
@@ -2767,22 +3096,22 @@ var MCPToolRegistrar = class {
2767
3096
  skillName,
2768
3097
  async (ctx) => {
2769
3098
  const args = ctx.input && typeof ctx.input === "object" ? ctx.input : {};
2770
- const result = await client.callTool(tool.name, args);
3099
+ const result = await client.callTool(tool2.name, args);
2771
3100
  return {
2772
3101
  status: "success",
2773
3102
  result,
2774
3103
  server: client.alias,
2775
- tool: tool.name
3104
+ tool: tool2.name
2776
3105
  };
2777
3106
  },
2778
3107
  {
2779
- description: tool.description ?? `MCP tool ${tool.name} from ${client.alias}`,
2780
- inputSchema: tool.inputSchema ?? tool.input_schema ?? {},
3108
+ description: tool2.description ?? `MCP tool ${tool2.name} from ${client.alias}`,
3109
+ inputSchema: tool2.inputSchema ?? tool2.input_schema ?? {},
2781
3110
  tags: this.buildTags(client.alias)
2782
3111
  }
2783
3112
  );
2784
3113
  this.registered.add(skillName);
2785
- registrations.push({ server: client.alias, skillName, tool });
3114
+ registrations.push({ server: client.alias, skillName, tool: tool2 });
2786
3115
  if (this.devMode) {
2787
3116
  console.info(`Registered MCP skill ${skillName}`);
2788
3117
  }
@@ -4202,6 +4531,6 @@ function sleep(ms) {
4202
4531
  return new Promise((resolve) => setTimeout(resolve, ms));
4203
4532
  }
4204
4533
 
4205
- export { ACTIVE_STATUSES, AIClient, Agent, AgentRouter, ApprovalClient, CANONICAL_STATUSES, DIDAuthenticator, DidClient, DidInterface, DidManager, ExecutionContext, ExecutionStatus, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, HarnessRunner, MCPClient, MCPClientRegistry, MCPToolRegistrar, MemoryClient, MemoryEventClient, MemoryInterface, RateLimitError, ReasonerContext, SUPPORTED_PROVIDERS, SkillContext, StatelessRateLimiter, TERMINAL_STATUSES, WorkflowReporter, buildProvider, createHarnessResult, createMetrics, createRawResult, getCurrentContext, getCurrentSkillContext, isActive, isTerminal, normalizeStatus };
4534
+ export { ACTIVE_STATUSES, AIClient, Agent, AgentRouter, ApprovalClient, CANONICAL_STATUSES, DIDAuthenticator, DidClient, DidInterface, DidManager, ExecutionContext, ExecutionStatus, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, HarnessRunner, MCPClient, MCPClientRegistry, MCPToolRegistrar, MemoryClient, MemoryEventClient, MemoryInterface, RateLimitError, ReasonerContext, SUPPORTED_PROVIDERS, SkillContext, StatelessRateLimiter, TERMINAL_STATUSES, WorkflowReporter, buildProvider, buildToolConfig, capabilitiesToTools, capabilityToMetadataTool, capabilityToTool, createHarnessResult, createMetrics, createRawResult, executeToolCallLoop, getCurrentContext, getCurrentSkillContext, isActive, isTerminal, normalizeStatus };
4206
4535
  //# sourceMappingURL=index.js.map
4207
4536
  //# sourceMappingURL=index.js.map