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