@inkeep/agents-run-api 0.39.5 → 0.40.0

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.
Files changed (180) hide show
  1. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/system-prompt.js +5 -0
  2. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/thinking-preparation.js +5 -0
  3. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase1/tool.js +5 -0
  4. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/data-component.js +5 -0
  5. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/data-components.js +5 -0
  6. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/phase2/system-prompt.js +5 -0
  7. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/shared/artifact-retrieval-guidance.js +5 -0
  8. package/dist/_virtual/_raw_/home/runner/work/agents/agents/agents-run-api/templates/v1/shared/artifact.js +5 -0
  9. package/dist/a2a/client.d.ts +184 -0
  10. package/dist/a2a/client.js +510 -0
  11. package/dist/a2a/handlers.d.ts +7 -0
  12. package/dist/a2a/handlers.js +560 -0
  13. package/dist/a2a/transfer.d.ts +22 -0
  14. package/dist/a2a/transfer.js +46 -0
  15. package/dist/a2a/types.d.ts +79 -0
  16. package/dist/a2a/types.js +22 -0
  17. package/dist/agents/Agent.d.ts +266 -0
  18. package/dist/agents/Agent.js +1927 -0
  19. package/dist/agents/ModelFactory.d.ts +63 -0
  20. package/dist/agents/ModelFactory.js +194 -0
  21. package/dist/agents/SystemPromptBuilder.d.ts +21 -0
  22. package/dist/agents/SystemPromptBuilder.js +48 -0
  23. package/dist/agents/ToolSessionManager.d.ts +63 -0
  24. package/dist/agents/ToolSessionManager.js +146 -0
  25. package/dist/agents/generateTaskHandler.d.ts +49 -0
  26. package/dist/agents/generateTaskHandler.js +521 -0
  27. package/dist/agents/relationTools.d.ts +57 -0
  28. package/dist/agents/relationTools.js +262 -0
  29. package/dist/agents/types.d.ts +28 -0
  30. package/dist/agents/types.js +1 -0
  31. package/dist/agents/versions/v1/Phase1Config.d.ts +27 -0
  32. package/dist/agents/versions/v1/Phase1Config.js +424 -0
  33. package/dist/agents/versions/v1/Phase2Config.d.ts +31 -0
  34. package/dist/agents/versions/v1/Phase2Config.js +330 -0
  35. package/dist/constants/execution-limits/defaults.d.ts +51 -0
  36. package/dist/constants/execution-limits/defaults.js +52 -0
  37. package/dist/constants/execution-limits/index.d.ts +6 -0
  38. package/dist/constants/execution-limits/index.js +21 -0
  39. package/dist/create-app.d.ts +9 -0
  40. package/dist/create-app.js +195 -0
  41. package/dist/data/agent.d.ts +7 -0
  42. package/dist/data/agent.js +72 -0
  43. package/dist/data/agents.d.ts +34 -0
  44. package/dist/data/agents.js +139 -0
  45. package/dist/data/conversations.d.ts +128 -0
  46. package/dist/data/conversations.js +522 -0
  47. package/dist/data/db/dbClient.d.ts +6 -0
  48. package/dist/data/db/dbClient.js +17 -0
  49. package/dist/env.d.ts +57 -0
  50. package/dist/env.js +1 -2
  51. package/dist/handlers/executionHandler.d.ts +39 -0
  52. package/dist/handlers/executionHandler.js +456 -0
  53. package/dist/index.d.ts +8 -29
  54. package/dist/index.js +5 -11386
  55. package/dist/instrumentation.d.ts +1 -2
  56. package/dist/instrumentation.js +66 -3
  57. package/dist/{logger2.js → logger.d.ts} +1 -2
  58. package/dist/logger.js +1 -1
  59. package/dist/middleware/api-key-auth.d.ts +26 -0
  60. package/dist/middleware/api-key-auth.js +240 -0
  61. package/dist/middleware/index.d.ts +2 -0
  62. package/dist/middleware/index.js +3 -0
  63. package/dist/openapi.d.ts +4 -0
  64. package/dist/openapi.js +54 -0
  65. package/dist/routes/agents.d.ts +12 -0
  66. package/dist/routes/agents.js +147 -0
  67. package/dist/routes/chat.d.ts +13 -0
  68. package/dist/routes/chat.js +293 -0
  69. package/dist/routes/chatDataStream.d.ts +13 -0
  70. package/dist/routes/chatDataStream.js +352 -0
  71. package/dist/routes/mcp.d.ts +13 -0
  72. package/dist/routes/mcp.js +495 -0
  73. package/dist/services/AgentSession.d.ts +356 -0
  74. package/dist/services/AgentSession.js +1208 -0
  75. package/dist/services/ArtifactParser.d.ts +105 -0
  76. package/dist/services/ArtifactParser.js +338 -0
  77. package/dist/services/ArtifactService.d.ts +123 -0
  78. package/dist/services/ArtifactService.js +612 -0
  79. package/dist/services/BaseCompressor.d.ts +183 -0
  80. package/dist/services/BaseCompressor.js +504 -0
  81. package/dist/services/ConversationCompressor.d.ts +32 -0
  82. package/dist/services/ConversationCompressor.js +91 -0
  83. package/dist/services/IncrementalStreamParser.d.ts +98 -0
  84. package/dist/services/IncrementalStreamParser.js +327 -0
  85. package/dist/services/MidGenerationCompressor.d.ts +63 -0
  86. package/dist/services/MidGenerationCompressor.js +104 -0
  87. package/dist/services/PendingToolApprovalManager.d.ts +62 -0
  88. package/dist/services/PendingToolApprovalManager.js +133 -0
  89. package/dist/services/ResponseFormatter.d.ts +39 -0
  90. package/dist/services/ResponseFormatter.js +152 -0
  91. package/dist/tools/NativeSandboxExecutor.d.ts +38 -0
  92. package/dist/tools/NativeSandboxExecutor.js +432 -0
  93. package/dist/tools/SandboxExecutorFactory.d.ts +36 -0
  94. package/dist/tools/SandboxExecutorFactory.js +80 -0
  95. package/dist/tools/VercelSandboxExecutor.d.ts +71 -0
  96. package/dist/tools/VercelSandboxExecutor.js +340 -0
  97. package/dist/tools/distill-conversation-history-tool.d.ts +62 -0
  98. package/dist/tools/distill-conversation-history-tool.js +206 -0
  99. package/dist/tools/distill-conversation-tool.d.ts +41 -0
  100. package/dist/tools/distill-conversation-tool.js +141 -0
  101. package/dist/tools/sandbox-utils.d.ts +18 -0
  102. package/dist/tools/sandbox-utils.js +53 -0
  103. package/dist/types/chat.d.ts +27 -0
  104. package/dist/types/chat.js +1 -0
  105. package/dist/types/execution-context.d.ts +46 -0
  106. package/dist/types/execution-context.js +27 -0
  107. package/dist/types/xml.d.ts +5 -0
  108. package/dist/utils/SchemaProcessor.d.ts +52 -0
  109. package/dist/utils/SchemaProcessor.js +182 -0
  110. package/dist/utils/agent-operations.d.ts +62 -0
  111. package/dist/utils/agent-operations.js +53 -0
  112. package/dist/utils/artifact-component-schema.d.ts +42 -0
  113. package/dist/utils/artifact-component-schema.js +186 -0
  114. package/dist/utils/cleanup.d.ts +21 -0
  115. package/dist/utils/cleanup.js +59 -0
  116. package/dist/utils/data-component-schema.d.ts +2 -0
  117. package/dist/utils/data-component-schema.js +3 -0
  118. package/dist/utils/default-status-schemas.d.ts +20 -0
  119. package/dist/utils/default-status-schemas.js +24 -0
  120. package/dist/utils/json-postprocessor.d.ts +13 -0
  121. package/dist/{json-postprocessor.cjs → utils/json-postprocessor.js} +1 -2
  122. package/dist/utils/model-context-utils.d.ts +39 -0
  123. package/dist/utils/model-context-utils.js +181 -0
  124. package/dist/utils/model-resolver.d.ts +6 -0
  125. package/dist/utils/model-resolver.js +34 -0
  126. package/dist/utils/schema-validation.d.ts +44 -0
  127. package/dist/utils/schema-validation.js +97 -0
  128. package/dist/utils/stream-helpers.d.ts +197 -0
  129. package/dist/utils/stream-helpers.js +518 -0
  130. package/dist/utils/stream-registry.d.ts +22 -0
  131. package/dist/utils/stream-registry.js +34 -0
  132. package/dist/utils/token-estimator.d.ts +69 -0
  133. package/dist/utils/token-estimator.js +53 -0
  134. package/dist/utils/tracer.d.ts +7 -0
  135. package/dist/utils/tracer.js +7 -0
  136. package/package.json +4 -20
  137. package/dist/SandboxExecutorFactory.cjs +0 -895
  138. package/dist/SandboxExecutorFactory.js +0 -893
  139. package/dist/SandboxExecutorFactory.js.map +0 -1
  140. package/dist/chunk-VBDAOXYI.cjs +0 -927
  141. package/dist/chunk-VBDAOXYI.js +0 -832
  142. package/dist/chunk-VBDAOXYI.js.map +0 -1
  143. package/dist/chunk.cjs +0 -34
  144. package/dist/conversations.cjs +0 -7
  145. package/dist/conversations.js +0 -7
  146. package/dist/conversations2.cjs +0 -209
  147. package/dist/conversations2.js +0 -180
  148. package/dist/conversations2.js.map +0 -1
  149. package/dist/dbClient.cjs +0 -9676
  150. package/dist/dbClient.js +0 -9670
  151. package/dist/dbClient.js.map +0 -1
  152. package/dist/dbClient2.cjs +0 -5
  153. package/dist/dbClient2.js +0 -5
  154. package/dist/env.cjs +0 -59
  155. package/dist/env.js.map +0 -1
  156. package/dist/execution-limits.cjs +0 -260
  157. package/dist/execution-limits.js +0 -63
  158. package/dist/execution-limits.js.map +0 -1
  159. package/dist/index.cjs +0 -11411
  160. package/dist/index.d.cts +0 -36
  161. package/dist/index.d.cts.map +0 -1
  162. package/dist/index.d.ts.map +0 -1
  163. package/dist/index.js.map +0 -1
  164. package/dist/instrumentation.cjs +0 -12
  165. package/dist/instrumentation.d.cts +0 -18
  166. package/dist/instrumentation.d.cts.map +0 -1
  167. package/dist/instrumentation.d.ts.map +0 -1
  168. package/dist/instrumentation2.cjs +0 -116
  169. package/dist/instrumentation2.js +0 -69
  170. package/dist/instrumentation2.js.map +0 -1
  171. package/dist/json-postprocessor.js +0 -20
  172. package/dist/json-postprocessor.js.map +0 -1
  173. package/dist/logger.cjs +0 -5
  174. package/dist/logger2.cjs +0 -1
  175. package/dist/nodefs.cjs +0 -29
  176. package/dist/nodefs.js +0 -27
  177. package/dist/nodefs.js.map +0 -1
  178. package/dist/opfs-ahp.cjs +0 -367
  179. package/dist/opfs-ahp.js +0 -368
  180. package/dist/opfs-ahp.js.map +0 -1
@@ -0,0 +1,1927 @@
1
+ import { getLogger } from "../logger.js";
2
+ import dbClient_default from "../data/db/dbClient.js";
3
+ import { AGENT_EXECUTION_MAX_GENERATION_STEPS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_VCPUS_DEFAULT, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS, LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS } from "../constants/execution-limits/index.js";
4
+ import { toolSessionManager } from "./ToolSessionManager.js";
5
+ import { getCompressionConfigForModel } from "../utils/model-context-utils.js";
6
+ import { setSpanWithError as setSpanWithError$1, tracer } from "../utils/tracer.js";
7
+ import { getModelAwareCompressionConfig } from "../services/BaseCompressor.js";
8
+ import "../services/ConversationCompressor.js";
9
+ import { createDefaultConversationHistoryConfig, getConversationHistoryWithCompression } from "../data/conversations.js";
10
+ import { getStreamHelper } from "../utils/stream-registry.js";
11
+ import { agentSessionManager } from "../services/AgentSession.js";
12
+ import { IncrementalStreamParser } from "../services/IncrementalStreamParser.js";
13
+ import { MidGenerationCompressor } from "../services/MidGenerationCompressor.js";
14
+ import { pendingToolApprovalManager } from "../services/PendingToolApprovalManager.js";
15
+ import { ResponseFormatter } from "../services/ResponseFormatter.js";
16
+ import { generateToolId } from "../utils/agent-operations.js";
17
+ import { jsonSchemaToZod } from "../utils/data-component-schema.js";
18
+ import { ArtifactCreateSchema, ArtifactReferenceSchema } from "../utils/artifact-component-schema.js";
19
+ import { withJsonPostProcessing } from "../utils/json-postprocessor.js";
20
+ import { calculateBreakdownTotal, estimateTokens } from "../utils/token-estimator.js";
21
+ import { createDelegateToAgentTool, createTransferToAgentTool } from "./relationTools.js";
22
+ import { SystemPromptBuilder } from "./SystemPromptBuilder.js";
23
+ import { Phase1Config } from "./versions/v1/Phase1Config.js";
24
+ import { Phase2Config } from "./versions/v1/Phase2Config.js";
25
+ import { z } from "@hono/zod-openapi";
26
+ import { ContextResolver, CredentialStuffer, MCPServerType, MCPTransportType, McpClient, ModelFactory, TemplateEngine, agentHasArtifactComponents, createMessage, generateId, getContextConfigById, getCredentialReference, getFullAgentDefinition, getFunction, getFunctionToolsForSubAgent, getLedgerArtifacts, getToolsForAgent, getUserScopedCredentialReference, listTaskIdsByContextId, parseEmbeddedJson } from "@inkeep/agents-core";
27
+ import { SpanStatusCode, trace } from "@opentelemetry/api";
28
+ import { generateObject, generateText, streamObject, streamText, tool } from "ai";
29
+
30
+ //#region src/agents/Agent.ts
31
+ /**
32
+ * Creates a stopWhen condition that stops when any tool call name starts with the given prefix
33
+ * @param prefix - The prefix to check for in tool call names
34
+ * @returns A function that can be used as a stopWhen condition
35
+ */
36
+ function hasToolCallWithPrefix(prefix) {
37
+ return ({ steps }) => {
38
+ const last = steps.at(-1);
39
+ if (last && "toolCalls" in last && last.toolCalls) return last.toolCalls.some((tc) => tc.toolName.startsWith(prefix));
40
+ return false;
41
+ };
42
+ }
43
+ const logger = getLogger("Agent");
44
+ function validateModel(modelString, modelType) {
45
+ if (!modelString?.trim()) throw new Error(`${modelType} model is required. Please configure models at the project level.`);
46
+ return modelString.trim();
47
+ }
48
+ function isValidTool(tool$1) {
49
+ return tool$1 && typeof tool$1 === "object" && typeof tool$1.description === "string" && tool$1.inputSchema && typeof tool$1.execute === "function";
50
+ }
51
+ var Agent = class {
52
+ config;
53
+ systemPromptBuilder = new SystemPromptBuilder("v1", new Phase1Config());
54
+ credentialStuffer;
55
+ streamHelper;
56
+ streamRequestId;
57
+ conversationId;
58
+ delegationId;
59
+ artifactComponents = [];
60
+ isDelegatedAgent = false;
61
+ contextResolver;
62
+ credentialStoreRegistry;
63
+ mcpClientCache = /* @__PURE__ */ new Map();
64
+ mcpConnectionLocks = /* @__PURE__ */ new Map();
65
+ currentCompressor = null;
66
+ constructor(config, credentialStoreRegistry) {
67
+ this.artifactComponents = config.artifactComponents || [];
68
+ let processedDataComponents = config.dataComponents || [];
69
+ if (processedDataComponents.length > 0) processedDataComponents.push({
70
+ id: "text-content",
71
+ name: "Text",
72
+ description: "Natural conversational text for the user - write naturally without mentioning technical details. Avoid redundancy and repetition with data components.",
73
+ props: {
74
+ type: "object",
75
+ properties: { text: {
76
+ type: "string",
77
+ description: "Natural conversational text - respond as if having a normal conversation, never mention JSON, components, schemas, or technical implementation. Avoid redundancy and repetition with data components."
78
+ } },
79
+ required: ["text"]
80
+ }
81
+ });
82
+ if (this.artifactComponents.length > 0 && config.dataComponents && config.dataComponents.length > 0) processedDataComponents = [ArtifactReferenceSchema.getDataComponent(config.tenantId, config.projectId), ...processedDataComponents];
83
+ this.config = {
84
+ ...config,
85
+ dataComponents: processedDataComponents,
86
+ conversationHistoryConfig: config.conversationHistoryConfig || createDefaultConversationHistoryConfig()
87
+ };
88
+ this.credentialStoreRegistry = credentialStoreRegistry;
89
+ if (credentialStoreRegistry) {
90
+ this.contextResolver = new ContextResolver(config.tenantId, config.projectId, dbClient_default, credentialStoreRegistry);
91
+ this.credentialStuffer = new CredentialStuffer(credentialStoreRegistry, this.contextResolver);
92
+ }
93
+ }
94
+ /**
95
+ * Get the maximum number of generation steps for this agent
96
+ * Uses agent's stopWhen.stepCountIs config or defaults to AGENT_EXECUTION_MAX_GENERATION_STEPS
97
+ */
98
+ getMaxGenerationSteps() {
99
+ return this.config.stopWhen?.stepCountIs ?? AGENT_EXECUTION_MAX_GENERATION_STEPS;
100
+ }
101
+ /**
102
+ * Sanitizes tool names at runtime for AI SDK compatibility.
103
+ * The AI SDK requires tool names to match pattern ^[a-zA-Z0-9_-]{1,128}$
104
+ */
105
+ sanitizeToolsForAISDK(tools) {
106
+ const sanitizedTools = {};
107
+ for (const [originalKey, toolDef] of Object.entries(tools)) {
108
+ let sanitizedKey = originalKey.replace(/[^a-zA-Z0-9_-]/g, "_");
109
+ sanitizedKey = sanitizedKey.replace(/_+/g, "_");
110
+ sanitizedKey = sanitizedKey.replace(/^_+|_+$/g, "");
111
+ if (!sanitizedKey || sanitizedKey.length === 0) sanitizedKey = "unnamed_tool";
112
+ if (sanitizedKey.length > 100) sanitizedKey = sanitizedKey.substring(0, 100);
113
+ let sanitizedId = (toolDef.id || originalKey).replace(/[^a-zA-Z0-9_.-]/g, "_");
114
+ sanitizedId = sanitizedId.replace(/_+/g, "_");
115
+ sanitizedId = sanitizedId.replace(/^_+|_+$/g, "");
116
+ if (sanitizedId.length > 128) sanitizedId = sanitizedId.substring(0, 128);
117
+ sanitizedTools[sanitizedKey] = {
118
+ ...toolDef,
119
+ id: sanitizedId
120
+ };
121
+ }
122
+ return sanitizedTools;
123
+ }
124
+ #createRelationToolName(prefix, targetId) {
125
+ return `${prefix}_to_${targetId.toLowerCase().replace(/\s+/g, "_")}`;
126
+ }
127
+ #getRelationshipIdForTool(toolName, toolType) {
128
+ if (toolType === "mcp") return (this.config.tools?.find((tool$1) => {
129
+ if (tool$1.config?.type !== "mcp") return false;
130
+ if (tool$1.availableTools?.some((available) => available.name === toolName)) return true;
131
+ if (tool$1.config.mcp.activeTools?.includes(toolName)) return true;
132
+ return tool$1.name === toolName;
133
+ }))?.relationshipId;
134
+ if (toolType === "delegation") return this.config.delegateRelations.find((relation) => this.#createRelationToolName("delegate", relation.config.id) === toolName)?.config.relationId;
135
+ }
136
+ /**
137
+ * Get the primary model settings for text generation and thinking
138
+ * Requires model to be configured at project level
139
+ */
140
+ getPrimaryModel() {
141
+ if (!this.config.models?.base) throw new Error("Base model configuration is required. Please configure models at the project level.");
142
+ return {
143
+ model: validateModel(this.config.models.base.model, "Base"),
144
+ providerOptions: this.config.models.base.providerOptions
145
+ };
146
+ }
147
+ /**
148
+ * Get the model settings for structured output generation
149
+ * Falls back to base model if structured output not configured
150
+ */
151
+ getStructuredOutputModel() {
152
+ if (!this.config.models) throw new Error("Model configuration is required. Please configure models at the project level.");
153
+ const structuredConfig = this.config.models.structuredOutput;
154
+ const baseConfig = this.config.models.base;
155
+ if (structuredConfig) return {
156
+ model: validateModel(structuredConfig.model, "Structured output"),
157
+ providerOptions: structuredConfig.providerOptions
158
+ };
159
+ if (!baseConfig) throw new Error("Base model configuration is required for structured output fallback. Please configure models at the project level.");
160
+ return {
161
+ model: validateModel(baseConfig.model, "Base (fallback for structured output)"),
162
+ providerOptions: baseConfig.providerOptions
163
+ };
164
+ }
165
+ /**
166
+ * Get the model settings for summarization/distillation
167
+ * Falls back to base model if summarizer not configured
168
+ */
169
+ getSummarizerModel() {
170
+ if (!this.config.models) throw new Error("Model configuration is required. Please configure models at the project level.");
171
+ const summarizerConfig = this.config.models.summarizer;
172
+ const baseConfig = this.config.models.base;
173
+ if (summarizerConfig) return {
174
+ model: validateModel(summarizerConfig.model, "Summarizer"),
175
+ providerOptions: summarizerConfig.providerOptions
176
+ };
177
+ if (!baseConfig) throw new Error("Base model configuration is required for summarizer fallback. Please configure models at the project level.");
178
+ return {
179
+ model: validateModel(baseConfig.model, "Base (fallback for summarizer)"),
180
+ providerOptions: baseConfig.providerOptions
181
+ };
182
+ }
183
+ setConversationId(conversationId) {
184
+ this.conversationId = conversationId;
185
+ }
186
+ /**
187
+ * Simple compression fallback: drop oldest messages to fit under token limit
188
+ */
189
+ /**
190
+ * Set delegation status for this agent instance
191
+ */
192
+ setDelegationStatus(isDelegated) {
193
+ this.isDelegatedAgent = isDelegated;
194
+ }
195
+ /**
196
+ * Set delegation ID for this agent instance
197
+ */
198
+ setDelegationId(delegationId) {
199
+ this.delegationId = delegationId;
200
+ }
201
+ /**
202
+ * Get streaming helper if this agent should stream to user
203
+ * Returns undefined for delegated agents to prevent streaming data operations to user
204
+ */
205
+ getStreamingHelper() {
206
+ return this.isDelegatedAgent ? void 0 : this.streamHelper;
207
+ }
208
+ /**
209
+ * Wraps a tool with streaming lifecycle tracking (start, complete, error) and AgentSession recording
210
+ */
211
+ wrapToolWithStreaming(toolName, toolDefinition, streamRequestId, toolType, options) {
212
+ if (!toolDefinition || typeof toolDefinition !== "object" || !("execute" in toolDefinition)) return toolDefinition;
213
+ const relationshipId = this.#getRelationshipIdForTool(toolName, toolType);
214
+ const originalExecute = toolDefinition.execute;
215
+ return {
216
+ ...toolDefinition,
217
+ execute: async (args, context$1) => {
218
+ const startTime = Date.now();
219
+ const toolCallId = context$1?.toolCallId || generateToolId();
220
+ const activeSpan = trace.getActiveSpan();
221
+ if (activeSpan) {
222
+ const attributes = {
223
+ "conversation.id": this.conversationId,
224
+ "tool.purpose": toolDefinition.description || "No description provided",
225
+ "ai.toolType": toolType || "unknown",
226
+ "subAgent.name": this.config.name || "unknown",
227
+ "subAgent.id": this.config.id || "unknown",
228
+ "agent.id": this.config.agentId || "unknown"
229
+ };
230
+ if (options?.mcpServerId) attributes["ai.toolCall.mcpServerId"] = options.mcpServerId;
231
+ if (options?.mcpServerName) attributes["ai.toolCall.mcpServerName"] = options.mcpServerName;
232
+ activeSpan.setAttributes(attributes);
233
+ }
234
+ const isInternalTool = toolName.includes("save_tool_result") || toolName.includes("thinking_complete") || toolName.startsWith("transfer_to_");
235
+ const needsApproval = options?.needsApproval || false;
236
+ if (streamRequestId && !isInternalTool) {
237
+ const toolCallData = {
238
+ toolName,
239
+ input: args,
240
+ toolCallId,
241
+ relationshipId
242
+ };
243
+ if (needsApproval) {
244
+ toolCallData.needsApproval = true;
245
+ toolCallData.conversationId = this.conversationId;
246
+ }
247
+ await agentSessionManager.recordEvent(streamRequestId, "tool_call", this.config.id, toolCallData);
248
+ }
249
+ try {
250
+ const result = await originalExecute(args, context$1);
251
+ const duration = Date.now() - startTime;
252
+ const toolResultConversationId = this.getToolResultConversationId();
253
+ if (streamRequestId && !isInternalTool && toolResultConversationId) try {
254
+ const messagePayload = {
255
+ id: generateId(),
256
+ tenantId: this.config.tenantId,
257
+ projectId: this.config.projectId,
258
+ conversationId: toolResultConversationId,
259
+ role: "assistant",
260
+ content: { text: this.formatToolResult(toolName, args, result, toolCallId) },
261
+ visibility: "internal",
262
+ messageType: "tool-result",
263
+ fromSubAgentId: this.config.id,
264
+ metadata: { a2a_metadata: {
265
+ toolName,
266
+ toolCallId,
267
+ timestamp: Date.now(),
268
+ delegationId: this.delegationId,
269
+ isDelegated: this.isDelegatedAgent
270
+ } }
271
+ };
272
+ await createMessage(dbClient_default)(messagePayload);
273
+ } catch (error) {
274
+ logger.warn({
275
+ error,
276
+ toolName,
277
+ toolCallId,
278
+ conversationId: toolResultConversationId
279
+ }, "Failed to store tool result in conversation history");
280
+ }
281
+ if (streamRequestId && !isInternalTool) agentSessionManager.recordEvent(streamRequestId, "tool_result", this.config.id, {
282
+ toolName,
283
+ output: result,
284
+ toolCallId,
285
+ duration,
286
+ relationshipId,
287
+ needsApproval
288
+ });
289
+ return result;
290
+ } catch (error) {
291
+ const duration = Date.now() - startTime;
292
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
293
+ if (streamRequestId && !isInternalTool) agentSessionManager.recordEvent(streamRequestId, "tool_result", this.config.id, {
294
+ toolName,
295
+ output: null,
296
+ toolCallId,
297
+ duration,
298
+ error: errorMessage,
299
+ relationshipId,
300
+ needsApproval
301
+ });
302
+ throw error;
303
+ }
304
+ }
305
+ };
306
+ }
307
+ getRelationTools(runtimeContext, sessionId) {
308
+ const { transferRelations = [], delegateRelations = [] } = this.config;
309
+ return Object.fromEntries([...transferRelations.map((agentConfig) => {
310
+ const toolName = this.#createRelationToolName("transfer", agentConfig.id);
311
+ return [toolName, this.wrapToolWithStreaming(toolName, createTransferToAgentTool({
312
+ transferConfig: agentConfig,
313
+ callingAgentId: this.config.id,
314
+ subAgent: this,
315
+ streamRequestId: runtimeContext?.metadata?.streamRequestId
316
+ }), runtimeContext?.metadata?.streamRequestId, "transfer")];
317
+ }), ...delegateRelations.map((relation) => {
318
+ const toolName = this.#createRelationToolName("delegate", relation.config.id);
319
+ return [toolName, this.wrapToolWithStreaming(toolName, createDelegateToAgentTool({
320
+ delegateConfig: relation,
321
+ callingAgentId: this.config.id,
322
+ tenantId: this.config.tenantId,
323
+ projectId: this.config.projectId,
324
+ agentId: this.config.agentId,
325
+ contextId: runtimeContext?.contextId || "default",
326
+ metadata: runtimeContext?.metadata || {
327
+ conversationId: runtimeContext?.contextId || "default",
328
+ threadId: runtimeContext?.contextId || "default",
329
+ streamRequestId: runtimeContext?.metadata?.streamRequestId,
330
+ apiKey: runtimeContext?.metadata?.apiKey
331
+ },
332
+ sessionId,
333
+ subAgent: this,
334
+ credentialStoreRegistry: this.credentialStoreRegistry
335
+ }), runtimeContext?.metadata?.streamRequestId, "delegation")];
336
+ })]);
337
+ }
338
+ async getMcpTools(sessionId, streamRequestId) {
339
+ const mcpTools = this.config.tools?.filter((tool$1) => {
340
+ return tool$1.config?.type === "mcp";
341
+ }) || [];
342
+ const tools = await Promise.all(mcpTools.map((tool$1) => this.getMcpTool(tool$1)) || []) || [];
343
+ if (!sessionId) {
344
+ const wrappedTools$1 = {};
345
+ for (const toolSet of tools) for (const [toolName, toolDef] of Object.entries(toolSet.tools)) {
346
+ const needsApproval = toolSet.toolPolicies?.[toolName]?.needsApproval || false;
347
+ const enhancedTool = {
348
+ ...toolDef,
349
+ needsApproval
350
+ };
351
+ wrappedTools$1[toolName] = this.wrapToolWithStreaming(toolName, enhancedTool, streamRequestId, "mcp", {
352
+ needsApproval,
353
+ mcpServerId: toolSet.mcpServerId,
354
+ mcpServerName: toolSet.mcpServerName
355
+ });
356
+ }
357
+ return wrappedTools$1;
358
+ }
359
+ const wrappedTools = {};
360
+ for (const toolResult of tools) for (const [toolName, originalTool] of Object.entries(toolResult.tools)) {
361
+ if (!isValidTool(originalTool)) {
362
+ logger.error({ toolName }, "Invalid MCP tool structure - missing required properties");
363
+ continue;
364
+ }
365
+ const needsApproval = toolResult.toolPolicies?.[toolName]?.needsApproval || false;
366
+ logger.debug({
367
+ toolName,
368
+ toolPolicies: toolResult.toolPolicies,
369
+ needsApproval,
370
+ policyForThisTool: toolResult.toolPolicies?.[toolName]
371
+ }, "Tool approval check");
372
+ const sessionWrappedTool = tool({
373
+ description: originalTool.description,
374
+ inputSchema: originalTool.inputSchema,
375
+ execute: async (args, { toolCallId }) => {
376
+ let processedArgs;
377
+ try {
378
+ processedArgs = parseEmbeddedJson(args);
379
+ if (JSON.stringify(args) !== JSON.stringify(processedArgs)) logger.warn({
380
+ toolName,
381
+ toolCallId
382
+ }, "Fixed stringified JSON parameters (indicates schema ambiguity)");
383
+ } catch (error) {
384
+ logger.warn({
385
+ toolName,
386
+ toolCallId,
387
+ error: error.message
388
+ }, "Failed to parse embedded JSON, using original args");
389
+ processedArgs = args;
390
+ }
391
+ const finalArgs = processedArgs;
392
+ if (needsApproval) {
393
+ logger.info({
394
+ toolName,
395
+ toolCallId,
396
+ args: finalArgs
397
+ }, "Tool requires approval - waiting for user response");
398
+ const currentSpan = trace.getActiveSpan();
399
+ if (currentSpan) currentSpan.addEvent("tool.approval.requested", {
400
+ "tool.name": toolName,
401
+ "tool.callId": toolCallId,
402
+ "subAgent.id": this.config.id
403
+ });
404
+ tracer.startActiveSpan("tool.approval_requested", { attributes: {
405
+ "tool.name": toolName,
406
+ "tool.callId": toolCallId,
407
+ "subAgent.id": this.config.id,
408
+ "subAgent.name": this.config.name
409
+ } }, (requestSpan) => {
410
+ requestSpan.setStatus({ code: SpanStatusCode.OK });
411
+ requestSpan.end();
412
+ });
413
+ const approvalResult = await pendingToolApprovalManager.waitForApproval(toolCallId, toolName, args, this.conversationId || "unknown", this.config.id);
414
+ if (!approvalResult.approved) return tracer.startActiveSpan("tool.approval_denied", { attributes: {
415
+ "tool.name": toolName,
416
+ "tool.callId": toolCallId,
417
+ "subAgent.id": this.config.id,
418
+ "subAgent.name": this.config.name
419
+ } }, (denialSpan) => {
420
+ logger.info({
421
+ toolName,
422
+ toolCallId,
423
+ reason: approvalResult.reason
424
+ }, "Tool execution denied by user");
425
+ denialSpan.setStatus({ code: SpanStatusCode.OK });
426
+ denialSpan.end();
427
+ return `User denied approval to run this tool: ${approvalResult.reason}`;
428
+ });
429
+ tracer.startActiveSpan("tool.approval_approved", { attributes: {
430
+ "tool.name": toolName,
431
+ "tool.callId": toolCallId,
432
+ "subAgent.id": this.config.id,
433
+ "subAgent.name": this.config.name
434
+ } }, (approvedSpan) => {
435
+ logger.info({
436
+ toolName,
437
+ toolCallId
438
+ }, "Tool approved, continuing with execution");
439
+ approvedSpan.setStatus({ code: SpanStatusCode.OK });
440
+ approvedSpan.end();
441
+ });
442
+ }
443
+ logger.debug({
444
+ toolName,
445
+ toolCallId
446
+ }, "MCP Tool Called");
447
+ try {
448
+ const rawResult = await originalTool.execute(finalArgs, { toolCallId });
449
+ if (rawResult && typeof rawResult === "object" && rawResult.isError) {
450
+ const errorMessage = rawResult.content?.[0]?.text || "MCP tool returned an error";
451
+ logger.error({
452
+ toolName,
453
+ toolCallId,
454
+ errorMessage,
455
+ rawResult
456
+ }, "MCP tool returned error status");
457
+ toolSessionManager.recordToolResult(sessionId, {
458
+ toolCallId,
459
+ toolName,
460
+ args: finalArgs,
461
+ result: {
462
+ error: errorMessage,
463
+ failed: true
464
+ },
465
+ timestamp: Date.now()
466
+ });
467
+ if (streamRequestId) {
468
+ const relationshipId = this.#getRelationshipIdForTool(toolName, "mcp");
469
+ agentSessionManager.recordEvent(streamRequestId, "error", this.config.id, {
470
+ message: `MCP tool "${toolName}" failed: ${errorMessage}`,
471
+ code: "mcp_tool_error",
472
+ severity: "error",
473
+ context: {
474
+ toolName,
475
+ toolCallId,
476
+ errorMessage,
477
+ relationshipId
478
+ }
479
+ });
480
+ }
481
+ const activeSpan = trace.getActiveSpan();
482
+ if (activeSpan) {
483
+ const error = /* @__PURE__ */ new Error(`Tool "${toolName}" failed: ${errorMessage}. This tool is currently unavailable. Please try a different approach or inform the user of the issue.`);
484
+ activeSpan.recordException(error);
485
+ activeSpan.setStatus({
486
+ code: SpanStatusCode.ERROR,
487
+ message: `MCP tool returned error: ${errorMessage}`
488
+ });
489
+ }
490
+ throw new Error(`Tool "${toolName}" failed: ${errorMessage}. This tool is currently unavailable. Please try a different approach or inform the user of the issue.`);
491
+ }
492
+ const parsedResult = parseEmbeddedJson(rawResult);
493
+ const enhancedResult = this.enhanceToolResultWithStructureHints(parsedResult);
494
+ toolSessionManager.recordToolResult(sessionId, {
495
+ toolCallId,
496
+ toolName,
497
+ args: finalArgs,
498
+ result: enhancedResult,
499
+ timestamp: Date.now()
500
+ });
501
+ return {
502
+ result: enhancedResult,
503
+ toolCallId
504
+ };
505
+ } catch (error) {
506
+ logger.error({
507
+ toolName,
508
+ toolCallId,
509
+ error
510
+ }, "MCP tool execution failed");
511
+ throw error;
512
+ }
513
+ }
514
+ });
515
+ wrappedTools[toolName] = this.wrapToolWithStreaming(toolName, sessionWrappedTool, streamRequestId, "mcp", {
516
+ needsApproval,
517
+ mcpServerId: toolResult.mcpServerId,
518
+ mcpServerName: toolResult.mcpServerName
519
+ });
520
+ }
521
+ return wrappedTools;
522
+ }
523
+ /**
524
+ * Convert database McpTool to builder MCPToolConfig format
525
+ */
526
+ convertToMCPToolConfig(tool$1, agentToolRelationHeaders) {
527
+ if (tool$1.config.type !== "mcp") throw new Error(`Cannot convert non-MCP tool to MCP config: ${tool$1.id}`);
528
+ return {
529
+ id: tool$1.id,
530
+ name: tool$1.name,
531
+ description: tool$1.name,
532
+ serverUrl: tool$1.config.mcp.server.url,
533
+ activeTools: tool$1.config.mcp.activeTools,
534
+ mcpType: tool$1.config.mcp.server.url.includes("api.nango.dev") ? MCPServerType.nango : MCPServerType.generic,
535
+ transport: tool$1.config.mcp.transport,
536
+ headers: {
537
+ ...tool$1.headers,
538
+ ...agentToolRelationHeaders
539
+ }
540
+ };
541
+ }
542
+ async getMcpTool(tool$1) {
543
+ const cacheKey = `${this.config.tenantId}-${this.config.projectId}-${tool$1.id}-${tool$1.credentialReferenceId || "no-cred"}`;
544
+ const credentialReferenceId = tool$1.credentialReferenceId;
545
+ const toolRelation = (await getToolsForAgent(dbClient_default)({ scopes: {
546
+ tenantId: this.config.tenantId,
547
+ projectId: this.config.projectId,
548
+ agentId: this.config.agentId,
549
+ subAgentId: this.config.id
550
+ } })).data.find((t) => t.toolId === tool$1.id);
551
+ const agentToolRelationHeaders = toolRelation?.headers || void 0;
552
+ const selectedTools = toolRelation?.selectedTools || void 0;
553
+ const toolPolicies = toolRelation?.toolPolicies || {};
554
+ let serverConfig;
555
+ const isUserScoped = tool$1.credentialScope === "user";
556
+ const userId = this.config.userId;
557
+ if (isUserScoped && userId && this.credentialStuffer) {
558
+ const userCredentialReference = await getUserScopedCredentialReference(dbClient_default)({
559
+ scopes: {
560
+ tenantId: this.config.tenantId,
561
+ projectId: this.config.projectId
562
+ },
563
+ toolId: tool$1.id,
564
+ userId
565
+ });
566
+ if (userCredentialReference) {
567
+ const storeReference = {
568
+ credentialStoreId: userCredentialReference.credentialStoreId,
569
+ retrievalParams: userCredentialReference.retrievalParams || {}
570
+ };
571
+ serverConfig = await this.credentialStuffer.buildMcpServerConfig({
572
+ tenantId: this.config.tenantId,
573
+ projectId: this.config.projectId,
574
+ contextConfigId: this.config.contextConfigId || void 0,
575
+ conversationId: this.conversationId || void 0
576
+ }, this.convertToMCPToolConfig(tool$1, agentToolRelationHeaders), storeReference, selectedTools);
577
+ } else {
578
+ logger.warn({
579
+ toolId: tool$1.id,
580
+ userId
581
+ }, "User-scoped tool has no credential connected for this user");
582
+ serverConfig = await this.credentialStuffer.buildMcpServerConfig({
583
+ tenantId: this.config.tenantId,
584
+ projectId: this.config.projectId,
585
+ contextConfigId: this.config.contextConfigId || void 0,
586
+ conversationId: this.conversationId || void 0
587
+ }, this.convertToMCPToolConfig(tool$1, agentToolRelationHeaders), void 0, selectedTools);
588
+ }
589
+ } else if (credentialReferenceId && this.credentialStuffer) {
590
+ const credentialReference = await getCredentialReference(dbClient_default)({
591
+ scopes: {
592
+ tenantId: this.config.tenantId,
593
+ projectId: this.config.projectId
594
+ },
595
+ id: credentialReferenceId
596
+ });
597
+ if (!credentialReference) throw new Error(`Credential store not found: ${credentialReferenceId}`);
598
+ const storeReference = {
599
+ credentialStoreId: credentialReference.credentialStoreId,
600
+ retrievalParams: credentialReference.retrievalParams || {}
601
+ };
602
+ serverConfig = await this.credentialStuffer.buildMcpServerConfig({
603
+ tenantId: this.config.tenantId,
604
+ projectId: this.config.projectId,
605
+ contextConfigId: this.config.contextConfigId || void 0,
606
+ conversationId: this.conversationId || void 0
607
+ }, this.convertToMCPToolConfig(tool$1, agentToolRelationHeaders), storeReference, selectedTools);
608
+ } else if (this.credentialStuffer) serverConfig = await this.credentialStuffer.buildMcpServerConfig({
609
+ tenantId: this.config.tenantId,
610
+ projectId: this.config.projectId,
611
+ contextConfigId: this.config.contextConfigId || void 0,
612
+ conversationId: this.conversationId || void 0
613
+ }, this.convertToMCPToolConfig(tool$1, agentToolRelationHeaders), void 0, selectedTools);
614
+ else {
615
+ if (tool$1.config.type !== "mcp") throw new Error(`Cannot build server config for non-MCP tool: ${tool$1.id}`);
616
+ serverConfig = {
617
+ type: tool$1.config.mcp.transport?.type || MCPTransportType.streamableHttp,
618
+ url: tool$1.config.mcp.server.url,
619
+ activeTools: tool$1.config.mcp.activeTools,
620
+ selectedTools,
621
+ headers: agentToolRelationHeaders
622
+ };
623
+ }
624
+ if (serverConfig.url?.toString().includes("composio.dev")) {
625
+ const urlObj = new URL(serverConfig.url.toString());
626
+ if (isUserScoped && userId) urlObj.searchParams.set("user_id", userId);
627
+ else urlObj.searchParams.set("user_id", `${this.config.tenantId}||${this.config.projectId}`);
628
+ serverConfig.url = urlObj.toString();
629
+ }
630
+ logger.info({
631
+ toolName: tool$1.name,
632
+ credentialReferenceId,
633
+ transportType: serverConfig.type,
634
+ headers: tool$1.headers
635
+ }, "Built MCP server config with credentials");
636
+ let client = this.mcpClientCache.get(cacheKey);
637
+ if (client && !client.isConnected()) {
638
+ this.mcpClientCache.delete(cacheKey);
639
+ client = void 0;
640
+ }
641
+ if (!client) {
642
+ let connectionPromise = this.mcpConnectionLocks.get(cacheKey);
643
+ if (!connectionPromise) {
644
+ connectionPromise = this.createMcpConnection(tool$1, serverConfig);
645
+ this.mcpConnectionLocks.set(cacheKey, connectionPromise);
646
+ }
647
+ try {
648
+ client = await connectionPromise;
649
+ this.mcpClientCache.set(cacheKey, client);
650
+ } catch (error) {
651
+ this.mcpConnectionLocks.delete(cacheKey);
652
+ logger.error({
653
+ toolName: tool$1.name,
654
+ subAgentId: this.config.id,
655
+ cacheKey,
656
+ error: error instanceof Error ? error.message : String(error)
657
+ }, "MCP connection failed");
658
+ throw error;
659
+ }
660
+ }
661
+ const tools = await client.tools();
662
+ if (!tools || Object.keys(tools).length === 0) {
663
+ const streamRequestId = this.getStreamRequestId();
664
+ if (streamRequestId) tracer.startActiveSpan("ai.toolCall", { attributes: {
665
+ "ai.toolCall.name": tool$1.name,
666
+ "ai.toolCall.args": JSON.stringify({ operation: "mcp_tool_discovery" }),
667
+ "ai.toolCall.result": JSON.stringify({
668
+ status: "no_tools_available",
669
+ message: `MCP server has 0 effective tools. Double check the selected tools in your agent and the active tools in the MCP server configuration.`,
670
+ serverUrl: tool$1.config.type === "mcp" ? tool$1.config.mcp.server.url : "unknown",
671
+ originalToolName: tool$1.name
672
+ }),
673
+ "ai.toolType": "mcp",
674
+ "subAgent.name": this.config.name || "unknown",
675
+ "subAgent.id": this.config.id || "unknown",
676
+ "conversation.id": this.conversationId || "unknown",
677
+ "agent.id": this.config.agentId || "unknown",
678
+ "tenant.id": this.config.tenantId || "unknown",
679
+ "project.id": this.config.projectId || "unknown"
680
+ } }, (span) => {
681
+ setSpanWithError$1(span, /* @__PURE__ */ new Error(`0 effective tools available for ${tool$1.name}`));
682
+ agentSessionManager.recordEvent(streamRequestId, "error", this.config.id, {
683
+ message: `MCP server has 0 effective tools. Double check the selected tools in your graph and the active tools in the MCP server configuration.`,
684
+ code: "no_tools_available",
685
+ severity: "error",
686
+ context: {
687
+ toolName: tool$1.name,
688
+ serverUrl: tool$1.config.type === "mcp" ? tool$1.config.mcp.server.url : "unknown",
689
+ operation: "mcp_tool_discovery"
690
+ }
691
+ });
692
+ span.end();
693
+ });
694
+ }
695
+ return {
696
+ tools,
697
+ toolPolicies,
698
+ mcpServerId: tool$1.id,
699
+ mcpServerName: tool$1.name
700
+ };
701
+ }
702
+ async createMcpConnection(tool$1, serverConfig) {
703
+ const client = new McpClient({
704
+ name: tool$1.name,
705
+ server: serverConfig
706
+ });
707
+ try {
708
+ await client.connect();
709
+ return client;
710
+ } catch (error) {
711
+ logger.error({
712
+ toolName: tool$1.name,
713
+ subAgentId: this.config.id,
714
+ error: error instanceof Error ? error.message : String(error)
715
+ }, "Agent failed to connect to MCP server");
716
+ if (error instanceof Error) {
717
+ if (error?.cause && JSON.stringify(error.cause).includes("ECONNREFUSED")) throw new Error("Connection refused. Please check if the MCP server is running.");
718
+ if (error.message.includes("404")) throw new Error("Error accessing endpoint (HTTP 404)");
719
+ throw new Error(`MCP server connection failed: ${error.message}`);
720
+ }
721
+ throw error;
722
+ }
723
+ }
724
+ async getFunctionTools(sessionId, streamRequestId) {
725
+ const functionTools = {};
726
+ try {
727
+ const functionToolsData = (await getFunctionToolsForSubAgent(dbClient_default)({
728
+ scopes: {
729
+ tenantId: this.config.tenantId,
730
+ projectId: this.config.projectId,
731
+ agentId: this.config.agentId
732
+ },
733
+ subAgentId: this.config.id
734
+ })).data || [];
735
+ if (functionToolsData.length === 0) return functionTools;
736
+ const { SandboxExecutorFactory } = await import("../tools/SandboxExecutorFactory.js");
737
+ const sandboxExecutor = SandboxExecutorFactory.getInstance();
738
+ for (const functionToolDef of functionToolsData) {
739
+ const functionId = functionToolDef.functionId;
740
+ if (!functionId) {
741
+ logger.warn({ functionToolId: functionToolDef.id }, "Function tool missing functionId reference");
742
+ continue;
743
+ }
744
+ const functionData = await getFunction(dbClient_default)({
745
+ functionId,
746
+ scopes: {
747
+ tenantId: this.config.tenantId || "default",
748
+ projectId: this.config.projectId || "default"
749
+ }
750
+ });
751
+ if (!functionData) {
752
+ logger.warn({
753
+ functionId,
754
+ functionToolId: functionToolDef.id
755
+ }, "Function not found in functions table");
756
+ continue;
757
+ }
758
+ const zodSchema = jsonSchemaToZod(functionData.inputSchema);
759
+ const aiTool = tool({
760
+ description: functionToolDef.description || functionToolDef.name,
761
+ inputSchema: zodSchema,
762
+ execute: async (args, { toolCallId }) => {
763
+ let processedArgs;
764
+ try {
765
+ processedArgs = parseEmbeddedJson(args);
766
+ if (JSON.stringify(args) !== JSON.stringify(processedArgs)) logger.warn({
767
+ toolName: functionToolDef.name,
768
+ toolCallId
769
+ }, "Fixed stringified JSON parameters (indicates schema ambiguity)");
770
+ } catch (error) {
771
+ logger.warn({
772
+ toolName: functionToolDef.name,
773
+ toolCallId,
774
+ error: error.message
775
+ }, "Failed to parse embedded JSON, using original args");
776
+ processedArgs = args;
777
+ }
778
+ const finalArgs = processedArgs;
779
+ logger.debug({
780
+ toolName: functionToolDef.name,
781
+ toolCallId,
782
+ args: finalArgs
783
+ }, "Function Tool Called");
784
+ try {
785
+ const defaultSandboxConfig = {
786
+ provider: "native",
787
+ runtime: "node22",
788
+ timeout: FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT,
789
+ vcpus: FUNCTION_TOOL_SANDBOX_VCPUS_DEFAULT
790
+ };
791
+ const result = await sandboxExecutor.executeFunctionTool(functionToolDef.id, finalArgs, {
792
+ description: functionToolDef.description || functionToolDef.name,
793
+ inputSchema: functionData.inputSchema || {},
794
+ executeCode: functionData.executeCode,
795
+ dependencies: functionData.dependencies || {},
796
+ sandboxConfig: this.config.sandboxConfig || defaultSandboxConfig
797
+ });
798
+ toolSessionManager.recordToolResult(sessionId || "", {
799
+ toolCallId,
800
+ toolName: functionToolDef.name,
801
+ args: finalArgs,
802
+ result,
803
+ timestamp: Date.now()
804
+ });
805
+ return {
806
+ result,
807
+ toolCallId
808
+ };
809
+ } catch (error) {
810
+ logger.error({
811
+ toolName: functionToolDef.name,
812
+ toolCallId,
813
+ error: error instanceof Error ? error.message : String(error)
814
+ }, "Function tool execution failed");
815
+ throw error;
816
+ }
817
+ }
818
+ });
819
+ functionTools[functionToolDef.name] = this.wrapToolWithStreaming(functionToolDef.name, aiTool, streamRequestId || "", "tool");
820
+ }
821
+ } catch (error) {
822
+ logger.error({ error }, "Failed to load function tools from database");
823
+ }
824
+ return functionTools;
825
+ }
826
+ /**
827
+ * Get resolved context using ContextResolver - will return cached data or fetch fresh data as needed
828
+ */
829
+ async getResolvedContext(conversationId, headers$1) {
830
+ try {
831
+ if (!this.config.contextConfigId) {
832
+ logger.debug({ agentId: this.config.agentId }, "No context config found for agent");
833
+ return null;
834
+ }
835
+ const contextConfig = await getContextConfigById(dbClient_default)({
836
+ scopes: {
837
+ tenantId: this.config.tenantId,
838
+ projectId: this.config.projectId,
839
+ agentId: this.config.agentId
840
+ },
841
+ id: this.config.contextConfigId
842
+ });
843
+ if (!contextConfig) {
844
+ logger.warn({ contextConfigId: this.config.contextConfigId }, "Context config not found");
845
+ return null;
846
+ }
847
+ if (!this.contextResolver) throw new Error("Context resolver not found");
848
+ const result = await this.contextResolver.resolve(contextConfig, {
849
+ triggerEvent: "invocation",
850
+ conversationId,
851
+ headers: headers$1 || {},
852
+ tenantId: this.config.tenantId
853
+ });
854
+ const contextWithBuiltins = {
855
+ ...result.resolvedContext,
856
+ $env: process.env
857
+ };
858
+ logger.debug({
859
+ conversationId,
860
+ contextConfigId: contextConfig.id,
861
+ resolvedKeys: Object.keys(contextWithBuiltins),
862
+ cacheHits: result.cacheHits.length,
863
+ cacheMisses: result.cacheMisses.length,
864
+ fetchedDefinitions: result.fetchedDefinitions.length,
865
+ errors: result.errors.length
866
+ }, "Context resolved for agent");
867
+ return contextWithBuiltins;
868
+ } catch (error) {
869
+ logger.error({
870
+ conversationId,
871
+ error: error instanceof Error ? error.message : "Unknown error"
872
+ }, "Failed to get resolved context");
873
+ return null;
874
+ }
875
+ }
876
+ /**
877
+ * Get the agent prompt for this agent's agent
878
+ */
879
+ async getPrompt() {
880
+ try {
881
+ return (await getFullAgentDefinition(dbClient_default)({ scopes: {
882
+ tenantId: this.config.tenantId,
883
+ projectId: this.config.projectId,
884
+ agentId: this.config.agentId
885
+ } }))?.prompt || void 0;
886
+ } catch (error) {
887
+ logger.warn({
888
+ agentId: this.config.agentId,
889
+ error: error instanceof Error ? error.message : "Unknown error"
890
+ }, "Failed to get agent prompt");
891
+ return;
892
+ }
893
+ }
894
+ /**
895
+ * Check if any agent in the agent has artifact components configured
896
+ */
897
+ async hasAgentArtifactComponents() {
898
+ try {
899
+ const agentDefinition = await getFullAgentDefinition(dbClient_default)({ scopes: {
900
+ tenantId: this.config.tenantId,
901
+ projectId: this.config.projectId,
902
+ agentId: this.config.agentId
903
+ } });
904
+ if (!agentDefinition) return false;
905
+ return Object.values(agentDefinition.subAgents).some((subAgent) => "artifactComponents" in subAgent && subAgent.artifactComponents && subAgent.artifactComponents.length > 0);
906
+ } catch (error) {
907
+ logger.warn({
908
+ agentId: this.config.agentId,
909
+ tenantId: this.config.tenantId,
910
+ projectId: this.config.projectId,
911
+ error: error instanceof Error ? error.message : "Unknown error"
912
+ }, "Failed to check agent artifact components, assuming none exist");
913
+ return this.artifactComponents.length > 0;
914
+ }
915
+ }
916
+ /**
917
+ * Build adaptive system prompt for Phase 2 structured output generation
918
+ * based on configured data components and artifact components across the agent
919
+ */
920
+ async buildPhase2SystemPrompt(runtimeContext) {
921
+ const phase2Config = new Phase2Config();
922
+ const compressionConfig = getModelAwareCompressionConfig();
923
+ const hasAgentArtifactComponents = await this.hasAgentArtifactComponents() || compressionConfig.enabled;
924
+ const conversationId = runtimeContext?.metadata?.conversationId || runtimeContext?.contextId;
925
+ const resolvedContext = conversationId ? await this.getResolvedContext(conversationId) : null;
926
+ let processedPrompt = this.config.prompt || "";
927
+ if (resolvedContext && this.config.prompt) try {
928
+ processedPrompt = TemplateEngine.render(this.config.prompt, resolvedContext, {
929
+ strict: false,
930
+ preserveUnresolved: false
931
+ });
932
+ } catch (error) {
933
+ logger.error({
934
+ conversationId,
935
+ error: error instanceof Error ? error.message : "Unknown error"
936
+ }, "Failed to process agent prompt with context for Phase 2, using original");
937
+ processedPrompt = this.config.prompt;
938
+ }
939
+ const referenceTaskIds = await listTaskIdsByContextId(dbClient_default)({ contextId: this.conversationId || "" });
940
+ const referenceArtifacts = [];
941
+ for (const taskId of referenceTaskIds) {
942
+ const artifacts = await getLedgerArtifacts(dbClient_default)({
943
+ scopes: {
944
+ tenantId: this.config.tenantId,
945
+ projectId: this.config.projectId
946
+ },
947
+ taskId
948
+ });
949
+ referenceArtifacts.push(...artifacts);
950
+ }
951
+ return phase2Config.assemblePhase2Prompt({
952
+ corePrompt: processedPrompt,
953
+ dataComponents: this.config.dataComponents || [],
954
+ artifactComponents: this.artifactComponents,
955
+ hasArtifactComponents: this.artifactComponents && this.artifactComponents.length > 0,
956
+ hasAgentArtifactComponents,
957
+ artifacts: referenceArtifacts
958
+ });
959
+ }
960
+ async buildSystemPrompt(runtimeContext, excludeDataComponents = false) {
961
+ const conversationId = runtimeContext?.metadata?.conversationId || runtimeContext?.contextId;
962
+ if (conversationId) this.setConversationId(conversationId);
963
+ const resolvedContext = conversationId ? await this.getResolvedContext(conversationId) : null;
964
+ let processedPrompt = this.config.prompt || "";
965
+ if (resolvedContext && this.config.prompt) try {
966
+ processedPrompt = TemplateEngine.render(this.config.prompt, resolvedContext, {
967
+ strict: false,
968
+ preserveUnresolved: false
969
+ });
970
+ } catch (error) {
971
+ logger.error({
972
+ conversationId,
973
+ error: error instanceof Error ? error.message : "Unknown error"
974
+ }, "Failed to process agent prompt with context, using original");
975
+ processedPrompt = this.config.prompt;
976
+ }
977
+ const streamRequestId = runtimeContext?.metadata?.streamRequestId;
978
+ const mcpTools = await this.getMcpTools(void 0, streamRequestId);
979
+ const functionTools = await this.getFunctionTools(streamRequestId || "");
980
+ const relationTools = this.getRelationTools(runtimeContext);
981
+ const allTools = {
982
+ ...mcpTools,
983
+ ...functionTools,
984
+ ...relationTools
985
+ };
986
+ logger.info({
987
+ mcpTools: Object.keys(mcpTools),
988
+ functionTools: Object.keys(functionTools),
989
+ relationTools: Object.keys(relationTools),
990
+ allTools: Object.keys(allTools),
991
+ functionToolsDetails: Object.entries(functionTools).map(([name, tool$1]) => ({
992
+ name,
993
+ hasExecute: typeof tool$1.execute === "function",
994
+ hasDescription: !!tool$1.description,
995
+ hasInputSchema: !!tool$1.inputSchema
996
+ }))
997
+ }, "Tools loaded for agent");
998
+ const toolDefinitions = Object.entries(allTools).map(([name, tool$1]) => ({
999
+ name,
1000
+ description: tool$1.description || "",
1001
+ inputSchema: tool$1.inputSchema || tool$1.parameters || {},
1002
+ usageGuidelines: name.startsWith("transfer_to_") || name.startsWith("delegate_to_") ? `Use this tool to ${name.startsWith("transfer_to_") ? "transfer" : "delegate"} to another agent when appropriate.` : "Use this tool when appropriate for the task at hand."
1003
+ }));
1004
+ const { getConversationScopedArtifacts } = await import("../data/conversations.js");
1005
+ const historyConfig = this.config.conversationHistoryConfig ?? createDefaultConversationHistoryConfig();
1006
+ const referenceArtifacts = await getConversationScopedArtifacts({
1007
+ tenantId: this.config.tenantId,
1008
+ projectId: this.config.projectId,
1009
+ conversationId: runtimeContext?.contextId || "",
1010
+ historyConfig
1011
+ });
1012
+ const componentDataComponents = excludeDataComponents ? [] : this.config.dataComponents || [];
1013
+ const isThinkingPreparation = this.config.dataComponents && this.config.dataComponents.length > 0 && excludeDataComponents;
1014
+ let prompt = await this.getPrompt();
1015
+ if (prompt && resolvedContext) try {
1016
+ prompt = TemplateEngine.render(prompt, resolvedContext, {
1017
+ strict: false,
1018
+ preserveUnresolved: false
1019
+ });
1020
+ } catch (error) {
1021
+ logger.error({
1022
+ conversationId,
1023
+ error: error instanceof Error ? error.message : "Unknown error"
1024
+ }, "Failed to process agent prompt with context, using original");
1025
+ }
1026
+ const shouldIncludeArtifactComponents = !excludeDataComponents;
1027
+ const compressionConfig = getModelAwareCompressionConfig();
1028
+ const hasAgentArtifactComponents = await this.hasAgentArtifactComponents() || compressionConfig.enabled;
1029
+ const config = {
1030
+ corePrompt: processedPrompt,
1031
+ prompt,
1032
+ tools: toolDefinitions,
1033
+ dataComponents: componentDataComponents,
1034
+ artifacts: referenceArtifacts,
1035
+ artifactComponents: shouldIncludeArtifactComponents ? this.artifactComponents : [],
1036
+ hasAgentArtifactComponents,
1037
+ isThinkingPreparation,
1038
+ hasTransferRelations: (this.config.transferRelations?.length ?? 0) > 0,
1039
+ hasDelegateRelations: (this.config.delegateRelations?.length ?? 0) > 0
1040
+ };
1041
+ return await this.systemPromptBuilder.buildSystemPrompt(config);
1042
+ }
1043
+ getArtifactTools() {
1044
+ return tool({
1045
+ description: "Call this tool to get the complete artifact data with the given artifactId. This retrieves the full artifact content (not just the summary). Only use this when you need the complete artifact data and the summary shown in your context is insufficient.",
1046
+ inputSchema: z.object({
1047
+ artifactId: z.string().describe("The unique identifier of the artifact to get."),
1048
+ toolCallId: z.string().describe("The tool call ID associated with this artifact.")
1049
+ }),
1050
+ execute: async ({ artifactId, toolCallId }) => {
1051
+ logger.info({
1052
+ artifactId,
1053
+ toolCallId
1054
+ }, "get_artifact_full executed");
1055
+ const streamRequestId = this.getStreamRequestId();
1056
+ const artifactService = agentSessionManager.getArtifactService(streamRequestId);
1057
+ if (!artifactService) throw new Error(`ArtifactService not found for session ${streamRequestId}`);
1058
+ const artifactData = await artifactService.getArtifactFull(artifactId, toolCallId);
1059
+ if (!artifactData) throw new Error(`Artifact ${artifactId} with toolCallId ${toolCallId} not found`);
1060
+ return {
1061
+ artifactId: artifactData.artifactId,
1062
+ name: artifactData.name,
1063
+ description: artifactData.description,
1064
+ type: artifactData.type,
1065
+ data: artifactData.data
1066
+ };
1067
+ }
1068
+ });
1069
+ }
1070
+ createThinkingCompleteTool() {
1071
+ return tool({
1072
+ description: "🚨 CRITICAL: Call this tool IMMEDIATELY when you have gathered enough information to answer the user. This is MANDATORY - you CANNOT provide text responses in thinking mode, only tool calls. Call thinking_complete as soon as you have sufficient data to generate a structured response.",
1073
+ inputSchema: z.object({
1074
+ complete: z.boolean().describe("ALWAYS set to true - marks end of research phase"),
1075
+ summary: z.string().describe("Brief summary of what information was gathered and why it is sufficient to answer the user")
1076
+ }),
1077
+ execute: async (params) => params
1078
+ });
1079
+ }
1080
+ async getDefaultTools(streamRequestId) {
1081
+ const defaultTools = {};
1082
+ const compressionConfig = getModelAwareCompressionConfig();
1083
+ if (await this.agentHasArtifactComponents() || compressionConfig.enabled) defaultTools.get_reference_artifact = this.getArtifactTools();
1084
+ if (this.config.dataComponents && this.config.dataComponents.length > 0) {
1085
+ const thinkingCompleteTool = this.createThinkingCompleteTool();
1086
+ if (thinkingCompleteTool) defaultTools.thinking_complete = this.wrapToolWithStreaming("thinking_complete", thinkingCompleteTool, streamRequestId, "tool");
1087
+ }
1088
+ logger.info({
1089
+ agentId: this.config.id,
1090
+ streamRequestId
1091
+ }, "Adding compress_context tool to defaultTools");
1092
+ defaultTools.compress_context = tool({
1093
+ description: "Manually compress the current conversation context to save space. Use when shifting topics, completing major tasks, or when context feels cluttered.",
1094
+ inputSchema: z.object({ reason: z.string().describe("Why you are requesting compression (e.g., \"shifting from research to coding\", \"completed analysis phase\")") }),
1095
+ execute: async ({ reason }) => {
1096
+ logger.info({
1097
+ agentId: this.config.id,
1098
+ streamRequestId,
1099
+ reason
1100
+ }, "Manual compression requested by LLM");
1101
+ if (this.currentCompressor) this.currentCompressor.requestManualCompression(reason);
1102
+ return {
1103
+ status: "compression_requested",
1104
+ reason,
1105
+ message: "Context compression will be applied on the next generation step. Previous work has been summarized and saved as artifacts."
1106
+ };
1107
+ }
1108
+ });
1109
+ logger.info("getDefaultTools returning tools:", Object.keys(defaultTools).join(", "));
1110
+ return defaultTools;
1111
+ }
1112
+ getStreamRequestId() {
1113
+ return this.streamRequestId || "";
1114
+ }
1115
+ /**
1116
+ * Format tool result for storage in conversation history
1117
+ */
1118
+ formatToolResult(toolName, args, result, toolCallId) {
1119
+ const input = args ? JSON.stringify(args, null, 2) : "No input";
1120
+ let parsedResult = result;
1121
+ if (typeof result === "string") try {
1122
+ parsedResult = JSON.parse(result);
1123
+ } catch (e) {}
1124
+ const cleanResult = parsedResult && typeof parsedResult === "object" && !Array.isArray(parsedResult) ? {
1125
+ ...parsedResult,
1126
+ result: parsedResult.result && typeof parsedResult.result === "object" && !Array.isArray(parsedResult.result) ? Object.fromEntries(Object.entries(parsedResult.result).filter(([key]) => key !== "_structureHints")) : parsedResult.result
1127
+ } : parsedResult;
1128
+ return `## Tool: ${toolName}
1129
+
1130
+ ### 🔧 TOOL_CALL_ID: ${toolCallId}
1131
+
1132
+ ### Input
1133
+ ${input}
1134
+
1135
+ ### Output
1136
+ ${typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, null, 2)}`;
1137
+ }
1138
+ /**
1139
+ * Get the conversation ID for storing tool results
1140
+ * Always uses the real conversation ID - delegation filtering happens at query time
1141
+ */
1142
+ getToolResultConversationId() {
1143
+ return this.conversationId;
1144
+ }
1145
+ /**
1146
+ * Analyze tool result structure and add helpful path hints for artifact creation
1147
+ * Only adds hints when artifact components are available
1148
+ */
1149
+ enhanceToolResultWithStructureHints(result) {
1150
+ if (!result) return result;
1151
+ if (!this.artifactComponents || this.artifactComponents.length === 0) return result;
1152
+ let parsedForAnalysis = result;
1153
+ if (typeof result === "string") try {
1154
+ parsedForAnalysis = parseEmbeddedJson(result);
1155
+ } catch (_error) {
1156
+ parsedForAnalysis = result;
1157
+ }
1158
+ if (!parsedForAnalysis || typeof parsedForAnalysis !== "object") return result;
1159
+ const findAllPaths = (obj, prefix = "result", depth = 0) => {
1160
+ if (depth > 8) return [];
1161
+ const paths = [];
1162
+ if (Array.isArray(obj)) {
1163
+ if (obj.length > 0) {
1164
+ paths.push(`${prefix}[array-${obj.length}-items]`);
1165
+ if (obj[0] && typeof obj[0] === "object") {
1166
+ const sampleItem = obj[0];
1167
+ Object.keys(sampleItem).forEach((key) => {
1168
+ const value = sampleItem[key];
1169
+ if (typeof value === "string" && value.length < 50) paths.push(`${prefix}[?${key}=='${value}']`);
1170
+ else if (typeof value === "boolean") paths.push(`${prefix}[?${key}==${value}]`);
1171
+ else if (key === "id" || key === "name" || key === "type") paths.push(`${prefix}[?${key}=='value']`);
1172
+ });
1173
+ }
1174
+ paths.push(...findAllPaths(obj[0], `${prefix}[?field=='value']`, depth + 1));
1175
+ }
1176
+ } else if (obj && typeof obj === "object") Object.entries(obj).forEach(([key, value]) => {
1177
+ const currentPath = `${prefix}.${key}`;
1178
+ if (value && typeof value === "object") {
1179
+ if (Array.isArray(value)) paths.push(`${currentPath}[array]`);
1180
+ else paths.push(`${currentPath}[object]`);
1181
+ paths.push(...findAllPaths(value, currentPath, depth + 1));
1182
+ } else paths.push(`${currentPath}[${typeof value}]`);
1183
+ });
1184
+ return paths;
1185
+ };
1186
+ const findCommonFields = (obj, depth = 0) => {
1187
+ if (depth > 5) return /* @__PURE__ */ new Set();
1188
+ const fields = /* @__PURE__ */ new Set();
1189
+ if (Array.isArray(obj)) obj.slice(0, 3).forEach((item) => {
1190
+ if (item && typeof item === "object") Object.keys(item).forEach((key) => {
1191
+ fields.add(key);
1192
+ });
1193
+ });
1194
+ else if (obj && typeof obj === "object") {
1195
+ Object.keys(obj).forEach((key) => {
1196
+ fields.add(key);
1197
+ });
1198
+ Object.values(obj).forEach((value) => {
1199
+ findCommonFields(value, depth + 1).forEach((field) => {
1200
+ fields.add(field);
1201
+ });
1202
+ });
1203
+ }
1204
+ return fields;
1205
+ };
1206
+ const findUsefulSelectors = (obj, prefix = "result", depth = 0) => {
1207
+ if (depth > 5) return [];
1208
+ const selectors = [];
1209
+ if (Array.isArray(obj) && obj.length > 0) {
1210
+ const firstItem = obj[0];
1211
+ if (firstItem && typeof firstItem === "object") {
1212
+ if (firstItem.title) selectors.push(`${prefix}[?title=='${String(firstItem.title).replace(/'/g, "\\'")}'] | [0]`);
1213
+ if (firstItem.type) selectors.push(`${prefix}[?type=='${firstItem.type}'] | [0]`);
1214
+ if (firstItem.record_type) selectors.push(`${prefix}[?record_type=='${firstItem.record_type}'] | [0]`);
1215
+ if (firstItem.url) selectors.push(`${prefix}[?url!=null] | [0]`);
1216
+ if (firstItem.type && firstItem.title) selectors.push(`${prefix}[?type=='${firstItem.type}' && title=='${String(firstItem.title).replace(/'/g, "\\'")}'] | [0]`);
1217
+ selectors.push(`${prefix}[0]`);
1218
+ }
1219
+ } else if (obj && typeof obj === "object") Object.entries(obj).forEach(([key, value]) => {
1220
+ if (typeof value === "object" && value !== null) selectors.push(...findUsefulSelectors(value, `${prefix}.${key}`, depth + 1));
1221
+ });
1222
+ return selectors;
1223
+ };
1224
+ const findNestedContentPaths = (obj, prefix = "result", depth = 0) => {
1225
+ if (depth > 6) return [];
1226
+ const paths = [];
1227
+ if (obj && typeof obj === "object") Object.entries(obj).forEach(([key, value]) => {
1228
+ const currentPath = `${prefix}.${key}`;
1229
+ if (Array.isArray(value) && value.length > 0) {
1230
+ const firstItem = value[0];
1231
+ if (firstItem && typeof firstItem === "object") {
1232
+ if (firstItem.type === "document" || firstItem.type === "text") {
1233
+ paths.push(`${currentPath}[?type=='document'] | [0]`);
1234
+ paths.push(`${currentPath}[?type=='text'] | [0]`);
1235
+ if (firstItem.title) {
1236
+ const titleSample = String(firstItem.title).slice(0, 20);
1237
+ paths.push(`${currentPath}[?title && contains(title, '${titleSample.split(" ")[0]}')] | [0]`);
1238
+ }
1239
+ if (firstItem.record_type) paths.push(`${currentPath}[?record_type=='${firstItem.record_type}'] | [0]`);
1240
+ }
1241
+ }
1242
+ paths.push(...findNestedContentPaths(value, currentPath, depth + 1));
1243
+ } else if (value && typeof value === "object") paths.push(...findNestedContentPaths(value, currentPath, depth + 1));
1244
+ });
1245
+ return paths;
1246
+ };
1247
+ try {
1248
+ const allPaths = findAllPaths(parsedForAnalysis);
1249
+ const commonFields = Array.from(findCommonFields(parsedForAnalysis)).slice(0, 15);
1250
+ const usefulSelectors = findUsefulSelectors(parsedForAnalysis).slice(0, 10);
1251
+ const nestedContentPaths = findNestedContentPaths(parsedForAnalysis).slice(0, 8);
1252
+ const terminalPaths = allPaths.filter((p) => p.includes("[string]") || p.includes("[number]") || p.includes("[boolean]")).slice(0, 20);
1253
+ const arrayPaths = allPaths.filter((p) => p.includes("[array")).slice(0, 15);
1254
+ const objectPaths = allPaths.filter((p) => p.includes("[object]")).slice(0, 15);
1255
+ const allSelectors = [...usefulSelectors, ...nestedContentPaths];
1256
+ const uniqueSelectors = [...new Set(allSelectors)].slice(0, 15);
1257
+ return {
1258
+ ...result,
1259
+ _structureHints: {
1260
+ terminalPaths,
1261
+ arrayPaths,
1262
+ objectPaths,
1263
+ commonFields,
1264
+ exampleSelectors: uniqueSelectors,
1265
+ deepStructureExamples: nestedContentPaths,
1266
+ maxDepthFound: Math.max(...allPaths.map((p) => (p.match(/\./g) || []).length)),
1267
+ totalPathsFound: allPaths.length,
1268
+ artifactGuidance: {
1269
+ creationFirst: "🚨 CRITICAL: Artifacts must be CREATED before they can be referenced. Use ArtifactCreate_[Type] components FIRST, then reference with Artifact components only if citing the SAME artifact again.",
1270
+ baseSelector: "🎯 CRITICAL: Use base_selector to navigate to ONE specific item. For deeply nested structures with repeated keys, use full paths with specific filtering (e.g., \"result.data.content.items[?type=='guide' && status=='active']\")",
1271
+ detailsSelector: "📝 Use relative selectors for specific fields (e.g., \"title\", \"metadata.category\", \"properties.status\", \"content.details\")",
1272
+ avoidLiterals: "❌ NEVER use literal values - always use field selectors to extract from data",
1273
+ avoidArrays: "✨ ALWAYS filter arrays to single items using [?condition] - NEVER use [*] notation which returns arrays",
1274
+ nestedKeys: "🔑 For structures with repeated keys (like result.content.data.content.items.content), use full paths with filtering at each level",
1275
+ filterTips: "💡 Use compound filters for precision: [?type=='document' && category=='api']",
1276
+ forbiddenSyntax: "🚫 FORBIDDEN JMESPATH PATTERNS:\n❌ NEVER: [?title~'.*text.*'] (regex patterns with ~ operator)\n❌ NEVER: [?field~'pattern.*'] (any ~ operator usage)\n❌ NEVER: [?title~'Slack.*Discord.*'] (regex wildcards)\n❌ NEVER: [?name~'https://.*'] (regex in URL matching)\n❌ NEVER: [?text ~ contains(@, 'word')] (~ with @ operator)\n❌ NEVER: contains(@, 'text') (@ operator usage)\n❌ NEVER: [?field==\"value\"] (double quotes in filters)\n❌ NEVER: result.items[?type=='doc'][?status=='active'] (chained filters)\n✅ USE INSTEAD:\n✅ [?contains(title, 'text')] (contains function)\n✅ [?title=='exact match'] (exact string matching)\n✅ [?contains(title, 'Slack') && contains(title, 'Discord')] (compound conditions)\n✅ [?starts_with(url, 'https://')] (starts_with function)\n✅ [?type=='doc' && status=='active'] (single filter with &&)",
1277
+ pathDepth: `📏 This structure goes ${Math.max(...allPaths.map((p) => (p.match(/\./g) || []).length))} levels deep - use full paths to avoid ambiguity`
1278
+ },
1279
+ note: `Comprehensive structure analysis: ${allPaths.length} paths found, ${Math.max(...allPaths.map((p) => (p.match(/\./g) || []).length))} levels deep. Use specific filtering for precise selection.`
1280
+ }
1281
+ };
1282
+ } catch (error) {
1283
+ logger.warn({ error }, "Failed to enhance tool result with structure hints");
1284
+ return result;
1285
+ }
1286
+ }
1287
+ async agentHasArtifactComponents() {
1288
+ try {
1289
+ return await agentHasArtifactComponents(dbClient_default)({ scopes: {
1290
+ tenantId: this.config.tenantId,
1291
+ projectId: this.config.projectId,
1292
+ agentId: this.config.agentId
1293
+ } });
1294
+ } catch (error) {
1295
+ logger.error({
1296
+ error,
1297
+ agentId: this.config.agentId
1298
+ }, "Failed to check agent artifact components");
1299
+ return false;
1300
+ }
1301
+ }
1302
+ async generate(userMessage, runtimeContext) {
1303
+ return tracer.startActiveSpan("agent.generate", { attributes: {
1304
+ "subAgent.id": this.config.id,
1305
+ "subAgent.name": this.config.name
1306
+ } }, async (span) => {
1307
+ const { contextId, taskId, streamRequestId, sessionId } = this.setupGenerationContext(runtimeContext);
1308
+ try {
1309
+ const { systemPrompt, thinkingSystemPrompt, sanitizedTools, contextBreakdown: initialContextBreakdown } = await this.loadToolsAndPrompts(sessionId, streamRequestId, runtimeContext);
1310
+ if (streamRequestId && this.artifactComponents.length > 0) agentSessionManager.updateArtifactComponents(streamRequestId, this.artifactComponents);
1311
+ const conversationId = runtimeContext?.metadata?.conversationId;
1312
+ if (conversationId) this.setConversationId(conversationId);
1313
+ const { conversationHistory, contextBreakdown } = await this.buildConversationHistory(contextId, taskId, userMessage, streamRequestId, initialContextBreakdown);
1314
+ span.setAttributes({
1315
+ "context.breakdown.system_template_tokens": contextBreakdown.systemPromptTemplate,
1316
+ "context.breakdown.core_instructions_tokens": contextBreakdown.coreInstructions,
1317
+ "context.breakdown.agent_prompt_tokens": contextBreakdown.agentPrompt,
1318
+ "context.breakdown.tools_tokens": contextBreakdown.toolsSection,
1319
+ "context.breakdown.artifacts_tokens": contextBreakdown.artifactsSection,
1320
+ "context.breakdown.data_components_tokens": contextBreakdown.dataComponents,
1321
+ "context.breakdown.artifact_components_tokens": contextBreakdown.artifactComponents,
1322
+ "context.breakdown.transfer_instructions_tokens": contextBreakdown.transferInstructions,
1323
+ "context.breakdown.delegation_instructions_tokens": contextBreakdown.delegationInstructions,
1324
+ "context.breakdown.thinking_preparation_tokens": contextBreakdown.thinkingPreparation,
1325
+ "context.breakdown.conversation_history_tokens": contextBreakdown.conversationHistory,
1326
+ "context.breakdown.total_tokens": contextBreakdown.total
1327
+ });
1328
+ const { primaryModelSettings, modelSettings, hasStructuredOutput, shouldStreamPhase1, timeoutMs } = this.configureModelSettings();
1329
+ let response;
1330
+ let textResponse;
1331
+ const messages = this.buildInitialMessages(systemPrompt, thinkingSystemPrompt, hasStructuredOutput, conversationHistory, userMessage);
1332
+ const { originalMessageCount, compressor } = this.setupCompression(messages, sessionId, contextId, primaryModelSettings);
1333
+ if (shouldStreamPhase1) {
1334
+ const streamConfig = {
1335
+ ...modelSettings,
1336
+ toolChoice: "auto"
1337
+ };
1338
+ const streamResult = streamText(this.buildBaseGenerationConfig(streamConfig, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, "auto", void 0, false, contextBreakdown.total));
1339
+ const parser = this.setupStreamParser(sessionId, contextId);
1340
+ await this.processStreamEvents(streamResult, parser);
1341
+ response = await streamResult;
1342
+ response = this.formatStreamingResponse(response, parser);
1343
+ } else {
1344
+ const toolChoice = hasStructuredOutput ? "required" : "auto";
1345
+ response = await generateText(this.buildBaseGenerationConfig(modelSettings, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, toolChoice, "planning", true, contextBreakdown.total));
1346
+ }
1347
+ if (response.steps) {
1348
+ const resolvedSteps = await response.steps;
1349
+ response = {
1350
+ ...response,
1351
+ steps: resolvedSteps
1352
+ };
1353
+ }
1354
+ if (hasStructuredOutput && !hasToolCallWithPrefix("transfer_to_")(response)) if (response.steps?.flatMap((s) => s.toolCalls || [])?.find((tc) => tc.toolName === "thinking_complete")) {
1355
+ const reasoningFlow = this.buildReasoningFlow(response, sessionId);
1356
+ const dataComponentsSchema = this.buildDataComponentsSchema();
1357
+ const structuredModelSettings = ModelFactory.prepareGenerationConfig(this.getStructuredOutputModel());
1358
+ const phase2TimeoutMs = this.calculatePhase2Timeout(structuredModelSettings);
1359
+ if (this.getStreamingHelper()) {
1360
+ const phase2Messages = await this.buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow);
1361
+ const result = await this.executeStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, sessionId, contextId, response);
1362
+ response = result;
1363
+ textResponse = result.textResponse;
1364
+ } else {
1365
+ const phase2Messages = await this.buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow);
1366
+ const result = await this.executeNonStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, response);
1367
+ response = result;
1368
+ textResponse = result.textResponse;
1369
+ }
1370
+ } else textResponse = response.text || "";
1371
+ else textResponse = response.steps[response.steps.length - 1].text || "";
1372
+ span.setStatus({ code: SpanStatusCode.OK });
1373
+ span.end();
1374
+ const formattedResponse = await this.formatFinalResponse(response, textResponse, sessionId, contextId);
1375
+ if (streamRequestId) {
1376
+ const generationType = response.object ? "object_generation" : "text_generation";
1377
+ agentSessionManager.recordEvent(streamRequestId, "agent_generate", this.config.id, {
1378
+ parts: (formattedResponse.formattedContent?.parts || []).map((part) => ({
1379
+ type: part.kind === "text" ? "text" : part.kind === "data" ? "tool_result" : "text",
1380
+ content: part.text || JSON.stringify(part.data)
1381
+ })),
1382
+ generationType
1383
+ });
1384
+ }
1385
+ if (compressor) compressor.fullCleanup();
1386
+ this.currentCompressor = null;
1387
+ return formattedResponse;
1388
+ } catch (error) {
1389
+ this.handleGenerationError(error, span);
1390
+ }
1391
+ });
1392
+ }
1393
+ /**
1394
+ * Setup generation context and initialize streaming helper
1395
+ */
1396
+ setupGenerationContext(runtimeContext) {
1397
+ const contextId = runtimeContext?.contextId || "default";
1398
+ const taskId = runtimeContext?.metadata?.taskId || "unknown";
1399
+ const streamRequestId = runtimeContext?.metadata?.streamRequestId;
1400
+ const sessionId = streamRequestId || "fallback-session";
1401
+ this.streamRequestId = streamRequestId;
1402
+ this.streamHelper = streamRequestId ? getStreamHelper(streamRequestId) : void 0;
1403
+ if (streamRequestId && this.artifactComponents.length > 0) agentSessionManager.updateArtifactComponents(streamRequestId, this.artifactComponents);
1404
+ const conversationId = runtimeContext?.metadata?.conversationId;
1405
+ if (conversationId) this.setConversationId(conversationId);
1406
+ return {
1407
+ contextId,
1408
+ taskId,
1409
+ streamRequestId,
1410
+ sessionId
1411
+ };
1412
+ }
1413
+ /**
1414
+ * Load all tools and system prompts in parallel, then combine and sanitize them
1415
+ */
1416
+ async loadToolsAndPrompts(sessionId, streamRequestId, runtimeContext) {
1417
+ const [mcpTools, systemPromptResult, thinkingSystemPromptResult, functionTools, relationTools, defaultTools] = await tracer.startActiveSpan("agent.load_tools", { attributes: {
1418
+ "subAgent.name": this.config.name,
1419
+ "session.id": sessionId || "none"
1420
+ } }, async (childSpan) => {
1421
+ try {
1422
+ const result = await Promise.all([
1423
+ this.getMcpTools(sessionId, streamRequestId),
1424
+ this.buildSystemPrompt(runtimeContext, false),
1425
+ this.buildSystemPrompt(runtimeContext, true),
1426
+ this.getFunctionTools(sessionId, streamRequestId),
1427
+ Promise.resolve(this.getRelationTools(runtimeContext, sessionId)),
1428
+ this.getDefaultTools(streamRequestId)
1429
+ ]);
1430
+ childSpan.setStatus({ code: SpanStatusCode.OK });
1431
+ return result;
1432
+ } catch (err) {
1433
+ setSpanWithError$1(childSpan, err instanceof Error ? err : new Error(String(err)));
1434
+ throw err;
1435
+ } finally {
1436
+ childSpan.end();
1437
+ }
1438
+ });
1439
+ const systemPrompt = systemPromptResult.prompt;
1440
+ const thinkingSystemPrompt = thinkingSystemPromptResult.prompt;
1441
+ const contextBreakdown = systemPromptResult.breakdown;
1442
+ const allTools = {
1443
+ ...mcpTools,
1444
+ ...functionTools,
1445
+ ...relationTools,
1446
+ ...defaultTools
1447
+ };
1448
+ return {
1449
+ systemPrompt,
1450
+ thinkingSystemPrompt,
1451
+ sanitizedTools: this.sanitizeToolsForAISDK(allTools),
1452
+ contextBreakdown
1453
+ };
1454
+ }
1455
+ /**
1456
+ * Build conversation history based on configuration mode and filters
1457
+ */
1458
+ async buildConversationHistory(contextId, taskId, userMessage, streamRequestId, initialContextBreakdown) {
1459
+ let conversationHistory = "";
1460
+ const historyConfig = this.config.conversationHistoryConfig ?? createDefaultConversationHistoryConfig();
1461
+ if (historyConfig && historyConfig.mode !== "none") {
1462
+ if (historyConfig.mode === "full") {
1463
+ const filters = {
1464
+ delegationId: this.delegationId,
1465
+ isDelegated: this.isDelegatedAgent
1466
+ };
1467
+ conversationHistory = await getConversationHistoryWithCompression({
1468
+ tenantId: this.config.tenantId,
1469
+ projectId: this.config.projectId,
1470
+ conversationId: contextId,
1471
+ currentMessage: userMessage,
1472
+ options: historyConfig,
1473
+ filters,
1474
+ summarizerModel: this.getSummarizerModel(),
1475
+ streamRequestId,
1476
+ fullContextSize: initialContextBreakdown.total
1477
+ });
1478
+ } else if (historyConfig.mode === "scoped") conversationHistory = await getConversationHistoryWithCompression({
1479
+ tenantId: this.config.tenantId,
1480
+ projectId: this.config.projectId,
1481
+ conversationId: contextId,
1482
+ currentMessage: userMessage,
1483
+ options: historyConfig,
1484
+ filters: {
1485
+ subAgentId: this.config.id,
1486
+ taskId,
1487
+ delegationId: this.delegationId,
1488
+ isDelegated: this.isDelegatedAgent
1489
+ },
1490
+ summarizerModel: this.getSummarizerModel(),
1491
+ streamRequestId,
1492
+ fullContextSize: initialContextBreakdown.total
1493
+ });
1494
+ }
1495
+ const conversationHistoryTokens = estimateTokens(conversationHistory);
1496
+ const updatedContextBreakdown = {
1497
+ ...initialContextBreakdown,
1498
+ conversationHistory: conversationHistoryTokens
1499
+ };
1500
+ calculateBreakdownTotal(updatedContextBreakdown);
1501
+ return {
1502
+ conversationHistory,
1503
+ contextBreakdown: updatedContextBreakdown
1504
+ };
1505
+ }
1506
+ /**
1507
+ * Configure model settings, timeouts, and streaming behavior
1508
+ */
1509
+ configureModelSettings() {
1510
+ const primaryModelSettings = this.getPrimaryModel();
1511
+ const modelSettings = ModelFactory.prepareGenerationConfig(primaryModelSettings);
1512
+ const hasStructuredOutput = Boolean(this.config.dataComponents && this.config.dataComponents.length > 0);
1513
+ const shouldStreamPhase1 = this.getStreamingHelper() && !hasStructuredOutput;
1514
+ const configuredTimeout = modelSettings.maxDuration ? Math.min(modelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : shouldStreamPhase1 ? LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING : LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING;
1515
+ const timeoutMs = Math.min(configuredTimeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
1516
+ if (modelSettings.maxDuration && modelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
1517
+ requestedTimeout: modelSettings.maxDuration * 1e3,
1518
+ appliedTimeout: timeoutMs,
1519
+ maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS
1520
+ }, "Requested timeout exceeded maximum allowed, capping to 10 minutes");
1521
+ return {
1522
+ primaryModelSettings,
1523
+ modelSettings: {
1524
+ ...modelSettings,
1525
+ maxDuration: timeoutMs / 1e3
1526
+ },
1527
+ hasStructuredOutput,
1528
+ shouldStreamPhase1,
1529
+ timeoutMs
1530
+ };
1531
+ }
1532
+ /**
1533
+ * Build initial messages array with system prompt and user content
1534
+ */
1535
+ buildInitialMessages(systemPrompt, thinkingSystemPrompt, hasStructuredOutput, conversationHistory, userMessage) {
1536
+ const phase1SystemPrompt = hasStructuredOutput ? thinkingSystemPrompt : systemPrompt;
1537
+ const messages = [];
1538
+ messages.push({
1539
+ role: "system",
1540
+ content: phase1SystemPrompt
1541
+ });
1542
+ if (conversationHistory.trim() !== "") messages.push({
1543
+ role: "user",
1544
+ content: conversationHistory
1545
+ });
1546
+ messages.push({
1547
+ role: "user",
1548
+ content: userMessage
1549
+ });
1550
+ return messages;
1551
+ }
1552
+ /**
1553
+ * Setup compression for the current generation
1554
+ */
1555
+ setupCompression(messages, sessionId, contextId, primaryModelSettings) {
1556
+ const originalMessageCount = messages.length;
1557
+ const compressionConfigResult = getCompressionConfigForModel(primaryModelSettings);
1558
+ const compressionConfig = {
1559
+ hardLimit: compressionConfigResult.hardLimit,
1560
+ safetyBuffer: compressionConfigResult.safetyBuffer,
1561
+ enabled: compressionConfigResult.enabled
1562
+ };
1563
+ const compressor = compressionConfig.enabled ? new MidGenerationCompressor(sessionId, contextId, this.config.tenantId, this.config.projectId, compressionConfig, this.getSummarizerModel(), primaryModelSettings) : null;
1564
+ this.currentCompressor = compressor;
1565
+ return {
1566
+ originalMessageCount,
1567
+ compressor
1568
+ };
1569
+ }
1570
+ /**
1571
+ * Prepare step function for streaming with compression logic
1572
+ */
1573
+ async handlePrepareStepCompression(stepMessages, compressor, originalMessageCount, fullContextSize) {
1574
+ if (!compressor) return {};
1575
+ if (compressor.isCompressionNeeded(stepMessages)) {
1576
+ logger.info({ compressorState: compressor.getState() }, "Triggering layered mid-generation compression");
1577
+ const originalMessages = stepMessages.slice(0, originalMessageCount);
1578
+ const generatedMessages = stepMessages.slice(originalMessageCount);
1579
+ if (generatedMessages.length > 0) {
1580
+ const compressionResult = await compressor.safeCompress(generatedMessages, fullContextSize);
1581
+ if (Array.isArray(compressionResult.summary)) {
1582
+ const compressedMessages = compressionResult.summary;
1583
+ logger.info({
1584
+ originalTotal: stepMessages.length,
1585
+ compressed: originalMessages.length + compressedMessages.length,
1586
+ originalKept: originalMessages.length,
1587
+ generatedCompressed: compressedMessages.length
1588
+ }, "Simple compression fallback applied");
1589
+ return { messages: [...originalMessages, ...compressedMessages] };
1590
+ }
1591
+ const finalMessages = [...originalMessages];
1592
+ if (compressionResult.summary.text_messages && compressionResult.summary.text_messages.length > 0) finalMessages.push(...compressionResult.summary.text_messages);
1593
+ const summaryData = {
1594
+ high_level: compressionResult.summary?.high_level,
1595
+ user_intent: compressionResult.summary?.user_intent,
1596
+ decisions: compressionResult.summary?.decisions,
1597
+ open_questions: compressionResult.summary?.open_questions,
1598
+ next_steps: compressionResult.summary?.next_steps,
1599
+ related_artifacts: compressionResult.summary?.related_artifacts
1600
+ };
1601
+ if (summaryData.related_artifacts && summaryData.related_artifacts.length > 0) summaryData.related_artifacts = summaryData.related_artifacts.map((artifact) => ({
1602
+ ...artifact,
1603
+ artifact_reference: `<artifact:ref id="${artifact.id}" tool="${artifact.tool_call_id}" />`
1604
+ }));
1605
+ const summaryMessage = JSON.stringify(summaryData);
1606
+ finalMessages.push({
1607
+ role: "user",
1608
+ content: `Based on your research, here's what you've discovered: ${summaryMessage}
1609
+
1610
+ **IMPORTANT**: If you have enough information from this compressed research to answer my original question, please provide your answer now. Only continue with additional tool calls if you need critical missing information that wasn't captured in the research above. When referencing any artifacts from the compressed research, you MUST use <artifact:ref id="artifact_id" tool="tool_call_id" /> tags with the exact IDs from the related_artifacts above.`
1611
+ });
1612
+ logger.info({
1613
+ originalTotal: stepMessages.length,
1614
+ compressed: finalMessages.length,
1615
+ originalKept: originalMessages.length,
1616
+ generatedCompressed: generatedMessages.length
1617
+ }, "AI compression completed successfully");
1618
+ return { messages: finalMessages };
1619
+ }
1620
+ return {};
1621
+ }
1622
+ return {};
1623
+ }
1624
+ async handleStopWhenConditions(steps, includeThinkingComplete = false) {
1625
+ const last = steps.at(-1);
1626
+ if (last && "text" in last && last.text) try {
1627
+ await agentSessionManager.recordEvent(this.getStreamRequestId(), "agent_reasoning", this.config.id, { parts: [{
1628
+ type: "text",
1629
+ content: last.text
1630
+ }] });
1631
+ } catch (error) {
1632
+ logger.debug({ error }, "Failed to track agent reasoning");
1633
+ }
1634
+ if (!includeThinkingComplete && last && last["content"] && last["content"].length > 0) {
1635
+ const lastContent = last["content"][last["content"].length - 1];
1636
+ if (lastContent["type"] === "tool-error") {
1637
+ const error = lastContent["error"];
1638
+ if (error && typeof error === "object" && "name" in error && error.name === "connection_refused") return true;
1639
+ }
1640
+ }
1641
+ if (steps.length >= 2) {
1642
+ const previousStep = steps[steps.length - 2];
1643
+ if (previousStep && "toolCalls" in previousStep && previousStep.toolCalls) {
1644
+ const stopToolNames = includeThinkingComplete ? ["transfer_to_", "thinking_complete"] : ["transfer_to_"];
1645
+ if (previousStep.toolCalls.some((tc) => stopToolNames.some((toolName) => toolName.endsWith("_") ? tc.toolName.startsWith(toolName) : tc.toolName === toolName)) && "toolResults" in previousStep && previousStep.toolResults) return true;
1646
+ }
1647
+ }
1648
+ return steps.length >= this.getMaxGenerationSteps();
1649
+ }
1650
+ setupStreamParser(sessionId, contextId) {
1651
+ const streamHelper = this.getStreamingHelper();
1652
+ if (!streamHelper) throw new Error("Stream helper is unexpectedly undefined in streaming context");
1653
+ const session = toolSessionManager.getSession(sessionId);
1654
+ const artifactParserOptions = {
1655
+ sessionId,
1656
+ taskId: session?.taskId,
1657
+ projectId: session?.projectId,
1658
+ artifactComponents: this.artifactComponents,
1659
+ streamRequestId: this.getStreamRequestId(),
1660
+ subAgentId: this.config.id
1661
+ };
1662
+ return new IncrementalStreamParser(streamHelper, this.config.tenantId, contextId, artifactParserOptions);
1663
+ }
1664
+ buildTelemetryConfig(phase) {
1665
+ return {
1666
+ isEnabled: true,
1667
+ functionId: this.config.id,
1668
+ recordInputs: true,
1669
+ recordOutputs: true,
1670
+ metadata: {
1671
+ ...phase && { phase },
1672
+ subAgentId: this.config.id,
1673
+ subAgentName: this.config.name
1674
+ }
1675
+ };
1676
+ }
1677
+ buildBaseGenerationConfig(modelSettings, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, toolChoice = "auto", phase, includeThinkingComplete = false, fullContextSize) {
1678
+ return {
1679
+ ...modelSettings,
1680
+ toolChoice,
1681
+ messages,
1682
+ tools: sanitizedTools,
1683
+ prepareStep: async ({ messages: stepMessages }) => {
1684
+ return await this.handlePrepareStepCompression(stepMessages, compressor, originalMessageCount, fullContextSize);
1685
+ },
1686
+ stopWhen: async ({ steps }) => {
1687
+ return await this.handleStopWhenConditions(steps, includeThinkingComplete);
1688
+ },
1689
+ experimental_telemetry: this.buildTelemetryConfig(phase),
1690
+ abortSignal: AbortSignal.timeout(timeoutMs)
1691
+ };
1692
+ }
1693
+ buildReasoningFlow(response, sessionId) {
1694
+ const reasoningFlow = [];
1695
+ const compressionSummary = this.currentCompressor?.getCompressionSummary();
1696
+ if (compressionSummary) {
1697
+ const summaryContent = JSON.stringify(compressionSummary, null, 2);
1698
+ reasoningFlow.push({
1699
+ role: "assistant",
1700
+ content: `## Research Summary (Compressed)\n\nBased on tool executions, here's the comprehensive summary:\n\n\`\`\`json\n${summaryContent}\n\`\`\`\n\nThis summary represents all tool execution results in compressed form. Full details are preserved in artifacts.`
1701
+ });
1702
+ } else if (response.steps) response.steps.forEach((step) => {
1703
+ if (step.toolCalls && step.toolResults) step.toolCalls.forEach((call, index) => {
1704
+ const result = step.toolResults[index];
1705
+ if (result) {
1706
+ const storedResult = toolSessionManager.getToolResult(sessionId, result.toolCallId);
1707
+ if ((storedResult?.toolName || call.toolName) === "thinking_complete") return;
1708
+ const actualResult = storedResult?.result || result.result || result;
1709
+ const actualArgs = storedResult?.args || call.args;
1710
+ const cleanResult = actualResult && typeof actualResult === "object" && !Array.isArray(actualResult) ? Object.fromEntries(Object.entries(actualResult).filter(([key]) => key !== "_structureHints")) : actualResult;
1711
+ const input = actualArgs ? JSON.stringify(actualArgs, null, 2) : "No input";
1712
+ const output = typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, null, 2);
1713
+ let structureHintsFormatted = "";
1714
+ if (actualResult?._structureHints && this.artifactComponents && this.artifactComponents.length > 0) {
1715
+ const hints = actualResult._structureHints;
1716
+ structureHintsFormatted = `
1717
+ ### 📊 Structure Hints for Artifact Creation
1718
+
1719
+ **Terminal Field Paths (${hints.terminalPaths?.length || 0} found):**
1720
+ ${hints.terminalPaths?.map((path) => ` • ${path}`).join("\n") || " None detected"}
1721
+
1722
+ **Array Structures (${hints.arrayPaths?.length || 0} found):**
1723
+ ${hints.arrayPaths?.map((path) => ` • ${path}`).join("\n") || " None detected"}
1724
+
1725
+ **Object Structures (${hints.objectPaths?.length || 0} found):**
1726
+ ${hints.objectPaths?.map((path) => ` • ${path}`).join("\n") || " None detected"}
1727
+
1728
+ **Example Selectors:**
1729
+ ${hints.exampleSelectors?.map((sel) => ` • ${sel}`).join("\n") || " None detected"}
1730
+
1731
+ **Common Fields:**
1732
+ ${hints.commonFields?.map((field) => ` • ${field}`).join("\n") || " None detected"}
1733
+
1734
+ **Structure Stats:** ${hints.totalPathsFound || 0} total paths, ${hints.maxDepthFound || 0} levels deep
1735
+
1736
+ **Note:** ${hints.note || "Use these paths for artifact base selectors."}
1737
+
1738
+ **Forbidden Syntax:** ${hints.forbiddenSyntax || "Use these paths for artifact base selectors."}
1739
+ `;
1740
+ }
1741
+ const formattedResult = `## Tool: ${call.toolName}
1742
+
1743
+ ### 🔧 TOOL_CALL_ID: ${result.toolCallId}
1744
+
1745
+ ### Input
1746
+ ${input}
1747
+
1748
+ ### Output
1749
+ ${output}${structureHintsFormatted}`;
1750
+ reasoningFlow.push({
1751
+ role: "assistant",
1752
+ content: formattedResult
1753
+ });
1754
+ }
1755
+ });
1756
+ });
1757
+ return reasoningFlow;
1758
+ }
1759
+ buildDataComponentsSchema() {
1760
+ const componentSchemas = [];
1761
+ if (this.config.dataComponents && this.config.dataComponents.length > 0) this.config.dataComponents.forEach((dc) => {
1762
+ const propsSchema = jsonSchemaToZod(dc.props);
1763
+ componentSchemas.push(z.object({
1764
+ id: z.string(),
1765
+ name: z.literal(dc.name),
1766
+ props: propsSchema
1767
+ }));
1768
+ });
1769
+ if (this.artifactComponents.length > 0) {
1770
+ const artifactCreateSchemas = ArtifactCreateSchema.getSchemas(this.artifactComponents);
1771
+ componentSchemas.push(...artifactCreateSchemas);
1772
+ componentSchemas.push(ArtifactReferenceSchema.getSchema());
1773
+ }
1774
+ let dataComponentsSchema;
1775
+ if (componentSchemas.length === 1) dataComponentsSchema = componentSchemas[0];
1776
+ else dataComponentsSchema = z.union(componentSchemas);
1777
+ return dataComponentsSchema;
1778
+ }
1779
+ calculatePhase2Timeout(structuredModelSettings) {
1780
+ const configuredPhase2Timeout = structuredModelSettings.maxDuration ? Math.min(structuredModelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS;
1781
+ const phase2TimeoutMs = Math.min(configuredPhase2Timeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
1782
+ if (structuredModelSettings.maxDuration && structuredModelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
1783
+ requestedTimeout: structuredModelSettings.maxDuration * 1e3,
1784
+ appliedTimeout: phase2TimeoutMs,
1785
+ maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS,
1786
+ phase: "structured_generation"
1787
+ }, "Phase 2 requested timeout exceeded maximum allowed, capping to 10 minutes");
1788
+ return phase2TimeoutMs;
1789
+ }
1790
+ async buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow) {
1791
+ const phase2Messages = [{
1792
+ role: "system",
1793
+ content: await this.buildPhase2SystemPrompt(runtimeContext)
1794
+ }];
1795
+ if (conversationHistory.trim() !== "") phase2Messages.push({
1796
+ role: "user",
1797
+ content: conversationHistory
1798
+ });
1799
+ phase2Messages.push({
1800
+ role: "user",
1801
+ content: userMessage
1802
+ });
1803
+ phase2Messages.push(...reasoningFlow);
1804
+ if (reasoningFlow.length > 0 && reasoningFlow[reasoningFlow.length - 1]?.role === "assistant") phase2Messages.push({
1805
+ role: "user",
1806
+ content: "Continue with the structured response."
1807
+ });
1808
+ return phase2Messages;
1809
+ }
1810
+ async executeStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, sessionId, contextId, response) {
1811
+ const streamResult = streamObject({
1812
+ ...structuredModelSettings,
1813
+ messages: phase2Messages,
1814
+ schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
1815
+ experimental_telemetry: this.buildTelemetryConfig("structured_generation"),
1816
+ abortSignal: AbortSignal.timeout(phase2TimeoutMs)
1817
+ });
1818
+ const parser = this.setupStreamParser(sessionId, contextId);
1819
+ for await (const delta of streamResult.partialObjectStream) if (delta) await parser.processObjectDelta(delta);
1820
+ await parser.finalize();
1821
+ const structuredResponse = await streamResult;
1822
+ const collectedParts = parser.getCollectedParts();
1823
+ if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
1824
+ kind: part.kind,
1825
+ ...part.kind === "text" && { text: part.text },
1826
+ ...part.kind === "data" && { data: part.data }
1827
+ })) };
1828
+ return {
1829
+ ...response,
1830
+ object: structuredResponse.object,
1831
+ textResponse: JSON.stringify(structuredResponse.object, null, 2)
1832
+ };
1833
+ }
1834
+ async executeNonStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, response) {
1835
+ const structuredResponse = await generateObject(withJsonPostProcessing({
1836
+ ...structuredModelSettings,
1837
+ messages: phase2Messages,
1838
+ schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
1839
+ experimental_telemetry: this.buildTelemetryConfig("structured_generation"),
1840
+ abortSignal: AbortSignal.timeout(phase2TimeoutMs)
1841
+ }));
1842
+ return {
1843
+ ...response,
1844
+ object: structuredResponse.object,
1845
+ textResponse: JSON.stringify(structuredResponse.object, null, 2)
1846
+ };
1847
+ }
1848
+ async formatFinalResponse(response, textResponse, sessionId, contextId) {
1849
+ let formattedContent = response.formattedContent || null;
1850
+ if (!formattedContent) {
1851
+ const session = toolSessionManager.getSession(sessionId);
1852
+ const responseFormatter = new ResponseFormatter(this.config.tenantId, {
1853
+ sessionId,
1854
+ taskId: session?.taskId,
1855
+ projectId: session?.projectId,
1856
+ contextId,
1857
+ artifactComponents: this.artifactComponents,
1858
+ streamRequestId: this.getStreamRequestId(),
1859
+ subAgentId: this.config.id
1860
+ });
1861
+ if (response.object) formattedContent = await responseFormatter.formatObjectResponse(response.object, contextId);
1862
+ else if (textResponse) formattedContent = await responseFormatter.formatResponse(textResponse, contextId);
1863
+ }
1864
+ return {
1865
+ ...response,
1866
+ formattedContent
1867
+ };
1868
+ }
1869
+ handleGenerationError(error, span) {
1870
+ if (this.currentCompressor) this.currentCompressor.fullCleanup();
1871
+ this.currentCompressor = null;
1872
+ const errorToThrow = error instanceof Error ? error : new Error(String(error));
1873
+ setSpanWithError$1(span, errorToThrow);
1874
+ span.end();
1875
+ throw errorToThrow;
1876
+ }
1877
+ /**
1878
+ * Public cleanup method for external lifecycle management (e.g., session cleanup)
1879
+ * Performs full cleanup of compression state when agent/session is ending
1880
+ */
1881
+ cleanupCompression() {
1882
+ if (this.currentCompressor) {
1883
+ this.currentCompressor.fullCleanup();
1884
+ this.currentCompressor = null;
1885
+ }
1886
+ }
1887
+ async processStreamEvents(streamResult, parser) {
1888
+ for await (const event of streamResult.fullStream) switch (event.type) {
1889
+ case "text-delta":
1890
+ await parser.processTextChunk(event.text);
1891
+ break;
1892
+ case "tool-call":
1893
+ parser.markToolResult();
1894
+ break;
1895
+ case "tool-result":
1896
+ parser.markToolResult();
1897
+ break;
1898
+ case "finish":
1899
+ if (event.finishReason === "tool-calls") parser.markToolResult();
1900
+ break;
1901
+ case "error": {
1902
+ if (event.error instanceof Error) throw event.error;
1903
+ const errorMessage = event.error?.error?.message;
1904
+ throw new Error(errorMessage);
1905
+ }
1906
+ }
1907
+ await parser.finalize();
1908
+ }
1909
+ formatStreamingResponse(response, parser) {
1910
+ const collectedParts = parser.getCollectedParts();
1911
+ if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
1912
+ kind: part.kind,
1913
+ ...part.kind === "text" && { text: part.text },
1914
+ ...part.kind === "data" && { data: part.data }
1915
+ })) };
1916
+ const streamedContent = parser.getAllStreamedContent();
1917
+ if (streamedContent.length > 0) response.streamedContent = { parts: streamedContent.map((part) => ({
1918
+ kind: part.kind,
1919
+ ...part.kind === "text" && { text: part.text },
1920
+ ...part.kind === "data" && { data: part.data }
1921
+ })) };
1922
+ return response;
1923
+ }
1924
+ };
1925
+
1926
+ //#endregion
1927
+ export { Agent, hasToolCallWithPrefix };