@inkeep/agents-run-api 0.40.0 → 0.41.1

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.
@@ -50,6 +50,13 @@ async function handleMessageSend(c, agent, request) {
50
50
  try {
51
51
  const params = request.params;
52
52
  const { agentId } = getRequestExecutionContext(c);
53
+ const forwardedHeaders = {};
54
+ const xForwardedCookie = c.req.header("x-forwarded-cookie");
55
+ const authorization = c.req.header("authorization");
56
+ const cookie = c.req.header("cookie");
57
+ if (xForwardedCookie) forwardedHeaders["x-forwarded-cookie"] = xForwardedCookie;
58
+ else if (cookie) forwardedHeaders["x-forwarded-cookie"] = cookie;
59
+ if (authorization) forwardedHeaders.authorization = authorization;
53
60
  const task = {
54
61
  id: generateId(),
55
62
  input: { parts: params.message.parts.map((part) => ({
@@ -62,7 +69,8 @@ async function handleMessageSend(c, agent, request) {
62
69
  metadata: {
63
70
  blocking: params.configuration?.blocking ?? false,
64
71
  custom: { agent_id: agentId || "" },
65
- ...params.message.metadata
72
+ ...params.message.metadata,
73
+ forwardedHeaders: Object.keys(forwardedHeaders).length > 0 ? forwardedHeaders : void 0
66
74
  }
67
75
  }
68
76
  };
@@ -297,6 +305,13 @@ async function handleMessageStream(c, agent, request) {
297
305
  },
298
306
  id: request.id
299
307
  });
308
+ const forwardedHeaders = {};
309
+ const xForwardedCookie = c.req.header("x-forwarded-cookie");
310
+ const authorization = c.req.header("authorization");
311
+ const cookie = c.req.header("cookie");
312
+ if (xForwardedCookie) forwardedHeaders["x-forwarded-cookie"] = xForwardedCookie;
313
+ else if (cookie) forwardedHeaders["x-forwarded-cookie"] = cookie;
314
+ if (authorization) forwardedHeaders.authorization = authorization;
300
315
  const task = {
301
316
  id: generateId(),
302
317
  input: { parts: params.message.parts.map((part) => ({
@@ -308,7 +323,8 @@ async function handleMessageStream(c, agent, request) {
308
323
  conversationId: params.message.contextId,
309
324
  metadata: {
310
325
  blocking: false,
311
- custom: { agent_id: agentId || "" }
326
+ custom: { agent_id: agentId || "" },
327
+ forwardedHeaders: Object.keys(forwardedHeaders).length > 0 ? forwardedHeaders : void 0
312
328
  }
313
329
  }
314
330
  };
@@ -48,6 +48,8 @@ type AgentConfig = {
48
48
  sandboxConfig?: SandboxConfig;
49
49
  /** User ID for user-scoped credential lookup (from temp JWT) */
50
50
  userId?: string;
51
+ /** Headers to forward to MCP servers (e.g., x-forwarded-cookie for user session auth) */
52
+ forwardedHeaders?: Record<string, string>;
51
53
  };
52
54
  type ExternalAgentRelationConfig = {
53
55
  relationId: string;
@@ -5,7 +5,6 @@ import { toolSessionManager } from "./ToolSessionManager.js";
5
5
  import { getCompressionConfigForModel } from "../utils/model-context-utils.js";
6
6
  import { setSpanWithError as setSpanWithError$1, tracer } from "../utils/tracer.js";
7
7
  import { getModelAwareCompressionConfig } from "../services/BaseCompressor.js";
8
- import "../services/ConversationCompressor.js";
9
8
  import { createDefaultConversationHistoryConfig, getConversationHistoryWithCompression } from "../data/conversations.js";
10
9
  import { getStreamHelper } from "../utils/stream-registry.js";
11
10
  import { agentSessionManager } from "../services/AgentSession.js";
@@ -25,7 +24,7 @@ import { Phase2Config } from "./versions/v1/Phase2Config.js";
25
24
  import { z } from "@hono/zod-openapi";
26
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";
27
26
  import { SpanStatusCode, trace } from "@opentelemetry/api";
28
- import { generateObject, generateText, streamObject, streamText, tool } from "ai";
27
+ import { Output, generateText, streamText, tool } from "ai";
29
28
 
30
29
  //#region src/agents/Agent.ts
31
30
  /**
@@ -540,7 +539,8 @@ var Agent = class {
540
539
  };
541
540
  }
542
541
  async getMcpTool(tool$1) {
543
- const cacheKey = `${this.config.tenantId}-${this.config.projectId}-${tool$1.id}-${tool$1.credentialReferenceId || "no-cred"}`;
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
544
  const credentialReferenceId = tool$1.credentialReferenceId;
545
545
  const toolRelation = (await getToolsForAgent(dbClient_default)({ scopes: {
546
546
  tenantId: this.config.tenantId,
@@ -627,11 +627,16 @@ var Agent = class {
627
627
  else urlObj.searchParams.set("user_id", `${this.config.tenantId}||${this.config.projectId}`);
628
628
  serverConfig.url = urlObj.toString();
629
629
  }
630
+ if (this.config.forwardedHeaders && Object.keys(this.config.forwardedHeaders).length > 0) serverConfig.headers = {
631
+ ...serverConfig.headers,
632
+ ...this.config.forwardedHeaders
633
+ };
630
634
  logger.info({
631
635
  toolName: tool$1.name,
632
636
  credentialReferenceId,
633
637
  transportType: serverConfig.type,
634
- headers: tool$1.headers
638
+ headers: tool$1.headers,
639
+ hasForwardedHeaders: !!this.config.forwardedHeaders
635
640
  }, "Built MCP server config with credentials");
636
641
  let client = this.mcpClientCache.get(cacheKey);
637
642
  if (client && !client.isConnected()) {
@@ -1042,7 +1047,7 @@ var Agent = class {
1042
1047
  }
1043
1048
  getArtifactTools() {
1044
1049
  return tool({
1045
- description: "Call this tool to get the complete artifact data with the given artifactId. This retrieves the full artifact content (not just the summary). Only use this when you need the complete artifact data and the summary shown in your context is insufficient.",
1050
+ description: "Call this tool to retrieve EXISTING artifacts that were previously created and saved. This tool is for accessing artifacts that already exist, NOT for extracting tool results. Only use this when you need the complete artifact data and the summary shown in your context is insufficient.",
1046
1051
  inputSchema: z.object({
1047
1052
  artifactId: z.string().describe("The unique identifier of the artifact to get."),
1048
1053
  toolCallId: z.string().describe("The tool call ID associated with this artifact.")
@@ -1638,11 +1643,13 @@ ${typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, nu
1638
1643
  if (error && typeof error === "object" && "name" in error && error.name === "connection_refused") return true;
1639
1644
  }
1640
1645
  }
1641
- if (steps.length >= 2) {
1642
- const previousStep = steps[steps.length - 2];
1643
- if (previousStep && "toolCalls" in previousStep && previousStep.toolCalls) {
1644
- const stopToolNames = includeThinkingComplete ? ["transfer_to_", "thinking_complete"] : ["transfer_to_"];
1645
- if (previousStep.toolCalls.some((tc) => stopToolNames.some((toolName) => toolName.endsWith("_") ? tc.toolName.startsWith(toolName) : tc.toolName === toolName)) && "toolResults" in previousStep && previousStep.toolResults) return true;
1646
+ if (steps.length >= 1) {
1647
+ const currentStep = steps[steps.length - 1];
1648
+ if (currentStep && "toolCalls" in currentStep && currentStep.toolCalls) {
1649
+ const hasTransferTool = currentStep.toolCalls.some((tc) => tc.toolName.startsWith("transfer_to_"));
1650
+ const hasThinkingComplete = currentStep.toolCalls.some((tc) => tc.toolName === "thinking_complete");
1651
+ if (hasTransferTool) return true;
1652
+ if (includeThinkingComplete && hasThinkingComplete && "toolResults" in currentStep && currentStep.toolResults) return true;
1646
1653
  }
1647
1654
  }
1648
1655
  return steps.length >= this.getMaxGenerationSteps();
@@ -1772,8 +1779,13 @@ ${output}${structureHintsFormatted}`;
1772
1779
  componentSchemas.push(ArtifactReferenceSchema.getSchema());
1773
1780
  }
1774
1781
  let dataComponentsSchema;
1775
- if (componentSchemas.length === 1) dataComponentsSchema = componentSchemas[0];
1776
- else dataComponentsSchema = z.union(componentSchemas);
1782
+ if (componentSchemas.length === 1) {
1783
+ dataComponentsSchema = componentSchemas[0];
1784
+ logger.info({ agentId: this.config.id }, "Using single schema (no union needed)");
1785
+ } else {
1786
+ dataComponentsSchema = z.union(componentSchemas);
1787
+ logger.info({ agentId: this.config.id }, "Created union schema");
1788
+ }
1777
1789
  return dataComponentsSchema;
1778
1790
  }
1779
1791
  calculatePhase2Timeout(structuredModelSettings) {
@@ -1808,15 +1820,15 @@ ${output}${structureHintsFormatted}`;
1808
1820
  return phase2Messages;
1809
1821
  }
1810
1822
  async executeStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, sessionId, contextId, response) {
1811
- const streamResult = streamObject({
1823
+ const streamResult = streamText({
1812
1824
  ...structuredModelSettings,
1813
1825
  messages: phase2Messages,
1814
- schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
1826
+ output: Output.object({ schema: z.object({ dataComponents: z.array(dataComponentsSchema) }) }),
1815
1827
  experimental_telemetry: this.buildTelemetryConfig("structured_generation"),
1816
1828
  abortSignal: AbortSignal.timeout(phase2TimeoutMs)
1817
1829
  });
1818
1830
  const parser = this.setupStreamParser(sessionId, contextId);
1819
- for await (const delta of streamResult.partialObjectStream) if (delta) await parser.processObjectDelta(delta);
1831
+ for await (const delta of streamResult.partialOutputStream) if (delta) await parser.processObjectDelta(delta);
1820
1832
  await parser.finalize();
1821
1833
  const structuredResponse = await streamResult;
1822
1834
  const collectedParts = parser.getCollectedParts();
@@ -1827,22 +1839,22 @@ ${output}${structureHintsFormatted}`;
1827
1839
  })) };
1828
1840
  return {
1829
1841
  ...response,
1830
- object: structuredResponse.object,
1831
- textResponse: JSON.stringify(structuredResponse.object, null, 2)
1842
+ object: structuredResponse.output,
1843
+ textResponse: JSON.stringify(structuredResponse.output, null, 2)
1832
1844
  };
1833
1845
  }
1834
1846
  async executeNonStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, response) {
1835
- const structuredResponse = await generateObject(withJsonPostProcessing({
1847
+ const structuredResponse = await generateText(withJsonPostProcessing({
1836
1848
  ...structuredModelSettings,
1837
1849
  messages: phase2Messages,
1838
- schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
1850
+ output: Output.object({ schema: z.object({ dataComponents: z.array(dataComponentsSchema) }) }),
1839
1851
  experimental_telemetry: this.buildTelemetryConfig("structured_generation"),
1840
1852
  abortSignal: AbortSignal.timeout(phase2TimeoutMs)
1841
1853
  }));
1842
1854
  return {
1843
1855
  ...response,
1844
- object: structuredResponse.object,
1845
- textResponse: JSON.stringify(structuredResponse.object, null, 2)
1856
+ object: structuredResponse.output,
1857
+ textResponse: JSON.stringify(structuredResponse.output, null, 2)
1846
1858
  };
1847
1859
  }
1848
1860
  async formatFinalResponse(response, textResponse, sessionId, contextId) {
@@ -21,6 +21,7 @@ const createTaskHandler = (config, credentialStoreRegistry) => {
21
21
  },
22
22
  artifacts: []
23
23
  };
24
+ const forwardedHeaders = task.context?.metadata?.forwardedHeaders;
24
25
  const [internalRelations, externalRelations, teamRelations, toolsForAgent, dataComponents, artifactComponents] = await Promise.all([
25
26
  getRelatedAgentsForAgent(dbClient_default)({
26
27
  scopes: {
@@ -322,7 +323,8 @@ const createTaskHandler = (config, credentialStoreRegistry) => {
322
323
  artifactComponents,
323
324
  contextConfigId: config.contextConfigId || void 0,
324
325
  conversationHistoryConfig: config.conversationHistoryConfig,
325
- sandboxConfig: config.sandboxConfig
326
+ sandboxConfig: config.sandboxConfig,
327
+ forwardedHeaders
326
328
  }, credentialStoreRegistry);
327
329
  const artifactStreamRequestId = task.context?.metadata?.streamRequestId;
328
330
  if (artifactStreamRequestId && artifactComponents.length > 0) agentSessionManager.updateArtifactComponents(artifactStreamRequestId, artifactComponents);
@@ -38,14 +38,24 @@ ${config.tools.map((tool$1) => {
38
38
  const toolsList = tool$1.availableTools?.map((t) => ` - ${t.name}: ${t.description || "No description available"}`).join("\n") || "";
39
39
  return `MCP Server: ${tool$1.name}\n${toolsList}`;
40
40
  }).join("\n\n")}`;
41
- return `Hand off the conversation to agent ${config.id}.
41
+ return `🚨 CRITICAL TRANSFER PROTOCOL 🚨
42
+
43
+ This tool immediately transfers conversation control to agent ${config.id}.
44
+
45
+ ⚠️ MANDATORY BEHAVIOR:
46
+ 1. DO NOT write any response to the user
47
+ 2. DO NOT explain what you're doing
48
+ 3. DO NOT provide partial answers
49
+ 4. ONLY call this tool and STOP
42
50
 
43
51
  Agent Information:
44
52
  - ID: ${config.id}
45
53
  - Name: ${config.name ?? "No name provided"}
46
54
  - Description: ${config.description ?? "No description provided"}${toolsSection}${transferSection}${delegateSection}
47
55
 
48
- Hand off the conversation to agent ${config.id} when the user's request would be better handled by this specialized agent.`;
56
+ 🔄 Use when: The user's request is better handled by this specialized agent.
57
+
58
+ ⛔ VIOLATION WARNING: Any text generation before/after this tool call will create a disjointed user experience. The receiving agent will provide the complete response.`;
49
59
  };
50
60
  const generateDelegateToolDescription = (delegateRelation) => {
51
61
  const config = delegateRelation.config;
@@ -70,7 +80,7 @@ ${agentConfig.transferRelations.map((transfer) => ` - ${transfer.name || transf
70
80
  Can Delegate To:
71
81
  ${agentConfig.delegateRelations.map((delegate) => ` - ${delegate.config.name || delegate.config.id}: ${delegate.config.description || "No description available"} (${delegate.type})`).join("\n")}`;
72
82
  }
73
- return `Delegate a specific task to another agent.
83
+ return `Delegate a specific task to another agent and wait for their response.
74
84
 
75
85
  Agent Information:
76
86
  - ID: ${config.id}
@@ -78,7 +88,9 @@ Agent Information:
78
88
  - Description: ${config.description || "No description provided"}
79
89
  - Type: ${delegateRelation.type}${toolsSection}${transferSection}${delegateSection}
80
90
 
81
- Delegate a specific task to agent ${config.id} when it seems like the agent can do relevant work.`;
91
+ Delegate a specific task to agent ${config.id} when it can do relevant work. The delegated agent will return results that you can incorporate into your response to the user.
92
+
93
+ NOTE: Unlike transfers, delegation returns control back to you with the delegated agent's results.`;
82
94
  };
83
95
  const createTransferToAgentTool = ({ transferConfig, callingAgentId, subAgent, streamRequestId }) => {
84
96
  return tool({
@@ -103,9 +103,20 @@ var Phase1Config = class Phase1Config {
103
103
  if (!hasTransferRelations) return "";
104
104
  return `You are part of a single unified assistant composed of specialized agents. To the user, you must always appear as one continuous, confident voice.
105
105
 
106
- You have transfer_to_* tools that seamlessly continue the conversation. When you determine another agent should handle a request: ONLY call the appropriate transfer_to_* tool. Do not provide any substantive answer, limitation, or explanation before transferring. NEVER announce, describe, or apologize for a transfer.
107
-
108
- Do NOT stream any text when transferring - call the transfer tool IMMEDIATELY. Do NOT acknowledge the request, do NOT say "Looking into that...", "Let me search...", "I'll help you find...", or provide ANY explanatory text. Place all reasoning or handoff details inside the transfer tool call, not in the user message. The tool call is sufficient - no additional text should be generated.
106
+ 🚨 CRITICAL TRANSFER PROTOCOL 🚨
107
+ When you determine another agent should handle a request:
108
+ 1. IMMEDIATELY call the appropriate transfer_to_* tool
109
+ 2. Generate ZERO text in your response - no words, no explanations, no acknowledgments
110
+ 3. Do NOT stream any content - the tool call must be your ONLY output
111
+
112
+ FORBIDDEN BEFORE TRANSFERS:
113
+ ❌ Do NOT acknowledge the request ("I understand you want...")
114
+ ❌ Do NOT provide partial answers ("The basics are..." then transfer)
115
+ ❌ Do NOT explain what you're doing ("Let me search...", "I'll help you find...")
116
+ ❌ Do NOT apologize or announce transfers ("I'll need to transfer you...")
117
+ ❌ Do NOT generate ANY text content whatsoever - just call the transfer tool
118
+
119
+ REMEMBER: Tool call = complete response. No additional text generation allowed.
109
120
 
110
121
  CRITICAL: When you receive a user message that ends with "Please continue from where this conversation was left off" - this indicates you are continuing a conversation that another agent started. You should:
111
122
  - Review the conversation history to see what was already communicated to the user
@@ -1,9 +1,9 @@
1
1
  import { SandboxConfig } from "./types/execution-context.js";
2
2
  import { CredentialStoreRegistry, ServerConfig } from "@inkeep/agents-core";
3
3
  import { Hono } from "hono";
4
- import * as hono_types0 from "hono/types";
4
+ import * as hono_types3 from "hono/types";
5
5
 
6
6
  //#region src/create-app.d.ts
7
- declare function createExecutionHono(serverConfig: ServerConfig, credentialStores: CredentialStoreRegistry, sandboxConfig?: SandboxConfig): Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
7
+ declare function createExecutionHono(serverConfig: ServerConfig, credentialStores: CredentialStoreRegistry, sandboxConfig?: SandboxConfig): Hono<hono_types3.BlankEnv, hono_types3.BlankSchema, "/">;
8
8
  //#endregion
9
9
  export { createExecutionHono };
@@ -10,6 +10,8 @@ interface ExecutionHandlerParams {
10
10
  requestId: string;
11
11
  sseHelper: StreamHelper;
12
12
  emitOperations?: boolean;
13
+ /** Headers to forward to MCP servers (e.g., x-forwarded-cookie for auth) */
14
+ forwardedHeaders?: Record<string, string>;
13
15
  }
14
16
  interface ExecutionResult {
15
17
  success: boolean;
@@ -32,7 +32,7 @@ var ExecutionHandler = class {
32
32
  * @returns
33
33
  */
34
34
  async execute(params) {
35
- const { executionContext, conversationId, userMessage, initialAgentId, requestId, sseHelper, emitOperations } = params;
35
+ const { executionContext, conversationId, userMessage, initialAgentId, requestId, sseHelper, emitOperations, forwardedHeaders } = params;
36
36
  const { tenantId, projectId, agentId, apiKey, baseUrl } = executionContext;
37
37
  registerStreamHelper(requestId, sseHelper);
38
38
  agentSessionManager.createSession(requestId, agentId, tenantId, projectId, conversationId);
@@ -177,7 +177,8 @@ var ExecutionHandler = class {
177
177
  "x-inkeep-tenant-id": tenantId,
178
178
  "x-inkeep-project-id": projectId,
179
179
  "x-inkeep-agent-id": agentId,
180
- "x-inkeep-sub-agent-id": currentAgentId
180
+ "x-inkeep-sub-agent-id": currentAgentId,
181
+ ...forwardedHeaders || {}
181
182
  } });
182
183
  let messageResponse = null;
183
184
  const messageMetadata = { stream_request_id: requestId };
package/dist/index.d.ts CHANGED
@@ -3,14 +3,14 @@ import { createExecutionHono } from "./create-app.js";
3
3
  import "./env.js";
4
4
  import { CredentialStore, ServerConfig } from "@inkeep/agents-core";
5
5
  import { Hono } from "hono";
6
- import * as hono_types1 from "hono/types";
6
+ import * as hono_types0 from "hono/types";
7
7
 
8
8
  //#region src/index.d.ts
9
- declare const app: Hono<hono_types1.BlankEnv, hono_types1.BlankSchema, "/">;
9
+ declare const app: Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
10
10
  declare function createExecutionApp(config?: {
11
11
  serverConfig?: ServerConfig;
12
12
  credentialStores?: CredentialStore[];
13
13
  sandboxConfig?: SandboxConfig;
14
- }): Hono<hono_types1.BlankEnv, hono_types1.BlankSchema, "/">;
14
+ }): Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
15
15
  //#endregion
16
16
  export { Hono, type NativeSandboxConfig, type SandboxConfig, type VercelSandboxConfig, createExecutionApp, createExecutionHono, app as default };
@@ -108,12 +108,18 @@ app.openapi(chatCompletionsRoute, async (c) => {
108
108
  }, "Extracted chat parameters from API key context");
109
109
  const body = c.get("requestBody") || {};
110
110
  const conversationId = body.conversationId || getConversationId();
111
+ const targetTenantId = c.req.header("x-target-tenant-id");
112
+ const targetProjectId = c.req.header("x-target-project-id");
113
+ const targetAgentId = c.req.header("x-target-agent-id");
111
114
  const activeSpan = trace.getActiveSpan();
112
115
  if (activeSpan) activeSpan.setAttributes({
113
116
  "conversation.id": conversationId,
114
117
  "tenant.id": tenantId,
115
118
  "agent.id": agentId,
116
- "project.id": projectId
119
+ "project.id": projectId,
120
+ ...targetTenantId && { "target.tenant.id": targetTenantId },
121
+ ...targetProjectId && { "target.project.id": targetProjectId },
122
+ ...targetAgentId && { "target.agent.id": targetAgentId }
117
123
  });
118
124
  let currentBag = propagation.getBaggage(context.active());
119
125
  if (!currentBag) currentBag = propagation.createBaggage();
@@ -244,6 +250,11 @@ app.openapi(chatCompletionsRoute, async (c) => {
244
250
  await sseHelper.writeRole();
245
251
  logger.info({ subAgentId }, "Starting execution");
246
252
  const emitOperations = c.req.header("x-emit-operations") === "true";
253
+ const forwardedHeaders = {};
254
+ const xForwardedCookie = c.req.header("x-forwarded-cookie");
255
+ const cookie = c.req.header("cookie");
256
+ if (xForwardedCookie) forwardedHeaders["x-forwarded-cookie"] = xForwardedCookie;
257
+ else if (cookie) forwardedHeaders["x-forwarded-cookie"] = cookie;
247
258
  const result = await new ExecutionHandler().execute({
248
259
  executionContext,
249
260
  conversationId,
@@ -251,7 +262,8 @@ app.openapi(chatCompletionsRoute, async (c) => {
251
262
  initialAgentId: subAgentId,
252
263
  requestId,
253
264
  sseHelper,
254
- emitOperations
265
+ emitOperations,
266
+ forwardedHeaders
255
267
  });
256
268
  logger.info({ result }, `Execution completed: ${result.success ? "success" : "failed"} after ${result.iterations} iterations`);
257
269
  if (!result.success) await sseHelper.writeOperation(errorOp("Sorry, I was unable to process your request at this time. Please try again.", "system"));
@@ -72,12 +72,23 @@ app.openapi(chatDataStreamRoute, async (c) => {
72
72
  }, "Extracted chatDataStream parameters");
73
73
  const body = c.get("requestBody") || {};
74
74
  const conversationId = body.conversationId || getConversationId();
75
+ const targetTenantId = c.req.header("x-target-tenant-id");
76
+ const targetProjectId = c.req.header("x-target-project-id");
77
+ const targetAgentId = c.req.header("x-target-agent-id");
78
+ const forwardedHeaders = {};
79
+ const xForwardedCookie = c.req.header("x-forwarded-cookie");
80
+ const cookie = c.req.header("cookie");
81
+ if (xForwardedCookie) forwardedHeaders["x-forwarded-cookie"] = xForwardedCookie;
82
+ else if (cookie) forwardedHeaders["x-forwarded-cookie"] = cookie;
75
83
  const activeSpan = trace.getActiveSpan();
76
84
  if (activeSpan) activeSpan.setAttributes({
77
85
  "conversation.id": conversationId,
78
86
  "tenant.id": tenantId,
79
87
  "agent.id": agentId,
80
- "project.id": projectId
88
+ "project.id": projectId,
89
+ ...targetTenantId && { "target.tenant.id": targetTenantId },
90
+ ...targetProjectId && { "target.project.id": targetProjectId },
91
+ ...targetAgentId && { "target.agent.id": targetAgentId }
81
92
  });
82
93
  let currentBag = propagation.getBaggage(context.active());
83
94
  if (!currentBag) currentBag = propagation.createBaggage();
@@ -177,7 +188,8 @@ app.openapi(chatDataStreamRoute, async (c) => {
177
188
  initialAgentId: subAgentId,
178
189
  requestId: `chat-${Date.now()}`,
179
190
  sseHelper: bufferingHelper,
180
- emitOperations
191
+ emitOperations,
192
+ forwardedHeaders
181
193
  });
182
194
  const captured = bufferingHelper.getCapturedResponse();
183
195
  return c.json({
@@ -211,7 +223,8 @@ app.openapi(chatDataStreamRoute, async (c) => {
211
223
  initialAgentId: subAgentId,
212
224
  requestId: `chatds-${Date.now()}`,
213
225
  sseHelper: streamHelper,
214
- emitOperations
226
+ emitOperations,
227
+ forwardedHeaders
215
228
  })).success) await streamHelper.writeOperation(errorOp("Unable to process request", "system"));
216
229
  } catch (err) {
217
230
  logger.error({ err }, "Streaming error");
@@ -11,7 +11,7 @@ import { ArtifactParser } from "./ArtifactParser.js";
11
11
  import { z } from "@hono/zod-openapi";
12
12
  import { CONVERSATION_HISTORY_DEFAULT_LIMIT, CONVERSATION_HISTORY_MAX_OUTPUT_TOKENS_DEFAULT, ModelFactory, getLedgerArtifacts, getSubAgentById } from "@inkeep/agents-core";
13
13
  import { SpanStatusCode } from "@opentelemetry/api";
14
- import { generateObject } from "ai";
14
+ import { Output, generateText } from "ai";
15
15
 
16
16
  //#region src/services/AgentSession.ts
17
17
  const logger = getLogger("AgentSession");
@@ -517,16 +517,22 @@ var AgentSession = class {
517
517
  }, "Failed to fetch conversation history for structured status update");
518
518
  }
519
519
  const previousSummaryContext = previousSummaries.length > 0 ? `\nPrevious updates sent to user:\n${previousSummaries.map((s, i) => `${i + 1}. ${s}`).join("\n")}\n` : "";
520
- const selectionSchema = z.object(Object.fromEntries([["no_relevant_updates", z.object({ no_updates: z.boolean().default(true) }).optional().describe("Use when nothing substantially new to report. Should only use on its own.")], ...statusComponents.map((component) => [component.type, this.getComponentSchema(component).optional().describe(component.description || component.type)])]));
520
+ const selectionSchema = z.object({ updates: z.array(z.union([z.object({
521
+ type: z.literal("no_relevant_updates"),
522
+ data: z.object({ no_updates: z.boolean().default(true) }).describe("Use when nothing substantially new to report. Should only use on its own.")
523
+ }), ...statusComponents.map((component) => z.object({
524
+ type: z.literal(component.type),
525
+ data: this.getComponentSchema(component).describe(component.description || component.type)
526
+ }))])) });
521
527
  const prompt = `Generate status updates for relevant components based on what the user has asked for.${conversationContext}${previousSummaries.length > 0 ? `\n${previousSummaryContext}` : ""}
522
528
 
523
529
  Activities:\n${userVisibleActivities.join("\n") || "No New Activities"}
524
530
 
525
- Available components: no_relevant_updates, ${statusComponents.map((c) => c.type).join(", ")}
531
+ Available component types: no_relevant_updates, ${statusComponents.map((c) => c.type).join(", ")}
526
532
 
527
533
  Rules:
528
- - Fill in data for relevant components only
529
- - Use 'no_relevant_updates' if nothing substantially new to report. DO NOT WRITE LABELS OR USE OTHER COMPONENTS IF YOU USE THIS COMPONENT.
534
+ - Return an array of updates for relevant components
535
+ - Use 'no_relevant_updates' type if nothing substantially new to report. DO NOT INCLUDE OTHER COMPONENT TYPES IF YOU USE THIS ONE.
530
536
  - Never repeat previous values, make every update EXTREMELY unique. If you cannot do that the update is not worth mentioning.
531
537
  - Labels MUST be short 3-7 word phrases with ACTUAL information discovered. NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION.
532
538
  - Use sentence case: only capitalize the first word and proper nouns (e.g., "Admin permissions required", not "Admin Permissions Required"). ALWAYS capitalize the first word of the label.
@@ -577,10 +583,10 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
577
583
  modelToUse = this.statusUpdateState.baseModel;
578
584
  }
579
585
  if (!modelToUse) throw new Error("No model configuration available");
580
- const { object } = await generateObject({
586
+ const { output: object } = await generateText({
581
587
  model: ModelFactory.createModel(modelToUse),
582
588
  prompt,
583
- schema: selectionSchema,
589
+ output: Output.object({ schema: selectionSchema }),
584
590
  experimental_telemetry: {
585
591
  isEnabled: true,
586
592
  functionId: `structured_update_${this.sessionId}`,
@@ -595,21 +601,19 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
595
601
  const result = object;
596
602
  logger.info({ result: JSON.stringify(result) }, "DEBUG: Result");
597
603
  const summaries = [];
598
- for (const [componentId, data] of Object.entries(result)) {
599
- logger.info({
600
- componentId,
601
- data: JSON.stringify(data)
602
- }, "DEBUG: Component data");
603
- if (componentId === "no_relevant_updates") continue;
604
- if (data && typeof data === "object" && Object.keys(data).length > 0) summaries.push({
605
- type: componentId,
606
- data
604
+ const updates = result.updates || [];
605
+ for (const update of updates) {
606
+ logger.info({ update: JSON.stringify(update) }, "DEBUG: Update data");
607
+ if (update.type === "no_relevant_updates") continue;
608
+ if (update.data && typeof update.data === "object" && Object.keys(update.data).length > 0) summaries.push({
609
+ type: update.type,
610
+ data: update.data
607
611
  });
608
612
  }
609
613
  span.setAttributes({
610
614
  "summaries.count": summaries.length,
611
615
  "user_activities.count": userVisibleActivities.length,
612
- "result_keys.count": Object.keys(result).length
616
+ "updates.count": updates.length
613
617
  });
614
618
  span.setStatus({ code: SpanStatusCode.OK });
615
619
  return { summaries };
@@ -875,7 +879,7 @@ Make the name extremely specific to what this tool call actually returned, not g
875
879
  name: z.string().describe("Concise, descriptive name for the artifact"),
876
880
  description: z.string().describe("Brief description of the artifact's relevance to the user's question")
877
881
  });
878
- const { object } = await tracer.startActiveSpan("agent_session.generate_artifact_metadata", { attributes: {
882
+ const { output: object } = await tracer.startActiveSpan("agent_session.generate_artifact_metadata", { attributes: {
879
883
  "llm.model": this.statusUpdateState?.summarizerModel?.model,
880
884
  "llm.operation": "generate_object",
881
885
  "artifact.id": artifactData.artifactId,
@@ -887,10 +891,10 @@ Make the name extremely specific to what this tool call actually returned, not g
887
891
  const maxRetries = 3;
888
892
  let lastError = null;
889
893
  for (let attempt = 1; attempt <= maxRetries; attempt++) try {
890
- const result$1 = await generateObject({
894
+ const result$1 = await generateText({
891
895
  model,
892
896
  prompt,
893
- schema,
897
+ output: Output.object({ schema }),
894
898
  experimental_telemetry: {
895
899
  isEnabled: true,
896
900
  functionId: `artifact_processing_${artifactData.artifactId}`,
@@ -906,12 +910,12 @@ Make the name extremely specific to what this tool call actually returned, not g
906
910
  generationSpan.setAttributes({
907
911
  "artifact.id": artifactData.artifactId,
908
912
  "artifact.type": artifactData.artifactType,
909
- "artifact.name": result$1.object.name,
910
- "artifact.description": result$1.object.description,
913
+ "artifact.name": result$1.output.name,
914
+ "artifact.description": result$1.output.description,
911
915
  "artifact.summary": JSON.stringify(artifactData.summaryData, null, 2),
912
916
  "artifact.full": JSON.stringify(artifactData.data || artifactData.summaryData, null, 2),
913
- "generation.name_length": result$1.object.name.length,
914
- "generation.description_length": result$1.object.description.length,
917
+ "generation.name_length": result$1.output.name.length,
918
+ "generation.description_length": result$1.output.description.length,
915
919
  "generation.attempts": attempt
916
920
  });
917
921
  generationSpan.setStatus({ code: SpanStatusCode.OK });
@@ -409,7 +409,6 @@ var BaseCompressor = class {
409
409
  return await tracer.startActiveSpan("compressor.safe_compress", { attributes: {
410
410
  "compression.type": this.getCompressionType(),
411
411
  "compression.session_id": this.sessionId,
412
- "compression.conversation_id": this.conversationId,
413
412
  "compression.message_count": messages.length,
414
413
  "compression.input_tokens": fullContextSize ?? this.calculateContextSize(messages),
415
414
  "compression.hard_limit": this.getHardLimit(),
@@ -423,7 +422,7 @@ var BaseCompressor = class {
423
422
  "compression.result.output_tokens": resultTokens,
424
423
  "compression.result.compression_ratio": (fullContextSize ?? this.calculateContextSize(messages)) > 0 ? ((fullContextSize ?? this.calculateContextSize(messages)) - resultTokens) / (fullContextSize ?? this.calculateContextSize(messages)) : 0,
425
424
  "compression.success": true,
426
- "compression.fallback_used": false
425
+ "compression.result.summary": result.summary?.high_level || ""
427
426
  });
428
427
  compressionSpan.setStatus({ code: SpanStatusCode.OK });
429
428
  return result;
@@ -434,10 +433,7 @@ var BaseCompressor = class {
434
433
  error: error instanceof Error ? error.message : String(error),
435
434
  stack: error instanceof Error ? error.stack : void 0
436
435
  }, "Compression failed, using simple fallback");
437
- compressionSpan.setAttributes({
438
- "compression.error": error instanceof Error ? error.message : String(error),
439
- "compression.fallback_used": true
440
- });
436
+ compressionSpan.setAttributes({ "compression.error": error instanceof Error ? error.message : String(error) });
441
437
  const fallbackResult = await this.simpleCompressionFallback(messages);
442
438
  const fallbackTokens = Array.isArray(fallbackResult.summary) ? this.calculateContextSize(fallbackResult.summary) : this.estimateTokens(fallbackResult.summary);
443
439
  compressionSpan.setAttributes({
@@ -1,6 +1,6 @@
1
1
  import { getLogger } from "../logger.js";
2
2
  import { ModelFactory } from "@inkeep/agents-core";
3
- import { generateObject } from "ai";
3
+ import { Output, generateText } from "ai";
4
4
  import { z } from "zod";
5
5
 
6
6
  //#region src/tools/distill-conversation-history-tool.ts
@@ -57,7 +57,7 @@ async function distillConversationHistory(params) {
57
57
  const { messages, conversationId, summarizerModel, toolCallToArtifactMap } = params;
58
58
  try {
59
59
  if (!summarizerModel?.model?.trim()) throw new Error("Summarizer model is required");
60
- const { object: summary } = await generateObject({
60
+ const { output: summary } = await generateText({
61
61
  model: ModelFactory.createModel(summarizerModel),
62
62
  prompt: `You are a conversation history summarization assistant. Your job is to create a comprehensive summary that can COMPLETELY REPLACE the original conversation history while preserving all essential context.
63
63
 
@@ -158,7 +158,7 @@ Create a comprehensive summary using this exact JSON schema:
158
158
  **REMEMBER**: This summary is REPLACING the entire conversation history. Include everything essential for context continuation.
159
159
 
160
160
  Return **only** valid JSON.`,
161
- schema: ConversationHistorySummarySchema
161
+ output: Output.object({ schema: ConversationHistorySummarySchema })
162
162
  });
163
163
  summary.session_id = conversationId;
164
164
  return summary;
@@ -1,6 +1,6 @@
1
1
  import { getLogger } from "../logger.js";
2
2
  import { ModelFactory } from "@inkeep/agents-core";
3
- import { generateObject, tool } from "ai";
3
+ import { Output, generateText } from "ai";
4
4
  import { z } from "zod";
5
5
 
6
6
  //#region src/tools/distill-conversation-tool.ts
@@ -36,7 +36,7 @@ async function distillConversation(params) {
36
36
  try {
37
37
  const modelToUse = summarizerModel;
38
38
  if (!modelToUse?.model?.trim()) throw new Error("Summarizer model is required");
39
- const { object: summary } = await generateObject({
39
+ const { output: summary } = await generateText({
40
40
  model: ModelFactory.createModel(modelToUse),
41
41
  prompt: `You are a conversation summarization assistant. Your job is to create or update a compact, structured summary that captures VALUABLE CONTENT and FINDINGS, not just operational details.
42
42
 
@@ -111,7 +111,7 @@ Create/update a summary using this exact JSON schema:
111
111
  **Focus on WHAT WAS LEARNED, not HOW IT WAS LEARNED**
112
112
 
113
113
  Return **only** valid JSON.`,
114
- schema: ConversationSummarySchema
114
+ output: Output.object({ schema: ConversationSummarySchema })
115
115
  });
116
116
  summary.session_id = conversationId;
117
117
  return summary;
@@ -4,7 +4,7 @@
4
4
  */
5
5
  declare function stripJsonCodeBlocks(text: string): string;
6
6
  /**
7
- * Configuration helper to add JSON post-processing to generateObject calls
7
+ * Configuration helper to add JSON post-processing to structured output generateText calls
8
8
  */
9
9
  declare function withJsonPostProcessing<T extends Record<string, any>>(config: T): T & {
10
10
  experimental_transform?: (text: string) => string;
@@ -6,7 +6,7 @@ function stripJsonCodeBlocks(text) {
6
6
  return text.trim().replace(/^```json\s*/is, "").replace(/^```\s*/s, "").replace(/\s*```$/s, "").replace(/^```json\s*([\s\S]*?)\s*```$/i, "$1").replace(/^```\s*([\s\S]*?)\s*```$/i, "$1").trim();
7
7
  }
8
8
  /**
9
- * Configuration helper to add JSON post-processing to generateObject calls
9
+ * Configuration helper to add JSON post-processing to structured output generateText calls
10
10
  */
11
11
  function withJsonPostProcessing(config) {
12
12
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-run-api",
3
- "version": "0.40.0",
3
+ "version": "0.41.1",
4
4
  "description": "Agents Run API for Inkeep Agent Framework - handles chat, agent execution, and streaming",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -13,13 +13,13 @@
13
13
  "type": "module",
14
14
  "license": "SEE LICENSE IN LICENSE.md",
15
15
  "dependencies": {
16
- "@electric-sql/pglite": "^0.3.13",
17
- "@ai-sdk/anthropic": "3.0.0-beta.66",
18
- "@ai-sdk/gateway": "2.0.0-beta.68",
19
- "@ai-sdk/google": "3.0.0-beta.62",
20
- "@ai-sdk/openai": "3.0.0-beta.74",
21
- "@ai-sdk/openai-compatible": "2.0.0-beta.41",
16
+ "@ai-sdk/anthropic": "3.0.7",
17
+ "@ai-sdk/gateway": "3.0.9",
18
+ "@ai-sdk/google": "3.0.4",
19
+ "@ai-sdk/openai": "3.0.7",
20
+ "@ai-sdk/openai-compatible": "2.0.4",
22
21
  "@alcyone-labs/modelcontextprotocol-sdk": "^1.16.0",
22
+ "@electric-sql/pglite": "^0.3.13",
23
23
  "@hono/otel": "^0.4.0",
24
24
  "@hono/swagger-ui": "^0.5.1",
25
25
  "@openrouter/ai-sdk-provider": "^1.2.0",
@@ -34,14 +34,14 @@
34
34
  "@opentelemetry/sdk-trace-base": "^2.1.0",
35
35
  "@opentelemetry/semantic-conventions": "^1.37.0",
36
36
  "@vercel/sandbox": "^0.0.24",
37
- "ai": "6.0.0-beta.124",
37
+ "ai": "6.0.14",
38
38
  "ajv": "^8.17.1",
39
39
  "drizzle-orm": "^0.44.4",
40
40
  "fetch-to-node": "^2.1.0",
41
41
  "hono": "^4.10.4",
42
42
  "jmespath": "^0.16.0",
43
43
  "llm-info": "^1.0.69",
44
- "@inkeep/agents-core": "^0.40.0"
44
+ "@inkeep/agents-core": "^0.41.1"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@hono/zod-openapi": "^1.1.5",