@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.
- package/dist/index.cjs +119 -229
- package/dist/index.js +120 -230
- 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(
|
|
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.
|
|
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}.
|
|
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
|
-
*
|
|
1583
|
+
* Built-in providers that have special handling
|
|
1568
1584
|
*/
|
|
1569
|
-
__publicField(_ModelFactory, "
|
|
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
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1953
|
-
|
|
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(
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
2237
|
-
|
|
2238
|
-
-
|
|
2239
|
-
-
|
|
2240
|
-
-
|
|
2241
|
-
-
|
|
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
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
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",
|
|
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,
|
|
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(
|
|
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.
|
|
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}.
|
|
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
|
-
*
|
|
1312
|
+
* Built-in providers that have special handling
|
|
1297
1313
|
*/
|
|
1298
|
-
__publicField(_ModelFactory, "
|
|
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
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1678
|
-
|
|
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(
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
1962
|
-
|
|
1963
|
-
-
|
|
1964
|
-
-
|
|
1965
|
-
-
|
|
1966
|
-
-
|
|
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
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
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",
|
|
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-
|
|
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-
|
|
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",
|