@inkeep/agents-run-api 0.0.0-dev-20250920001717 → 0.0.0-dev-20250924191551

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 (3) hide show
  1. package/dist/index.cjs +119 -229
  2. package/dist/index.js +120 -230
  3. package/package.json +5 -2
package/dist/index.cjs CHANGED
@@ -27,9 +27,12 @@ var streaming = require('hono/streaming');
27
27
  var destr = require('destr');
28
28
  var traverse = require('traverse');
29
29
  var ai = require('ai');
30
+ var agentsSdk = require('@inkeep/agents-sdk');
30
31
  var anthropic = require('@ai-sdk/anthropic');
32
+ var gateway = require('@ai-sdk/gateway');
31
33
  var google = require('@ai-sdk/google');
32
34
  var openai = require('@ai-sdk/openai');
35
+ var aiSdkProvider = require('@openrouter/ai-sdk-provider');
33
36
  var jmespath = require('jmespath');
34
37
  var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
35
38
  var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
@@ -1411,6 +1414,18 @@ var _ModelFactory = class _ModelFactory {
1411
1414
  return openai.createOpenAI(config);
1412
1415
  case "google":
1413
1416
  return google.createGoogleGenerativeAI(config);
1417
+ case "openrouter":
1418
+ return {
1419
+ ...aiSdkProvider.createOpenRouter(config),
1420
+ textEmbeddingModel: () => {
1421
+ throw new Error("OpenRouter does not support text embeddings");
1422
+ },
1423
+ imageModel: () => {
1424
+ throw new Error("OpenRouter does not support image generation");
1425
+ }
1426
+ };
1427
+ case "gateway":
1428
+ return gateway.createGateway(config);
1414
1429
  default:
1415
1430
  throw new Error(`Unsupported provider: ${provider}`);
1416
1431
  }
@@ -1470,26 +1485,29 @@ var _ModelFactory = class _ModelFactory {
1470
1485
  return openai.openai(modelName);
1471
1486
  case "google":
1472
1487
  return google.google(modelName);
1488
+ case "openrouter":
1489
+ return aiSdkProvider.openrouter(modelName);
1490
+ case "gateway":
1491
+ return gateway.gateway(modelName);
1473
1492
  default:
1474
- throw new Error(`Unsupported provider: ${provider}`);
1493
+ throw new Error(
1494
+ `Unsupported provider: ${provider}. Supported providers are: ${_ModelFactory.BUILT_IN_PROVIDERS.join(", ")}. To access other models, use OpenRouter (openrouter/model-id) or Vercel AI Gateway (gateway/model-id).`
1495
+ );
1475
1496
  }
1476
1497
  }
1477
1498
  /**
1478
1499
  * Parse model string to extract provider and model name
1479
1500
  * Examples: "anthropic/claude-sonnet-4" -> { provider: "anthropic", modelName: "claude-sonnet-4" }
1501
+ * "openrouter/anthropic/claude-sonnet-4" -> { provider: "openrouter", modelName: "anthropic/claude-sonnet-4" }
1480
1502
  * "claude-sonnet-4" -> { provider: "anthropic", modelName: "claude-sonnet-4" } (default to anthropic)
1481
1503
  */
1482
1504
  static parseModelString(modelString) {
1483
1505
  if (modelString.includes("/")) {
1484
1506
  const [provider, ...modelParts] = modelString.split("/");
1485
1507
  const normalizedProvider = provider.toLowerCase();
1486
- if (!_ModelFactory.SUPPORTED_PROVIDERS.includes(normalizedProvider)) {
1487
- logger5.error(
1488
- { provider: normalizedProvider, modelName: modelParts.join("/") },
1489
- "Unsupported provider detected, falling back to anthropic"
1490
- );
1508
+ if (!_ModelFactory.BUILT_IN_PROVIDERS.includes(normalizedProvider)) {
1491
1509
  throw new Error(
1492
- `Unsupported provider: ${normalizedProvider}. Please provide a model in the format of provider/model-name.`
1510
+ `Unsupported provider: ${normalizedProvider}. Supported providers are: ${_ModelFactory.BUILT_IN_PROVIDERS.join(", ")}. To access other models, use OpenRouter (openrouter/model-id) or Vercel AI Gateway (gateway/model-id).`
1493
1511
  );
1494
1512
  }
1495
1513
  return {
@@ -1498,9 +1516,7 @@ var _ModelFactory = class _ModelFactory {
1498
1516
  // In case model name has slashes
1499
1517
  };
1500
1518
  }
1501
- throw new Error(
1502
- `Invalid model provided: ${modelString}. Please provide a model in the format of provider/model-name.`
1503
- );
1519
+ throw new Error(`No provider specified in model string: ${modelString}`);
1504
1520
  }
1505
1521
  /**
1506
1522
  * Get generation parameters from provider options
@@ -1564,9 +1580,15 @@ var _ModelFactory = class _ModelFactory {
1564
1580
  }
1565
1581
  };
1566
1582
  /**
1567
- * Supported providers for security validation
1583
+ * Built-in providers that have special handling
1568
1584
  */
1569
- __publicField(_ModelFactory, "SUPPORTED_PROVIDERS", ["anthropic", "openai", "google"]);
1585
+ __publicField(_ModelFactory, "BUILT_IN_PROVIDERS", [
1586
+ "anthropic",
1587
+ "openai",
1588
+ "google",
1589
+ "openrouter",
1590
+ "gateway"
1591
+ ]);
1570
1592
  var ModelFactory = _ModelFactory;
1571
1593
 
1572
1594
  // src/utils/graph-session.ts
@@ -1908,68 +1930,52 @@ var GraphSession = class {
1908
1930
  }
1909
1931
  const now = Date.now();
1910
1932
  const elapsedTime = now - statusUpdateState.startTime;
1911
- let summaryToSend;
1912
- if (statusUpdateState.config.statusComponents && statusUpdateState.config.statusComponents.length > 0) {
1913
- const result = await this.generateStructuredStatusUpdate(
1914
- this.events.slice(statusUpdateState.lastEventCount),
1915
- elapsedTime,
1916
- statusUpdateState.config.statusComponents,
1917
- statusUpdateState.summarizerModel,
1918
- this.previousSummaries
1919
- );
1920
- if (result.summaries && result.summaries.length > 0) {
1921
- for (const summary of result.summaries) {
1922
- if (!summary || !summary.type || !summary.data || !summary.data.label || Object.keys(summary.data).length === 0) {
1923
- logger6.warn(
1924
- {
1925
- sessionId: this.sessionId,
1926
- summary
1927
- },
1928
- "Skipping empty or invalid structured operation"
1929
- );
1930
- continue;
1931
- }
1932
- const summaryToSend2 = {
1933
- type: summary.data.type || summary.type,
1934
- // Preserve the actual custom type from LLM
1935
- label: summary.data.label,
1936
- details: Object.fromEntries(
1937
- Object.entries(summary.data).filter(([key]) => !["label", "type"].includes(key))
1938
- )
1939
- };
1940
- await streamHelper.writeSummary(summaryToSend2);
1941
- }
1942
- const summaryTexts = result.summaries.map(
1943
- (summary) => JSON.stringify({ type: summary.type, data: summary.data })
1944
- );
1945
- this.previousSummaries.push(...summaryTexts);
1946
- if (this.statusUpdateState) {
1947
- this.statusUpdateState.lastUpdateTime = now;
1948
- this.statusUpdateState.lastEventCount = this.events.length;
1933
+ const statusComponents = statusUpdateState.config.statusComponents && statusUpdateState.config.statusComponents.length > 0 ? statusUpdateState.config.statusComponents : agentsSdk.defaultStatusSchemas;
1934
+ const result = await this.generateStructuredStatusUpdate(
1935
+ this.events.slice(statusUpdateState.lastEventCount),
1936
+ elapsedTime,
1937
+ statusComponents,
1938
+ statusUpdateState.summarizerModel,
1939
+ this.previousSummaries
1940
+ );
1941
+ if (result.summaries && result.summaries.length > 0) {
1942
+ for (const summary of result.summaries) {
1943
+ if (!summary || !summary.type || !summary.data || !summary.data.label || Object.keys(summary.data).length === 0) {
1944
+ logger6.warn(
1945
+ {
1946
+ sessionId: this.sessionId,
1947
+ summary
1948
+ },
1949
+ "Skipping empty or invalid structured operation"
1950
+ );
1951
+ continue;
1949
1952
  }
1950
- return;
1953
+ const summaryToSend = {
1954
+ type: summary.data.type || summary.type,
1955
+ // Preserve the actual custom type from LLM
1956
+ label: summary.data.label,
1957
+ details: Object.fromEntries(
1958
+ Object.entries(summary.data).filter(([key]) => !["label", "type"].includes(key))
1959
+ )
1960
+ };
1961
+ await streamHelper.writeSummary(summaryToSend);
1951
1962
  }
1952
- } else {
1953
- const summary = await this.generateProgressSummary(
1954
- this.events.slice(statusUpdateState.lastEventCount),
1955
- elapsedTime,
1956
- statusUpdateState.summarizerModel,
1957
- this.previousSummaries
1963
+ const summaryTexts = result.summaries.map(
1964
+ (summary) => JSON.stringify({ type: summary.type, data: summary.data })
1958
1965
  );
1959
- this.previousSummaries.push(summary);
1966
+ this.previousSummaries.push(...summaryTexts);
1967
+ if (this.statusUpdateState) {
1968
+ this.statusUpdateState.lastUpdateTime = now;
1969
+ this.statusUpdateState.lastEventCount = this.events.length;
1970
+ }
1971
+ return;
1960
1972
  }
1961
1973
  if (this.previousSummaries.length > 3) {
1962
1974
  this.previousSummaries.shift();
1963
1975
  }
1964
- {
1965
- logger6.warn(
1966
- {
1967
- sessionId: this.sessionId,
1968
- summaryToSend
1969
- },
1970
- "Skipping empty or invalid status update operation"
1971
- );
1972
- return;
1976
+ if (this.statusUpdateState) {
1977
+ this.statusUpdateState.lastUpdateTime = now;
1978
+ this.statusUpdateState.lastEventCount = this.events.length;
1973
1979
  }
1974
1980
  } catch (error) {
1975
1981
  logger6.error(
@@ -2050,106 +2056,6 @@ var GraphSession = class {
2050
2056
  this.statusUpdateState.updateLock = false;
2051
2057
  }
2052
2058
  }
2053
- /**
2054
- * Generate user-focused progress summary hiding internal operations
2055
- */
2056
- async generateProgressSummary(newEvents, elapsedTime, summarizerModel, previousSummaries = []) {
2057
- return tracer.startActiveSpan(
2058
- "graph_session.generate_progress_summary",
2059
- {
2060
- attributes: {
2061
- "graph_session.id": this.sessionId,
2062
- "events.count": newEvents.length,
2063
- "elapsed_time.seconds": Math.round(elapsedTime / 1e3),
2064
- "llm.model": summarizerModel?.model,
2065
- "previous_summaries.count": previousSummaries.length
2066
- }
2067
- },
2068
- async (span) => {
2069
- try {
2070
- const userVisibleActivities = this.extractUserVisibleActivities(newEvents);
2071
- let conversationContext = "";
2072
- if (this.tenantId && this.projectId) {
2073
- try {
2074
- const conversationHistory = await getFormattedConversationHistory({
2075
- tenantId: this.tenantId,
2076
- projectId: this.projectId,
2077
- conversationId: this.sessionId,
2078
- options: {
2079
- limit: 10,
2080
- // Get recent conversation context
2081
- maxOutputTokens: 2e3
2082
- },
2083
- filters: {}
2084
- });
2085
- conversationContext = conversationHistory.trim() ? `
2086
- User's Question/Context:
2087
- ${conversationHistory}
2088
- ` : "";
2089
- } catch (error) {
2090
- logger6.warn(
2091
- { sessionId: this.sessionId, error },
2092
- "Failed to fetch conversation history for status update"
2093
- );
2094
- }
2095
- }
2096
- const previousSummaryContext = previousSummaries.length > 0 ? `
2097
- Previous updates provided to user:
2098
- ${previousSummaries.map((s, i) => `${i + 1}. ${s}`).join("\n")}
2099
- ` : "";
2100
- const basePrompt = `Generate a meaningful status update that tells the user what specific information or result was just found/achieved.${conversationContext}${previousSummaries.length > 0 ? `
2101
- ${previousSummaryContext}` : ""}
2102
-
2103
- Activities:
2104
- ${userVisibleActivities.join("\n") || "No New Activities"}
2105
-
2106
- Create a short 3-5 word label describing the ACTUAL finding. Use sentence case (only capitalize the first word and proper nouns). Examples: "Found admin permissions needed", "Identified three channel types", "OAuth token required".
2107
-
2108
- ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2109
- const prompt = basePrompt;
2110
- let modelToUse = summarizerModel;
2111
- if (!summarizerModel?.model?.trim()) {
2112
- if (!this.statusUpdateState?.baseModel?.model?.trim()) {
2113
- throw new Error(
2114
- "Either summarizer or base model is required for progress summary generation. Please configure models at the project level."
2115
- );
2116
- }
2117
- modelToUse = this.statusUpdateState.baseModel;
2118
- }
2119
- if (!modelToUse) {
2120
- throw new Error("No model configuration available");
2121
- }
2122
- const model = ModelFactory.createModel(modelToUse);
2123
- const { text } = await ai.generateText({
2124
- model,
2125
- prompt,
2126
- experimental_telemetry: {
2127
- isEnabled: true,
2128
- functionId: `status_update_${this.sessionId}`,
2129
- recordInputs: true,
2130
- recordOutputs: true,
2131
- metadata: {
2132
- operation: "progress_summary_generation",
2133
- sessionId: this.sessionId
2134
- }
2135
- }
2136
- });
2137
- span.setAttributes({
2138
- "summary.length": text.trim().length,
2139
- "user_activities.count": userVisibleActivities.length
2140
- });
2141
- span.setStatus({ code: api.SpanStatusCode.OK });
2142
- return text.trim();
2143
- } catch (error) {
2144
- agentsCore.setSpanWithError(span, error);
2145
- logger6.error({ error }, "Failed to generate summary, using fallback");
2146
- return this.generateFallbackSummary(newEvents, elapsedTime);
2147
- } finally {
2148
- span.end();
2149
- }
2150
- }
2151
- );
2152
- }
2153
2059
  /**
2154
2060
  * Generate structured status update using configured data components
2155
2061
  */
@@ -2228,17 +2134,45 @@ Rules:
2228
2134
  - Fill in data for relevant components only
2229
2135
  - Use 'no_relevant_updates' if nothing substantially new to report. DO NOT WRITE LABELS OR USE OTHER COMPONENTS IF YOU USE THIS COMPONENT.
2230
2136
  - Never repeat previous values, make every update EXTREMELY unique. If you cannot do that the update is not worth mentioning.
2231
- - Labels MUST be short 3-5 word phrases with ACTUAL information discovered. NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION.
2137
+ - Labels MUST be short 3-7 word phrases with ACTUAL information discovered. NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION.
2232
2138
  - 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.
2233
2139
  - DO NOT use action words like "Searching", "Processing", "Analyzing" - state what was FOUND
2234
2140
  - Include specific details, numbers, requirements, or insights discovered
2235
2141
  - Examples: "Admin permissions required", "Three OAuth steps found", "Token expires daily"
2236
- - You are ONE unified AI system - NEVER mention agents, transfers, delegations, or routing
2237
- - CRITICAL: NEVER use the words "transfer", "delegation", "agent", "routing", "artifact", or any internal system terminology in labels or any names of agents, tools, or systems.
2238
- - Present all operations as seamless actions by a single system
2239
- - Anonymize all internal operations so that the information appears descriptive and USER FRIENDLY. HIDE ALL INTERNAL OPERATIONS!
2240
- - Bad examples: "Transferring to search agent", "continuing transfer to qa agent", "Delegating task", "Routing request", "Processing request", "Artifact found", "Artifact saved", or not using the no_relevant_updates
2241
- - Good examples: "Slack bot needs admin privileges", "Found 3-step OAuth flow required", "Channel limit is 500 per workspace", or use the no_relevant_updates component if nothing new to report.
2142
+
2143
+ CRITICAL - HIDE ALL INTERNAL SYSTEM OPERATIONS:
2144
+ - You are ONE unified AI system presenting results to the user
2145
+ - ABSOLUTELY FORBIDDEN WORDS/PHRASES: "transfer", "transferring", "delegation", "delegating", "delegate", "agent", "routing", "route", "artifact", "saving artifact", "stored artifact", "artifact saved", "continuing", "passing to", "handing off", "switching to"
2146
+ - NEVER reveal internal architecture: No mentions of different agents, components, systems, or modules working together
2147
+ - NEVER mention artifact operations: Users don't need to know about data being saved, stored, or organized internally
2148
+ - NEVER describe handoffs or transitions: Present everything as one seamless operation
2149
+ - If you see "transfer", "delegation_sent", "delegation_returned", or "artifact_saved" events - IGNORE THEM or translate to user-facing information only
2150
+ - Focus ONLY on actual discoveries, findings, and results that matter to the user
2151
+
2152
+ - Bad examples:
2153
+ * "Transferring to search agent"
2154
+ * "Delegating research task"
2155
+ * "Routing to QA specialist"
2156
+ * "Artifact saved successfully"
2157
+ * "Storing results for later"
2158
+ * "Passing request to tool handler"
2159
+ * "Continuing with analysis"
2160
+ * "Handing off to processor"
2161
+ - Good examples:
2162
+ * "Slack bot needs admin privileges"
2163
+ * "Found 3-step OAuth flow required"
2164
+ * "Channel limit is 500 per workspace"
2165
+ * Use no_relevant_updates if nothing new to report
2166
+
2167
+ CRITICAL ANTI-HALLUCINATION RULES:
2168
+ - NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION. EVERY SINGLE UPDATE MUST BE BACKED UP WITH ACTUAL INFORMATION.
2169
+ - DO NOT MAKE UP PEOPLE, NAMES, PLACES, THINGS, ORGANIZATIONS, OR INFORMATION. IT IS OBVIOUS WHEN A PERSON/ENTITY DOES NOT EXIST.
2170
+ - Only report facts that are EXPLICITLY mentioned in the activities or tool results
2171
+ - If you don't have concrete information about something, DO NOT mention it
2172
+ - Never invent names like "John Doe", "Alice", "Bob", or any other placeholder names
2173
+ - Never create fictional companies, products, or services
2174
+ - If a tool returned no results or an error, DO NOT pretend it found something
2175
+ - Every detail in your status update must be traceable back to the actual activities provided
2242
2176
 
2243
2177
  REMEMBER YOU CAN ONLY USE 'no_relevant_updates' ALONE! IT CANNOT BE CONCATENATED WITH OTHER STATUS UPDATES!
2244
2178
 
@@ -2311,7 +2245,7 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2311
2245
  }
2312
2246
  return z5.z.object({
2313
2247
  label: z5.z.string().describe(
2314
- 'A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The ACTUAL finding or result, not the action. What specific information was discovered? (e.g., "Slack requires OAuth 2.0 setup", "Found 5 integration methods", "API rate limit is 100/minute"). Include the actual detail or insight, not just that you searched or processed.'
2248
+ 'A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The ACTUAL finding or result, not the action. What specific information was discovered? (e.g., "Slack requires OAuth 2.0 setup", "Found 5 integration methods", "API rate limit is 100/minute"). Include the actual detail or insight, not just that you searched or processed. CRITICAL: Only use facts explicitly found in the activities - NEVER invent names, people, organizations, or details that are not present in the actual tool results.'
2315
2249
  )
2316
2250
  });
2317
2251
  }
@@ -2321,7 +2255,7 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2321
2255
  buildZodSchemaFromJson(jsonSchema) {
2322
2256
  const properties = {};
2323
2257
  properties["label"] = z5.z.string().describe(
2324
- 'A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The SPECIFIC finding, result, or insight discovered (e.g., "Slack bot needs workspace admin role", "Found ingestion requires 3 steps", "Channel history limited to 10k messages"). State the ACTUAL information found, not that you searched. What did you LEARN or DISCOVER? What specific detail is now known?'
2258
+ 'A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The SPECIFIC finding, result, or insight discovered (e.g., "Slack bot needs workspace admin role", "Found ingestion requires 3 steps", "Channel history limited to 10k messages"). State the ACTUAL information found, not that you searched. What did you LEARN or DISCOVER? What specific detail is now known? CRITICAL: Only use facts explicitly found in the activities - NEVER invent names, people, organizations, or details that are not present in the actual tool results.'
2325
2259
  );
2326
2260
  for (const [key, value] of Object.entries(jsonSchema.properties)) {
2327
2261
  let zodType;
@@ -2398,41 +2332,12 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2398
2332
  );
2399
2333
  break;
2400
2334
  }
2401
- case "transfer": {
2402
- const data = event.data;
2403
- activities.push(
2404
- `\u{1F504} **Continuing**: ${data.reason || "Processing request"}
2405
- ${data.context ? `Context: ${JSON.stringify(data.context, null, 2)}` : ""}`
2406
- );
2335
+ // INTERNAL OPERATIONS - DO NOT EXPOSE TO STATUS UPDATES
2336
+ case "transfer":
2337
+ case "delegation_sent":
2338
+ case "delegation_returned":
2339
+ case "artifact_saved":
2407
2340
  break;
2408
- }
2409
- case "delegation_sent": {
2410
- const data = event.data;
2411
- activities.push(
2412
- `\u{1F4E4} **Processing**: ${data.taskDescription}
2413
- ${data.context ? `Context: ${JSON.stringify(data.context, null, 2)}` : ""}`
2414
- );
2415
- break;
2416
- }
2417
- case "delegation_returned": {
2418
- const data = event.data;
2419
- activities.push(
2420
- `\u{1F4E5} **Completed subtask**
2421
- Result: ${JSON.stringify(data.result, null, 2)}`
2422
- );
2423
- break;
2424
- }
2425
- case "artifact_saved": {
2426
- const data = event.data;
2427
- activities.push(
2428
- `\u{1F4BE} **Artifact Saved**: ${data.artifactType}
2429
- ID: ${data.artifactId}
2430
- Task: ${data.taskId}
2431
- ${data.summaryData ? `Summary: ${data.summaryData}` : ""}
2432
- ${data.fullData ? `Full Data: ${data.fullData}` : ""}`
2433
- );
2434
- break;
2435
- }
2436
2341
  case "agent_reasoning": {
2437
2342
  const data = event.data;
2438
2343
  activities.push(
@@ -2457,21 +2362,6 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2457
2362
  }
2458
2363
  return activities;
2459
2364
  }
2460
- /**
2461
- * Generate fallback summary when LLM fails
2462
- */
2463
- generateFallbackSummary(events, elapsedTime) {
2464
- const timeStr = Math.round(elapsedTime / 1e3);
2465
- const toolCalls = events.filter((e) => e.eventType === "tool_execution").length;
2466
- const artifacts = events.filter((e) => e.eventType === "artifact_saved").length;
2467
- if (artifacts > 0) {
2468
- return `Generated ${artifacts} result${artifacts > 1 ? "s" : ""} so far (${timeStr}s elapsed)`;
2469
- } else if (toolCalls > 0) {
2470
- return `Used ${toolCalls} tool${toolCalls > 1 ? "s" : ""} to gather information (${timeStr}s elapsed)`;
2471
- } else {
2472
- return `Processing your request... (${timeStr}s elapsed)`;
2473
- }
2474
- }
2475
2365
  /**
2476
2366
  * Process a single artifact to generate name and description using conversation context
2477
2367
  */
@@ -3225,7 +3115,7 @@ var _IncrementalStreamParser = class _IncrementalStreamParser {
3225
3115
  await this.streamPart(part);
3226
3116
  }
3227
3117
  if (this.pendingTextBuffer) {
3228
- const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
3118
+ const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/artifact:ref>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
3229
3119
  if (cleanedText) {
3230
3120
  this.collectedParts.push({
3231
3121
  kind: "text",
@@ -3303,7 +3193,7 @@ var _IncrementalStreamParser = class _IncrementalStreamParser {
3303
3193
  if (part.kind === "text" && part.text) {
3304
3194
  this.pendingTextBuffer += part.text;
3305
3195
  if (!this.artifactParser.hasIncompleteArtifact(this.pendingTextBuffer)) {
3306
- const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
3196
+ const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/artifact:ref>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
3307
3197
  if (cleanedText) {
3308
3198
  await this.streamHelper.streamText(cleanedText, 50);
3309
3199
  }
@@ -3311,7 +3201,7 @@ var _IncrementalStreamParser = class _IncrementalStreamParser {
3311
3201
  }
3312
3202
  } else if (part.kind === "data" && part.data) {
3313
3203
  if (this.pendingTextBuffer) {
3314
- const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
3204
+ const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/artifact:ref>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
3315
3205
  if (cleanedText) {
3316
3206
  await this.streamHelper.streamText(cleanedText, 50);
3317
3207
  }
@@ -7298,7 +7188,7 @@ var _VercelDataStreamHelper = class _VercelDataStreamHelper {
7298
7188
  __publicField(this, "queuedEvents", []);
7299
7189
  // Timing tracking for text sequences (text-end to text-start gap)
7300
7190
  __publicField(this, "lastTextEndTimestamp", 0);
7301
- __publicField(this, "TEXT_GAP_THRESHOLD", 50);
7191
+ __publicField(this, "TEXT_GAP_THRESHOLD", 2e3);
7302
7192
  // milliseconds - if gap between text sequences is less than this, queue operations
7303
7193
  // Connection management and forced cleanup
7304
7194
  __publicField(this, "connectionDropTimer");
package/dist/index.js CHANGED
@@ -23,10 +23,13 @@ import { streamSSE, stream } from 'hono/streaming';
23
23
  import { nanoid } from 'nanoid';
24
24
  import destr from 'destr';
25
25
  import traverse from 'traverse';
26
- import { createUIMessageStream, JsonToSseTransformStream, parsePartialJson, generateText, generateObject, tool, streamText, streamObject } from 'ai';
26
+ import { createUIMessageStream, JsonToSseTransformStream, parsePartialJson, generateObject, tool, streamText, generateText, streamObject } from 'ai';
27
+ import { defaultStatusSchemas } from '@inkeep/agents-sdk';
27
28
  import { createAnthropic, anthropic } from '@ai-sdk/anthropic';
29
+ import { createGateway, gateway } from '@ai-sdk/gateway';
28
30
  import { createGoogleGenerativeAI, google } from '@ai-sdk/google';
29
31
  import { createOpenAI, openai } from '@ai-sdk/openai';
32
+ import { createOpenRouter, openrouter } from '@openrouter/ai-sdk-provider';
30
33
  import jmespath from 'jmespath';
31
34
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
32
35
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
@@ -1140,6 +1143,18 @@ var _ModelFactory = class _ModelFactory {
1140
1143
  return createOpenAI(config);
1141
1144
  case "google":
1142
1145
  return createGoogleGenerativeAI(config);
1146
+ case "openrouter":
1147
+ return {
1148
+ ...createOpenRouter(config),
1149
+ textEmbeddingModel: () => {
1150
+ throw new Error("OpenRouter does not support text embeddings");
1151
+ },
1152
+ imageModel: () => {
1153
+ throw new Error("OpenRouter does not support image generation");
1154
+ }
1155
+ };
1156
+ case "gateway":
1157
+ return createGateway(config);
1143
1158
  default:
1144
1159
  throw new Error(`Unsupported provider: ${provider}`);
1145
1160
  }
@@ -1199,26 +1214,29 @@ var _ModelFactory = class _ModelFactory {
1199
1214
  return openai(modelName);
1200
1215
  case "google":
1201
1216
  return google(modelName);
1217
+ case "openrouter":
1218
+ return openrouter(modelName);
1219
+ case "gateway":
1220
+ return gateway(modelName);
1202
1221
  default:
1203
- throw new Error(`Unsupported provider: ${provider}`);
1222
+ throw new Error(
1223
+ `Unsupported provider: ${provider}. Supported providers are: ${_ModelFactory.BUILT_IN_PROVIDERS.join(", ")}. To access other models, use OpenRouter (openrouter/model-id) or Vercel AI Gateway (gateway/model-id).`
1224
+ );
1204
1225
  }
1205
1226
  }
1206
1227
  /**
1207
1228
  * Parse model string to extract provider and model name
1208
1229
  * Examples: "anthropic/claude-sonnet-4" -> { provider: "anthropic", modelName: "claude-sonnet-4" }
1230
+ * "openrouter/anthropic/claude-sonnet-4" -> { provider: "openrouter", modelName: "anthropic/claude-sonnet-4" }
1209
1231
  * "claude-sonnet-4" -> { provider: "anthropic", modelName: "claude-sonnet-4" } (default to anthropic)
1210
1232
  */
1211
1233
  static parseModelString(modelString) {
1212
1234
  if (modelString.includes("/")) {
1213
1235
  const [provider, ...modelParts] = modelString.split("/");
1214
1236
  const normalizedProvider = provider.toLowerCase();
1215
- if (!_ModelFactory.SUPPORTED_PROVIDERS.includes(normalizedProvider)) {
1216
- logger5.error(
1217
- { provider: normalizedProvider, modelName: modelParts.join("/") },
1218
- "Unsupported provider detected, falling back to anthropic"
1219
- );
1237
+ if (!_ModelFactory.BUILT_IN_PROVIDERS.includes(normalizedProvider)) {
1220
1238
  throw new Error(
1221
- `Unsupported provider: ${normalizedProvider}. Please provide a model in the format of provider/model-name.`
1239
+ `Unsupported provider: ${normalizedProvider}. Supported providers are: ${_ModelFactory.BUILT_IN_PROVIDERS.join(", ")}. To access other models, use OpenRouter (openrouter/model-id) or Vercel AI Gateway (gateway/model-id).`
1222
1240
  );
1223
1241
  }
1224
1242
  return {
@@ -1227,9 +1245,7 @@ var _ModelFactory = class _ModelFactory {
1227
1245
  // In case model name has slashes
1228
1246
  };
1229
1247
  }
1230
- throw new Error(
1231
- `Invalid model provided: ${modelString}. Please provide a model in the format of provider/model-name.`
1232
- );
1248
+ throw new Error(`No provider specified in model string: ${modelString}`);
1233
1249
  }
1234
1250
  /**
1235
1251
  * Get generation parameters from provider options
@@ -1293,9 +1309,15 @@ var _ModelFactory = class _ModelFactory {
1293
1309
  }
1294
1310
  };
1295
1311
  /**
1296
- * Supported providers for security validation
1312
+ * Built-in providers that have special handling
1297
1313
  */
1298
- __publicField(_ModelFactory, "SUPPORTED_PROVIDERS", ["anthropic", "openai", "google"]);
1314
+ __publicField(_ModelFactory, "BUILT_IN_PROVIDERS", [
1315
+ "anthropic",
1316
+ "openai",
1317
+ "google",
1318
+ "openrouter",
1319
+ "gateway"
1320
+ ]);
1299
1321
  var ModelFactory = _ModelFactory;
1300
1322
 
1301
1323
  // src/utils/stream-registry.ts
@@ -1633,68 +1655,52 @@ var GraphSession = class {
1633
1655
  }
1634
1656
  const now = Date.now();
1635
1657
  const elapsedTime = now - statusUpdateState.startTime;
1636
- let summaryToSend;
1637
- if (statusUpdateState.config.statusComponents && statusUpdateState.config.statusComponents.length > 0) {
1638
- const result = await this.generateStructuredStatusUpdate(
1639
- this.events.slice(statusUpdateState.lastEventCount),
1640
- elapsedTime,
1641
- statusUpdateState.config.statusComponents,
1642
- statusUpdateState.summarizerModel,
1643
- this.previousSummaries
1644
- );
1645
- if (result.summaries && result.summaries.length > 0) {
1646
- for (const summary of result.summaries) {
1647
- if (!summary || !summary.type || !summary.data || !summary.data.label || Object.keys(summary.data).length === 0) {
1648
- logger6.warn(
1649
- {
1650
- sessionId: this.sessionId,
1651
- summary
1652
- },
1653
- "Skipping empty or invalid structured operation"
1654
- );
1655
- continue;
1656
- }
1657
- const summaryToSend2 = {
1658
- type: summary.data.type || summary.type,
1659
- // Preserve the actual custom type from LLM
1660
- label: summary.data.label,
1661
- details: Object.fromEntries(
1662
- Object.entries(summary.data).filter(([key]) => !["label", "type"].includes(key))
1663
- )
1664
- };
1665
- await streamHelper.writeSummary(summaryToSend2);
1666
- }
1667
- const summaryTexts = result.summaries.map(
1668
- (summary) => JSON.stringify({ type: summary.type, data: summary.data })
1669
- );
1670
- this.previousSummaries.push(...summaryTexts);
1671
- if (this.statusUpdateState) {
1672
- this.statusUpdateState.lastUpdateTime = now;
1673
- this.statusUpdateState.lastEventCount = this.events.length;
1658
+ const statusComponents = statusUpdateState.config.statusComponents && statusUpdateState.config.statusComponents.length > 0 ? statusUpdateState.config.statusComponents : defaultStatusSchemas;
1659
+ const result = await this.generateStructuredStatusUpdate(
1660
+ this.events.slice(statusUpdateState.lastEventCount),
1661
+ elapsedTime,
1662
+ statusComponents,
1663
+ statusUpdateState.summarizerModel,
1664
+ this.previousSummaries
1665
+ );
1666
+ if (result.summaries && result.summaries.length > 0) {
1667
+ for (const summary of result.summaries) {
1668
+ if (!summary || !summary.type || !summary.data || !summary.data.label || Object.keys(summary.data).length === 0) {
1669
+ logger6.warn(
1670
+ {
1671
+ sessionId: this.sessionId,
1672
+ summary
1673
+ },
1674
+ "Skipping empty or invalid structured operation"
1675
+ );
1676
+ continue;
1674
1677
  }
1675
- return;
1678
+ const summaryToSend = {
1679
+ type: summary.data.type || summary.type,
1680
+ // Preserve the actual custom type from LLM
1681
+ label: summary.data.label,
1682
+ details: Object.fromEntries(
1683
+ Object.entries(summary.data).filter(([key]) => !["label", "type"].includes(key))
1684
+ )
1685
+ };
1686
+ await streamHelper.writeSummary(summaryToSend);
1676
1687
  }
1677
- } else {
1678
- const summary = await this.generateProgressSummary(
1679
- this.events.slice(statusUpdateState.lastEventCount),
1680
- elapsedTime,
1681
- statusUpdateState.summarizerModel,
1682
- this.previousSummaries
1688
+ const summaryTexts = result.summaries.map(
1689
+ (summary) => JSON.stringify({ type: summary.type, data: summary.data })
1683
1690
  );
1684
- this.previousSummaries.push(summary);
1691
+ this.previousSummaries.push(...summaryTexts);
1692
+ if (this.statusUpdateState) {
1693
+ this.statusUpdateState.lastUpdateTime = now;
1694
+ this.statusUpdateState.lastEventCount = this.events.length;
1695
+ }
1696
+ return;
1685
1697
  }
1686
1698
  if (this.previousSummaries.length > 3) {
1687
1699
  this.previousSummaries.shift();
1688
1700
  }
1689
- {
1690
- logger6.warn(
1691
- {
1692
- sessionId: this.sessionId,
1693
- summaryToSend
1694
- },
1695
- "Skipping empty or invalid status update operation"
1696
- );
1697
- return;
1701
+ if (this.statusUpdateState) {
1702
+ this.statusUpdateState.lastUpdateTime = now;
1703
+ this.statusUpdateState.lastEventCount = this.events.length;
1698
1704
  }
1699
1705
  } catch (error) {
1700
1706
  logger6.error(
@@ -1775,106 +1781,6 @@ var GraphSession = class {
1775
1781
  this.statusUpdateState.updateLock = false;
1776
1782
  }
1777
1783
  }
1778
- /**
1779
- * Generate user-focused progress summary hiding internal operations
1780
- */
1781
- async generateProgressSummary(newEvents, elapsedTime, summarizerModel, previousSummaries = []) {
1782
- return tracer.startActiveSpan(
1783
- "graph_session.generate_progress_summary",
1784
- {
1785
- attributes: {
1786
- "graph_session.id": this.sessionId,
1787
- "events.count": newEvents.length,
1788
- "elapsed_time.seconds": Math.round(elapsedTime / 1e3),
1789
- "llm.model": summarizerModel?.model,
1790
- "previous_summaries.count": previousSummaries.length
1791
- }
1792
- },
1793
- async (span) => {
1794
- try {
1795
- const userVisibleActivities = this.extractUserVisibleActivities(newEvents);
1796
- let conversationContext = "";
1797
- if (this.tenantId && this.projectId) {
1798
- try {
1799
- const conversationHistory = await getFormattedConversationHistory({
1800
- tenantId: this.tenantId,
1801
- projectId: this.projectId,
1802
- conversationId: this.sessionId,
1803
- options: {
1804
- limit: 10,
1805
- // Get recent conversation context
1806
- maxOutputTokens: 2e3
1807
- },
1808
- filters: {}
1809
- });
1810
- conversationContext = conversationHistory.trim() ? `
1811
- User's Question/Context:
1812
- ${conversationHistory}
1813
- ` : "";
1814
- } catch (error) {
1815
- logger6.warn(
1816
- { sessionId: this.sessionId, error },
1817
- "Failed to fetch conversation history for status update"
1818
- );
1819
- }
1820
- }
1821
- const previousSummaryContext = previousSummaries.length > 0 ? `
1822
- Previous updates provided to user:
1823
- ${previousSummaries.map((s, i) => `${i + 1}. ${s}`).join("\n")}
1824
- ` : "";
1825
- const basePrompt = `Generate a meaningful status update that tells the user what specific information or result was just found/achieved.${conversationContext}${previousSummaries.length > 0 ? `
1826
- ${previousSummaryContext}` : ""}
1827
-
1828
- Activities:
1829
- ${userVisibleActivities.join("\n") || "No New Activities"}
1830
-
1831
- Create a short 3-5 word label describing the ACTUAL finding. Use sentence case (only capitalize the first word and proper nouns). Examples: "Found admin permissions needed", "Identified three channel types", "OAuth token required".
1832
-
1833
- ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
1834
- const prompt = basePrompt;
1835
- let modelToUse = summarizerModel;
1836
- if (!summarizerModel?.model?.trim()) {
1837
- if (!this.statusUpdateState?.baseModel?.model?.trim()) {
1838
- throw new Error(
1839
- "Either summarizer or base model is required for progress summary generation. Please configure models at the project level."
1840
- );
1841
- }
1842
- modelToUse = this.statusUpdateState.baseModel;
1843
- }
1844
- if (!modelToUse) {
1845
- throw new Error("No model configuration available");
1846
- }
1847
- const model = ModelFactory.createModel(modelToUse);
1848
- const { text } = await generateText({
1849
- model,
1850
- prompt,
1851
- experimental_telemetry: {
1852
- isEnabled: true,
1853
- functionId: `status_update_${this.sessionId}`,
1854
- recordInputs: true,
1855
- recordOutputs: true,
1856
- metadata: {
1857
- operation: "progress_summary_generation",
1858
- sessionId: this.sessionId
1859
- }
1860
- }
1861
- });
1862
- span.setAttributes({
1863
- "summary.length": text.trim().length,
1864
- "user_activities.count": userVisibleActivities.length
1865
- });
1866
- span.setStatus({ code: SpanStatusCode.OK });
1867
- return text.trim();
1868
- } catch (error) {
1869
- setSpanWithError(span, error);
1870
- logger6.error({ error }, "Failed to generate summary, using fallback");
1871
- return this.generateFallbackSummary(newEvents, elapsedTime);
1872
- } finally {
1873
- span.end();
1874
- }
1875
- }
1876
- );
1877
- }
1878
1784
  /**
1879
1785
  * Generate structured status update using configured data components
1880
1786
  */
@@ -1953,17 +1859,45 @@ Rules:
1953
1859
  - Fill in data for relevant components only
1954
1860
  - Use 'no_relevant_updates' if nothing substantially new to report. DO NOT WRITE LABELS OR USE OTHER COMPONENTS IF YOU USE THIS COMPONENT.
1955
1861
  - Never repeat previous values, make every update EXTREMELY unique. If you cannot do that the update is not worth mentioning.
1956
- - Labels MUST be short 3-5 word phrases with ACTUAL information discovered. NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION.
1862
+ - Labels MUST be short 3-7 word phrases with ACTUAL information discovered. NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION.
1957
1863
  - 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.
1958
1864
  - DO NOT use action words like "Searching", "Processing", "Analyzing" - state what was FOUND
1959
1865
  - Include specific details, numbers, requirements, or insights discovered
1960
1866
  - Examples: "Admin permissions required", "Three OAuth steps found", "Token expires daily"
1961
- - You are ONE unified AI system - NEVER mention agents, transfers, delegations, or routing
1962
- - CRITICAL: NEVER use the words "transfer", "delegation", "agent", "routing", "artifact", or any internal system terminology in labels or any names of agents, tools, or systems.
1963
- - Present all operations as seamless actions by a single system
1964
- - Anonymize all internal operations so that the information appears descriptive and USER FRIENDLY. HIDE ALL INTERNAL OPERATIONS!
1965
- - Bad examples: "Transferring to search agent", "continuing transfer to qa agent", "Delegating task", "Routing request", "Processing request", "Artifact found", "Artifact saved", or not using the no_relevant_updates
1966
- - Good examples: "Slack bot needs admin privileges", "Found 3-step OAuth flow required", "Channel limit is 500 per workspace", or use the no_relevant_updates component if nothing new to report.
1867
+
1868
+ CRITICAL - HIDE ALL INTERNAL SYSTEM OPERATIONS:
1869
+ - You are ONE unified AI system presenting results to the user
1870
+ - ABSOLUTELY FORBIDDEN WORDS/PHRASES: "transfer", "transferring", "delegation", "delegating", "delegate", "agent", "routing", "route", "artifact", "saving artifact", "stored artifact", "artifact saved", "continuing", "passing to", "handing off", "switching to"
1871
+ - NEVER reveal internal architecture: No mentions of different agents, components, systems, or modules working together
1872
+ - NEVER mention artifact operations: Users don't need to know about data being saved, stored, or organized internally
1873
+ - NEVER describe handoffs or transitions: Present everything as one seamless operation
1874
+ - If you see "transfer", "delegation_sent", "delegation_returned", or "artifact_saved" events - IGNORE THEM or translate to user-facing information only
1875
+ - Focus ONLY on actual discoveries, findings, and results that matter to the user
1876
+
1877
+ - Bad examples:
1878
+ * "Transferring to search agent"
1879
+ * "Delegating research task"
1880
+ * "Routing to QA specialist"
1881
+ * "Artifact saved successfully"
1882
+ * "Storing results for later"
1883
+ * "Passing request to tool handler"
1884
+ * "Continuing with analysis"
1885
+ * "Handing off to processor"
1886
+ - Good examples:
1887
+ * "Slack bot needs admin privileges"
1888
+ * "Found 3-step OAuth flow required"
1889
+ * "Channel limit is 500 per workspace"
1890
+ * Use no_relevant_updates if nothing new to report
1891
+
1892
+ CRITICAL ANTI-HALLUCINATION RULES:
1893
+ - NEVER MAKE UP SOMETHING WITHOUT BACKING IT UP WITH ACTUAL INFORMATION. EVERY SINGLE UPDATE MUST BE BACKED UP WITH ACTUAL INFORMATION.
1894
+ - DO NOT MAKE UP PEOPLE, NAMES, PLACES, THINGS, ORGANIZATIONS, OR INFORMATION. IT IS OBVIOUS WHEN A PERSON/ENTITY DOES NOT EXIST.
1895
+ - Only report facts that are EXPLICITLY mentioned in the activities or tool results
1896
+ - If you don't have concrete information about something, DO NOT mention it
1897
+ - Never invent names like "John Doe", "Alice", "Bob", or any other placeholder names
1898
+ - Never create fictional companies, products, or services
1899
+ - If a tool returned no results or an error, DO NOT pretend it found something
1900
+ - Every detail in your status update must be traceable back to the actual activities provided
1967
1901
 
1968
1902
  REMEMBER YOU CAN ONLY USE 'no_relevant_updates' ALONE! IT CANNOT BE CONCATENATED WITH OTHER STATUS UPDATES!
1969
1903
 
@@ -2036,7 +1970,7 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2036
1970
  }
2037
1971
  return z.object({
2038
1972
  label: z.string().describe(
2039
- 'A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The ACTUAL finding or result, not the action. What specific information was discovered? (e.g., "Slack requires OAuth 2.0 setup", "Found 5 integration methods", "API rate limit is 100/minute"). Include the actual detail or insight, not just that you searched or processed.'
1973
+ 'A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The ACTUAL finding or result, not the action. What specific information was discovered? (e.g., "Slack requires OAuth 2.0 setup", "Found 5 integration methods", "API rate limit is 100/minute"). Include the actual detail or insight, not just that you searched or processed. CRITICAL: Only use facts explicitly found in the activities - NEVER invent names, people, organizations, or details that are not present in the actual tool results.'
2040
1974
  )
2041
1975
  });
2042
1976
  }
@@ -2046,7 +1980,7 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2046
1980
  buildZodSchemaFromJson(jsonSchema) {
2047
1981
  const properties = {};
2048
1982
  properties["label"] = z.string().describe(
2049
- 'A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The SPECIFIC finding, result, or insight discovered (e.g., "Slack bot needs workspace admin role", "Found ingestion requires 3 steps", "Channel history limited to 10k messages"). State the ACTUAL information found, not that you searched. What did you LEARN or DISCOVER? What specific detail is now known?'
1983
+ 'A short 3-5 word phrase, that is a descriptive label for the update component. This Label must be EXTREMELY unique to represent the UNIQUE update we are providing. The SPECIFIC finding, result, or insight discovered (e.g., "Slack bot needs workspace admin role", "Found ingestion requires 3 steps", "Channel history limited to 10k messages"). State the ACTUAL information found, not that you searched. What did you LEARN or DISCOVER? What specific detail is now known? CRITICAL: Only use facts explicitly found in the activities - NEVER invent names, people, organizations, or details that are not present in the actual tool results.'
2050
1984
  );
2051
1985
  for (const [key, value] of Object.entries(jsonSchema.properties)) {
2052
1986
  let zodType;
@@ -2123,41 +2057,12 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2123
2057
  );
2124
2058
  break;
2125
2059
  }
2126
- case "transfer": {
2127
- const data = event.data;
2128
- activities.push(
2129
- `\u{1F504} **Continuing**: ${data.reason || "Processing request"}
2130
- ${data.context ? `Context: ${JSON.stringify(data.context, null, 2)}` : ""}`
2131
- );
2060
+ // INTERNAL OPERATIONS - DO NOT EXPOSE TO STATUS UPDATES
2061
+ case "transfer":
2062
+ case "delegation_sent":
2063
+ case "delegation_returned":
2064
+ case "artifact_saved":
2132
2065
  break;
2133
- }
2134
- case "delegation_sent": {
2135
- const data = event.data;
2136
- activities.push(
2137
- `\u{1F4E4} **Processing**: ${data.taskDescription}
2138
- ${data.context ? `Context: ${JSON.stringify(data.context, null, 2)}` : ""}`
2139
- );
2140
- break;
2141
- }
2142
- case "delegation_returned": {
2143
- const data = event.data;
2144
- activities.push(
2145
- `\u{1F4E5} **Completed subtask**
2146
- Result: ${JSON.stringify(data.result, null, 2)}`
2147
- );
2148
- break;
2149
- }
2150
- case "artifact_saved": {
2151
- const data = event.data;
2152
- activities.push(
2153
- `\u{1F4BE} **Artifact Saved**: ${data.artifactType}
2154
- ID: ${data.artifactId}
2155
- Task: ${data.taskId}
2156
- ${data.summaryData ? `Summary: ${data.summaryData}` : ""}
2157
- ${data.fullData ? `Full Data: ${data.fullData}` : ""}`
2158
- );
2159
- break;
2160
- }
2161
2066
  case "agent_reasoning": {
2162
2067
  const data = event.data;
2163
2068
  activities.push(
@@ -2182,21 +2087,6 @@ ${this.statusUpdateState?.config.prompt?.trim() || ""}`;
2182
2087
  }
2183
2088
  return activities;
2184
2089
  }
2185
- /**
2186
- * Generate fallback summary when LLM fails
2187
- */
2188
- generateFallbackSummary(events, elapsedTime) {
2189
- const timeStr = Math.round(elapsedTime / 1e3);
2190
- const toolCalls = events.filter((e) => e.eventType === "tool_execution").length;
2191
- const artifacts = events.filter((e) => e.eventType === "artifact_saved").length;
2192
- if (artifacts > 0) {
2193
- return `Generated ${artifacts} result${artifacts > 1 ? "s" : ""} so far (${timeStr}s elapsed)`;
2194
- } else if (toolCalls > 0) {
2195
- return `Used ${toolCalls} tool${toolCalls > 1 ? "s" : ""} to gather information (${timeStr}s elapsed)`;
2196
- } else {
2197
- return `Processing your request... (${timeStr}s elapsed)`;
2198
- }
2199
- }
2200
2090
  /**
2201
2091
  * Process a single artifact to generate name and description using conversation context
2202
2092
  */
@@ -2947,7 +2837,7 @@ var _IncrementalStreamParser = class _IncrementalStreamParser {
2947
2837
  await this.streamPart(part);
2948
2838
  }
2949
2839
  if (this.pendingTextBuffer) {
2950
- const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
2840
+ const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/artifact:ref>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
2951
2841
  if (cleanedText) {
2952
2842
  this.collectedParts.push({
2953
2843
  kind: "text",
@@ -3025,7 +2915,7 @@ var _IncrementalStreamParser = class _IncrementalStreamParser {
3025
2915
  if (part.kind === "text" && part.text) {
3026
2916
  this.pendingTextBuffer += part.text;
3027
2917
  if (!this.artifactParser.hasIncompleteArtifact(this.pendingTextBuffer)) {
3028
- const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
2918
+ const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/artifact:ref>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
3029
2919
  if (cleanedText) {
3030
2920
  await this.streamHelper.streamText(cleanedText, 50);
3031
2921
  }
@@ -3033,7 +2923,7 @@ var _IncrementalStreamParser = class _IncrementalStreamParser {
3033
2923
  }
3034
2924
  } else if (part.kind === "data" && part.data) {
3035
2925
  if (this.pendingTextBuffer) {
3036
- const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
2926
+ const cleanedText = this.pendingTextBuffer.replace(/<\/?artifact:ref(?:\s[^>]*)?>\/?>/g, "").replace(/<\/?artifact(?:\s[^>]*)?>\/?>/g, "").replace(/<\/artifact:ref>/g, "").replace(/<\/(?:\w+:)?artifact>/g, "");
3037
2927
  if (cleanedText) {
3038
2928
  await this.streamHelper.streamText(cleanedText, 50);
3039
2929
  }
@@ -7007,7 +6897,7 @@ var _VercelDataStreamHelper = class _VercelDataStreamHelper {
7007
6897
  __publicField(this, "queuedEvents", []);
7008
6898
  // Timing tracking for text sequences (text-end to text-start gap)
7009
6899
  __publicField(this, "lastTextEndTimestamp", 0);
7010
- __publicField(this, "TEXT_GAP_THRESHOLD", 50);
6900
+ __publicField(this, "TEXT_GAP_THRESHOLD", 2e3);
7011
6901
  // milliseconds - if gap between text sequences is less than this, queue operations
7012
6902
  // Connection management and forced cleanup
7013
6903
  __publicField(this, "connectionDropTimer");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-run-api",
3
- "version": "0.0.0-dev-20250920001717",
3
+ "version": "0.0.0-dev-20250924191551",
4
4
  "description": "Agents Run API for Inkeep Agent Framework - handles chat, agent execution, and streaming",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,6 +11,7 @@
11
11
  "license": "SEE LICENSE IN LICENSE.md",
12
12
  "dependencies": {
13
13
  "@ai-sdk/anthropic": "2.0.2",
14
+ "@ai-sdk/gateway": "^1.0.23",
14
15
  "@ai-sdk/google": "^2.0.14",
15
16
  "@ai-sdk/openai": "2.0.11",
16
17
  "@ai-sdk/react": "2.0.11",
@@ -19,6 +20,7 @@
19
20
  "@hono/swagger-ui": "^0.5.1",
20
21
  "@hono/zod-openapi": "^1.0.2",
21
22
  "@modelcontextprotocol/sdk": "^1.17.2",
23
+ "@openrouter/ai-sdk-provider": "^1.2.0",
22
24
  "@opentelemetry/api": "^1.9.0",
23
25
  "@opentelemetry/auto-instrumentations-node": "^0.64.1",
24
26
  "@opentelemetry/baggage-span-processor": "^0.4.0",
@@ -45,7 +47,8 @@
45
47
  "traverse": "^0.6.11",
46
48
  "ts-pattern": "^5.7.1",
47
49
  "zod": "^4.1.5",
48
- "@inkeep/agents-core": "^0.0.0-dev-20250920001717"
50
+ "@inkeep/agents-core": "^0.0.0-dev-20250924191551",
51
+ "@inkeep/agents-sdk": "^0.0.0-dev-20250924191551"
49
52
  },
50
53
  "devDependencies": {
51
54
  "@hono/vite-dev-server": "^0.20.1",