@inkeep/agents-run-api 0.0.0-dev-20260105192809 → 0.0.0-dev-20260105201718

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.
@@ -2,18 +2,21 @@ import { getLogger } from "../logger.js";
2
2
  import dbClient_default from "../data/db/dbClient.js";
3
3
  import { AGENT_EXECUTION_MAX_GENERATION_STEPS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_VCPUS_DEFAULT, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS, LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS } from "../constants/execution-limits/index.js";
4
4
  import { toolSessionManager } from "./ToolSessionManager.js";
5
- import { createDefaultConversationHistoryConfig, getFormattedConversationHistory } from "../data/conversations.js";
6
- import { getStreamHelper } from "../utils/stream-registry.js";
5
+ import { getCompressionConfigForModel } from "../utils/model-context-utils.js";
7
6
  import { setSpanWithError as setSpanWithError$1, tracer } from "../utils/tracer.js";
7
+ import { getModelAwareCompressionConfig } from "../services/BaseCompressor.js";
8
+ import "../services/ConversationCompressor.js";
9
+ import { createDefaultConversationHistoryConfig, getConversationHistoryWithCompression } from "../data/conversations.js";
10
+ import { getStreamHelper } from "../utils/stream-registry.js";
8
11
  import { agentSessionManager } from "../services/AgentSession.js";
9
12
  import { IncrementalStreamParser } from "../services/IncrementalStreamParser.js";
10
- import { MidGenerationCompressor, getCompressionConfigFromEnv } from "../services/MidGenerationCompressor.js";
13
+ import { MidGenerationCompressor } from "../services/MidGenerationCompressor.js";
11
14
  import { pendingToolApprovalManager } from "../services/PendingToolApprovalManager.js";
12
15
  import { ResponseFormatter } from "../services/ResponseFormatter.js";
13
16
  import { generateToolId } from "../utils/agent-operations.js";
14
17
  import { jsonSchemaToZod } from "../utils/data-component-schema.js";
15
18
  import { ArtifactCreateSchema, ArtifactReferenceSchema } from "../utils/artifact-component-schema.js";
16
- import { getCompressionConfigForModel } from "../utils/model-context-utils.js";
19
+ import { withJsonPostProcessing } from "../utils/json-postprocessor.js";
17
20
  import { calculateBreakdownTotal, estimateTokens } from "../utils/token-estimator.js";
18
21
  import { createDelegateToAgentTool, createTransferToAgentTool } from "./relationTools.js";
19
22
  import { SystemPromptBuilder } from "./SystemPromptBuilder.js";
@@ -183,21 +186,6 @@ var Agent = class {
183
186
  /**
184
187
  * Simple compression fallback: drop oldest messages to fit under token limit
185
188
  */
186
- simpleCompression(messages, targetTokens) {
187
- if (messages.length === 0) return messages;
188
- const estimateTokens$1 = (msg) => {
189
- const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
190
- return Math.ceil(content.length / 4);
191
- };
192
- let totalTokens = messages.reduce((sum, msg) => sum + estimateTokens$1(msg), 0);
193
- if (totalTokens <= targetTokens) return messages;
194
- const result = [...messages];
195
- while (totalTokens > targetTokens && result.length > 1) {
196
- const dropped = result.shift();
197
- if (dropped) totalTokens -= estimateTokens$1(dropped);
198
- }
199
- return result;
200
- }
201
189
  /**
202
190
  * Set delegation status for this agent instance
203
191
  */
@@ -931,7 +919,7 @@ var Agent = class {
931
919
  */
932
920
  async buildPhase2SystemPrompt(runtimeContext) {
933
921
  const phase2Config = new Phase2Config();
934
- const compressionConfig = getCompressionConfigFromEnv();
922
+ const compressionConfig = getModelAwareCompressionConfig();
935
923
  const hasAgentArtifactComponents = await this.hasAgentArtifactComponents() || compressionConfig.enabled;
936
924
  const conversationId = runtimeContext?.metadata?.conversationId || runtimeContext?.contextId;
937
925
  const resolvedContext = conversationId ? await this.getResolvedContext(conversationId) : null;
@@ -1036,7 +1024,7 @@ var Agent = class {
1036
1024
  }, "Failed to process agent prompt with context, using original");
1037
1025
  }
1038
1026
  const shouldIncludeArtifactComponents = !excludeDataComponents;
1039
- const compressionConfig = getCompressionConfigFromEnv();
1027
+ const compressionConfig = getModelAwareCompressionConfig();
1040
1028
  const hasAgentArtifactComponents = await this.hasAgentArtifactComponents() || compressionConfig.enabled;
1041
1029
  const config = {
1042
1030
  corePrompt: processedPrompt,
@@ -1091,7 +1079,7 @@ var Agent = class {
1091
1079
  }
1092
1080
  async getDefaultTools(streamRequestId) {
1093
1081
  const defaultTools = {};
1094
- const compressionConfig = getCompressionConfigFromEnv();
1082
+ const compressionConfig = getModelAwareCompressionConfig();
1095
1083
  if (await this.agentHasArtifactComponents() || compressionConfig.enabled) defaultTools.get_reference_artifact = this.getArtifactTools();
1096
1084
  if (this.config.dataComponents && this.config.dataComponents.length > 0) {
1097
1085
  const thinkingCompleteTool = this.createThinkingCompleteTool();
@@ -1316,80 +1304,13 @@ ${typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, nu
1316
1304
  "subAgent.id": this.config.id,
1317
1305
  "subAgent.name": this.config.name
1318
1306
  } }, async (span) => {
1319
- const contextId = runtimeContext?.contextId || "default";
1320
- const taskId = runtimeContext?.metadata?.taskId || "unknown";
1321
- const streamRequestId = runtimeContext?.metadata?.streamRequestId;
1322
- const sessionId = streamRequestId || "fallback-session";
1307
+ const { contextId, taskId, streamRequestId, sessionId } = this.setupGenerationContext(runtimeContext);
1323
1308
  try {
1324
- this.streamRequestId = streamRequestId;
1325
- this.streamHelper = streamRequestId ? getStreamHelper(streamRequestId) : void 0;
1309
+ const { systemPrompt, thinkingSystemPrompt, sanitizedTools, contextBreakdown: initialContextBreakdown } = await this.loadToolsAndPrompts(sessionId, streamRequestId, runtimeContext);
1326
1310
  if (streamRequestId && this.artifactComponents.length > 0) agentSessionManager.updateArtifactComponents(streamRequestId, this.artifactComponents);
1327
1311
  const conversationId = runtimeContext?.metadata?.conversationId;
1328
1312
  if (conversationId) this.setConversationId(conversationId);
1329
- const [mcpTools, systemPromptResult, thinkingSystemPromptResult, functionTools, relationTools, defaultTools] = await tracer.startActiveSpan("agent.load_tools", { attributes: {
1330
- "subAgent.name": this.config.name,
1331
- "session.id": sessionId || "none"
1332
- } }, async (childSpan) => {
1333
- try {
1334
- const result = await Promise.all([
1335
- this.getMcpTools(sessionId, streamRequestId),
1336
- this.buildSystemPrompt(runtimeContext, false),
1337
- this.buildSystemPrompt(runtimeContext, true),
1338
- this.getFunctionTools(sessionId, streamRequestId),
1339
- Promise.resolve(this.getRelationTools(runtimeContext, sessionId)),
1340
- this.getDefaultTools(streamRequestId)
1341
- ]);
1342
- childSpan.setStatus({ code: SpanStatusCode.OK });
1343
- return result;
1344
- } catch (err) {
1345
- setSpanWithError$1(childSpan, err instanceof Error ? err : new Error(String(err)));
1346
- throw err;
1347
- } finally {
1348
- childSpan.end();
1349
- }
1350
- });
1351
- const systemPrompt = systemPromptResult.prompt;
1352
- const thinkingSystemPrompt = thinkingSystemPromptResult.prompt;
1353
- const contextBreakdown = systemPromptResult.breakdown;
1354
- const allTools = {
1355
- ...mcpTools,
1356
- ...functionTools,
1357
- ...relationTools,
1358
- ...defaultTools
1359
- };
1360
- const sanitizedTools = this.sanitizeToolsForAISDK(allTools);
1361
- let conversationHistory = "";
1362
- const historyConfig = this.config.conversationHistoryConfig ?? createDefaultConversationHistoryConfig();
1363
- if (historyConfig && historyConfig.mode !== "none") {
1364
- if (historyConfig.mode === "full") {
1365
- const filters = {
1366
- delegationId: this.delegationId,
1367
- isDelegated: this.isDelegatedAgent
1368
- };
1369
- conversationHistory = await getFormattedConversationHistory({
1370
- tenantId: this.config.tenantId,
1371
- projectId: this.config.projectId,
1372
- conversationId: contextId,
1373
- currentMessage: userMessage,
1374
- options: historyConfig,
1375
- filters
1376
- });
1377
- } else if (historyConfig.mode === "scoped") conversationHistory = await getFormattedConversationHistory({
1378
- tenantId: this.config.tenantId,
1379
- projectId: this.config.projectId,
1380
- conversationId: contextId,
1381
- currentMessage: userMessage,
1382
- options: historyConfig,
1383
- filters: {
1384
- subAgentId: this.config.id,
1385
- taskId,
1386
- delegationId: this.delegationId,
1387
- isDelegated: this.isDelegatedAgent
1388
- }
1389
- });
1390
- }
1391
- contextBreakdown.conversationHistory = estimateTokens(conversationHistory);
1392
- calculateBreakdownTotal(contextBreakdown);
1313
+ const { conversationHistory, contextBreakdown } = await this.buildConversationHistory(contextId, taskId, userMessage, streamRequestId, initialContextBreakdown);
1393
1314
  span.setAttributes({
1394
1315
  "context.breakdown.system_template_tokens": contextBreakdown.systemPromptTemplate,
1395
1316
  "context.breakdown.core_instructions_tokens": contextBreakdown.coreInstructions,
@@ -1404,289 +1325,24 @@ ${typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, nu
1404
1325
  "context.breakdown.conversation_history_tokens": contextBreakdown.conversationHistory,
1405
1326
  "context.breakdown.total_tokens": contextBreakdown.total
1406
1327
  });
1407
- const primaryModelSettings = this.getPrimaryModel();
1408
- const modelSettings = ModelFactory.prepareGenerationConfig(primaryModelSettings);
1328
+ const { primaryModelSettings, modelSettings, hasStructuredOutput, shouldStreamPhase1, timeoutMs } = this.configureModelSettings();
1409
1329
  let response;
1410
1330
  let textResponse;
1411
- const hasStructuredOutput = this.config.dataComponents && this.config.dataComponents.length > 0;
1412
- const shouldStreamPhase1 = this.getStreamingHelper() && !hasStructuredOutput;
1413
- const configuredTimeout = modelSettings.maxDuration ? Math.min(modelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : shouldStreamPhase1 ? LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING : LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING;
1414
- const timeoutMs = Math.min(configuredTimeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
1415
- if (modelSettings.maxDuration && modelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
1416
- requestedTimeout: modelSettings.maxDuration * 1e3,
1417
- appliedTimeout: timeoutMs,
1418
- maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS
1419
- }, "Requested timeout exceeded maximum allowed, capping to 10 minutes");
1420
- const phase1SystemPrompt = hasStructuredOutput ? thinkingSystemPrompt : systemPrompt;
1421
- const messages = [];
1422
- messages.push({
1423
- role: "system",
1424
- content: phase1SystemPrompt
1425
- });
1426
- if (conversationHistory.trim() !== "") messages.push({
1427
- role: "user",
1428
- content: conversationHistory
1429
- });
1430
- messages.push({
1431
- role: "user",
1432
- content: userMessage
1433
- });
1434
- const originalMessageCount = messages.length;
1435
- const compressionConfigResult = getCompressionConfigForModel(primaryModelSettings);
1436
- const compressionConfig = {
1437
- hardLimit: compressionConfigResult.hardLimit,
1438
- safetyBuffer: compressionConfigResult.safetyBuffer,
1439
- enabled: compressionConfigResult.enabled
1440
- };
1441
- const compressor = compressionConfig.enabled ? new MidGenerationCompressor(sessionId, contextId, this.config.tenantId, this.config.projectId, compressionConfig, this.getSummarizerModel(), primaryModelSettings) : null;
1442
- this.currentCompressor = compressor;
1331
+ const messages = this.buildInitialMessages(systemPrompt, thinkingSystemPrompt, hasStructuredOutput, conversationHistory, userMessage);
1332
+ const { originalMessageCount, compressor } = this.setupCompression(messages, sessionId, contextId, primaryModelSettings);
1443
1333
  if (shouldStreamPhase1) {
1444
- const streamResult = streamText({
1334
+ const streamConfig = {
1445
1335
  ...modelSettings,
1446
- toolChoice: "auto",
1447
- messages,
1448
- tools: sanitizedTools,
1449
- prepareStep: async ({ messages: stepMessages }) => {
1450
- if (!compressor) return {};
1451
- if (compressor.isCompressionNeeded(stepMessages)) {
1452
- logger.info({ compressorState: compressor.getState() }, "Triggering layered mid-generation compression");
1453
- try {
1454
- const originalMessages = stepMessages.slice(0, originalMessageCount);
1455
- const generatedMessages = stepMessages.slice(originalMessageCount);
1456
- if (generatedMessages.length > 0) {
1457
- const compressionResult = await compressor.compress(generatedMessages);
1458
- const finalMessages = [...originalMessages];
1459
- if (compressionResult.summary.text_messages && compressionResult.summary.text_messages.length > 0) finalMessages.push(...compressionResult.summary.text_messages);
1460
- const summaryMessage = JSON.stringify({
1461
- high_level: compressionResult.summary?.summary?.high_level,
1462
- user_intent: compressionResult.summary?.summary?.user_intent,
1463
- decisions: compressionResult.summary?.summary?.decisions,
1464
- open_questions: compressionResult.summary?.summary?.open_questions,
1465
- next_steps: compressionResult.summary?.summary?.next_steps,
1466
- related_artifacts: compressionResult?.summary?.summary?.related_artifacts
1467
- });
1468
- finalMessages.push({
1469
- role: "user",
1470
- content: `Based on your research, here's what you've discovered: ${summaryMessage}
1471
-
1472
- Now please provide your answer to my original question using this context.`
1473
- });
1474
- logger.info({
1475
- originalTotal: stepMessages.length,
1476
- compressed: finalMessages.length,
1477
- originalKept: originalMessages.length,
1478
- generatedCompressed: generatedMessages.length
1479
- }, "Generated content compression completed");
1480
- logger.info({ summaryMessage }, "Summary message");
1481
- return { messages: finalMessages };
1482
- }
1483
- return {};
1484
- } catch (error) {
1485
- logger.error({
1486
- error: error instanceof Error ? error.message : String(error),
1487
- stack: error instanceof Error ? error.stack : void 0
1488
- }, "Smart compression failed, falling back to simple compression");
1489
- try {
1490
- const targetSize = Math.floor(compressor.getHardLimit() * .5);
1491
- const fallbackMessages = this.simpleCompression(stepMessages, targetSize);
1492
- logger.info({
1493
- originalCount: stepMessages.length,
1494
- compressedCount: fallbackMessages.length,
1495
- compressionType: "simple_fallback"
1496
- }, "Simple compression fallback completed");
1497
- return { messages: fallbackMessages };
1498
- } catch (fallbackError) {
1499
- logger.error({ error: fallbackError instanceof Error ? fallbackError.message : String(fallbackError) }, "Fallback compression also failed, continuing without compression");
1500
- return {};
1501
- }
1502
- }
1503
- }
1504
- return {};
1505
- },
1506
- stopWhen: async ({ steps }) => {
1507
- const last = steps.at(-1);
1508
- if (last && "text" in last && last.text) try {
1509
- await agentSessionManager.recordEvent(this.getStreamRequestId(), "agent_reasoning", this.config.id, { parts: [{
1510
- type: "text",
1511
- content: last.text
1512
- }] });
1513
- } catch (error) {
1514
- logger.debug({ error }, "Failed to track agent reasoning");
1515
- }
1516
- if (last && last["content"] && last["content"].length > 0) {
1517
- const lastContent = last["content"][last["content"].length - 1];
1518
- if (lastContent["type"] === "tool-error") {
1519
- const error = lastContent["error"];
1520
- if (error && typeof error === "object" && "name" in error && error.name === "connection_refused") return true;
1521
- }
1522
- }
1523
- if (steps.length >= 2) {
1524
- const previousStep = steps[steps.length - 2];
1525
- if (previousStep && "toolCalls" in previousStep && previousStep.toolCalls) {
1526
- if (previousStep.toolCalls.some((tc) => tc.toolName.startsWith("transfer_to_")) && "toolResults" in previousStep && previousStep.toolResults) return true;
1527
- }
1528
- }
1529
- return steps.length >= this.getMaxGenerationSteps();
1530
- },
1531
- experimental_telemetry: {
1532
- isEnabled: true,
1533
- functionId: this.config.id,
1534
- recordInputs: true,
1535
- recordOutputs: true,
1536
- metadata: {
1537
- subAgentId: this.config.id,
1538
- subAgentName: this.config.name
1539
- }
1540
- },
1541
- abortSignal: AbortSignal.timeout(timeoutMs)
1542
- });
1543
- const streamHelper = this.getStreamingHelper();
1544
- if (!streamHelper) throw new Error("Stream helper is unexpectedly undefined in streaming context");
1545
- const session = toolSessionManager.getSession(sessionId);
1546
- const artifactParserOptions = {
1547
- sessionId,
1548
- taskId: session?.taskId,
1549
- projectId: session?.projectId,
1550
- artifactComponents: this.artifactComponents,
1551
- streamRequestId: this.getStreamRequestId(),
1552
- subAgentId: this.config.id
1336
+ toolChoice: "auto"
1553
1337
  };
1554
- const parser = new IncrementalStreamParser(streamHelper, this.config.tenantId, contextId, artifactParserOptions);
1555
- for await (const event of streamResult.fullStream) switch (event.type) {
1556
- case "text-delta":
1557
- await parser.processTextChunk(event.text);
1558
- break;
1559
- case "tool-call":
1560
- parser.markToolResult();
1561
- break;
1562
- case "tool-result":
1563
- parser.markToolResult();
1564
- break;
1565
- case "finish":
1566
- if (event.finishReason === "tool-calls") parser.markToolResult();
1567
- break;
1568
- case "error": {
1569
- if (event.error instanceof Error) throw event.error;
1570
- const errorMessage = event.error?.error?.message;
1571
- throw new Error(errorMessage);
1572
- }
1573
- }
1574
- await parser.finalize();
1338
+ const streamResult = streamText(this.buildBaseGenerationConfig(streamConfig, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, "auto", void 0, false, contextBreakdown.total));
1339
+ const parser = this.setupStreamParser(sessionId, contextId);
1340
+ await this.processStreamEvents(streamResult, parser);
1575
1341
  response = await streamResult;
1576
- const collectedParts = parser.getCollectedParts();
1577
- if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
1578
- kind: part.kind,
1579
- ...part.kind === "text" && { text: part.text },
1580
- ...part.kind === "data" && { data: part.data }
1581
- })) };
1582
- const streamedContent = parser.getAllStreamedContent();
1583
- if (streamedContent.length > 0) response.streamedContent = { parts: streamedContent.map((part) => ({
1584
- kind: part.kind,
1585
- ...part.kind === "text" && { text: part.text },
1586
- ...part.kind === "data" && { data: part.data }
1587
- })) };
1342
+ response = this.formatStreamingResponse(response, parser);
1588
1343
  } else {
1589
- let genConfig;
1590
- if (hasStructuredOutput) genConfig = {
1591
- ...modelSettings,
1592
- toolChoice: "required"
1593
- };
1594
- else genConfig = {
1595
- ...modelSettings,
1596
- toolChoice: "auto"
1597
- };
1598
- response = await generateText({
1599
- ...genConfig,
1600
- messages,
1601
- tools: sanitizedTools,
1602
- prepareStep: async ({ messages: stepMessages }) => {
1603
- if (!compressor) return {};
1604
- if (compressor.isCompressionNeeded(stepMessages)) {
1605
- logger.info({ compressorState: compressor.getState() }, "Triggering layered mid-generation compression");
1606
- try {
1607
- const originalMessages = stepMessages.slice(0, originalMessageCount);
1608
- const generatedMessages = stepMessages.slice(originalMessageCount);
1609
- if (generatedMessages.length > 0) {
1610
- const compressionResult = await compressor.compress(generatedMessages);
1611
- const finalMessages = [...originalMessages];
1612
- if (compressionResult.summary.text_messages && compressionResult.summary.text_messages.length > 0) finalMessages.push(...compressionResult.summary.text_messages);
1613
- const summaryMessage = JSON.stringify({
1614
- high_level: compressionResult.summary?.summary?.high_level,
1615
- user_intent: compressionResult.summary?.summary?.user_intent,
1616
- decisions: compressionResult.summary?.summary?.decisions,
1617
- open_questions: compressionResult.summary?.summary?.open_questions,
1618
- next_steps: compressionResult.summary?.summary?.next_steps,
1619
- related_artifacts: compressionResult?.summary?.summary?.related_artifacts
1620
- });
1621
- finalMessages.push({
1622
- role: "user",
1623
- content: `Based on your research, here's what you've discovered: ${summaryMessage}
1624
-
1625
- Now please provide your answer to my original question using this context.`
1626
- });
1627
- logger.info({
1628
- originalTotal: stepMessages.length,
1629
- compressed: finalMessages.length,
1630
- originalKept: originalMessages.length,
1631
- generatedCompressed: generatedMessages.length
1632
- }, "Generated content compression completed");
1633
- logger.info({ summaryMessage }, "Summary message");
1634
- return { messages: finalMessages };
1635
- }
1636
- return {};
1637
- } catch (error) {
1638
- logger.error({
1639
- error: error instanceof Error ? error.message : String(error),
1640
- stack: error instanceof Error ? error.stack : void 0
1641
- }, "Smart compression failed, falling back to simple compression");
1642
- try {
1643
- const targetSize = Math.floor(compressor.getHardLimit() * .5);
1644
- const fallbackMessages = this.simpleCompression(stepMessages, targetSize);
1645
- logger.info({
1646
- originalCount: stepMessages.length,
1647
- compressedCount: fallbackMessages.length,
1648
- compressionType: "simple_fallback"
1649
- }, "Simple compression fallback completed");
1650
- return { messages: fallbackMessages };
1651
- } catch (fallbackError) {
1652
- logger.error({ error: fallbackError instanceof Error ? fallbackError.message : String(fallbackError) }, "Fallback compression also failed, continuing without compression");
1653
- return {};
1654
- }
1655
- }
1656
- }
1657
- return {};
1658
- },
1659
- stopWhen: async ({ steps }) => {
1660
- const last = steps.at(-1);
1661
- if (last && "text" in last && last.text) try {
1662
- await agentSessionManager.recordEvent(this.getStreamRequestId(), "agent_reasoning", this.config.id, { parts: [{
1663
- type: "text",
1664
- content: last.text
1665
- }] });
1666
- } catch (error) {
1667
- logger.debug({ error }, "Failed to track agent reasoning");
1668
- }
1669
- if (steps.length >= 2) {
1670
- const previousStep = steps[steps.length - 2];
1671
- if (previousStep && "toolCalls" in previousStep && previousStep.toolCalls) {
1672
- if (previousStep.toolCalls.some((tc) => tc.toolName.startsWith("transfer_to_") || tc.toolName === "thinking_complete") && "toolResults" in previousStep && previousStep.toolResults) return true;
1673
- }
1674
- }
1675
- return steps.length >= this.getMaxGenerationSteps();
1676
- },
1677
- experimental_telemetry: {
1678
- isEnabled: true,
1679
- functionId: this.config.id,
1680
- recordInputs: true,
1681
- recordOutputs: true,
1682
- metadata: {
1683
- phase: "planning",
1684
- subAgentId: this.config.id,
1685
- subAgentName: this.config.name
1686
- }
1687
- },
1688
- abortSignal: AbortSignal.timeout(timeoutMs)
1689
- });
1344
+ const toolChoice = hasStructuredOutput ? "required" : "auto";
1345
+ response = await generateText(this.buildBaseGenerationConfig(modelSettings, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, toolChoice, "planning", true, contextBreakdown.total));
1690
1346
  }
1691
1347
  if (response.steps) {
1692
1348
  const resolvedSteps = await response.steps;
@@ -1696,29 +1352,368 @@ Now please provide your answer to my original question using this context.`
1696
1352
  };
1697
1353
  }
1698
1354
  if (hasStructuredOutput && !hasToolCallWithPrefix("transfer_to_")(response)) if (response.steps?.flatMap((s) => s.toolCalls || [])?.find((tc) => tc.toolName === "thinking_complete")) {
1699
- const reasoningFlow = [];
1700
- const compressionSummary = this.currentCompressor?.getCompressionSummary();
1701
- if (compressionSummary) {
1702
- const summaryContent = JSON.stringify(compressionSummary, null, 2);
1703
- reasoningFlow.push({
1704
- role: "assistant",
1705
- content: `## Research Summary (Compressed)\n\nBased on tool executions, here's the comprehensive summary:\n\n\`\`\`json\n${summaryContent}\n\`\`\`\n\nThis summary represents all tool execution results in compressed form. Full details are preserved in artifacts.`
1706
- });
1707
- } else if (response.steps) response.steps.forEach((step) => {
1708
- if (step.toolCalls && step.toolResults) step.toolCalls.forEach((call, index) => {
1709
- const result = step.toolResults[index];
1710
- if (result) {
1711
- const storedResult = toolSessionManager.getToolResult(sessionId, result.toolCallId);
1712
- if ((storedResult?.toolName || call.toolName) === "thinking_complete") return;
1713
- const actualResult = storedResult?.result || result.result || result;
1714
- const actualArgs = storedResult?.args || call.args;
1715
- const cleanResult = actualResult && typeof actualResult === "object" && !Array.isArray(actualResult) ? Object.fromEntries(Object.entries(actualResult).filter(([key]) => key !== "_structureHints")) : actualResult;
1716
- const input = actualArgs ? JSON.stringify(actualArgs, null, 2) : "No input";
1717
- const output = typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, null, 2);
1718
- let structureHintsFormatted = "";
1719
- if (actualResult?._structureHints && this.artifactComponents && this.artifactComponents.length > 0) {
1720
- const hints = actualResult._structureHints;
1721
- structureHintsFormatted = `
1355
+ const reasoningFlow = this.buildReasoningFlow(response, sessionId);
1356
+ const dataComponentsSchema = this.buildDataComponentsSchema();
1357
+ const structuredModelSettings = ModelFactory.prepareGenerationConfig(this.getStructuredOutputModel());
1358
+ const phase2TimeoutMs = this.calculatePhase2Timeout(structuredModelSettings);
1359
+ if (this.getStreamingHelper()) {
1360
+ const phase2Messages = await this.buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow);
1361
+ const result = await this.executeStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, sessionId, contextId, response);
1362
+ response = result;
1363
+ textResponse = result.textResponse;
1364
+ } else {
1365
+ const phase2Messages = await this.buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow);
1366
+ const result = await this.executeNonStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, response);
1367
+ response = result;
1368
+ textResponse = result.textResponse;
1369
+ }
1370
+ } else textResponse = response.text || "";
1371
+ else textResponse = response.steps[response.steps.length - 1].text || "";
1372
+ span.setStatus({ code: SpanStatusCode.OK });
1373
+ span.end();
1374
+ const formattedResponse = await this.formatFinalResponse(response, textResponse, sessionId, contextId);
1375
+ if (streamRequestId) {
1376
+ const generationType = response.object ? "object_generation" : "text_generation";
1377
+ agentSessionManager.recordEvent(streamRequestId, "agent_generate", this.config.id, {
1378
+ parts: (formattedResponse.formattedContent?.parts || []).map((part) => ({
1379
+ type: part.kind === "text" ? "text" : part.kind === "data" ? "tool_result" : "text",
1380
+ content: part.text || JSON.stringify(part.data)
1381
+ })),
1382
+ generationType
1383
+ });
1384
+ }
1385
+ if (compressor) compressor.fullCleanup();
1386
+ this.currentCompressor = null;
1387
+ return formattedResponse;
1388
+ } catch (error) {
1389
+ this.handleGenerationError(error, span);
1390
+ }
1391
+ });
1392
+ }
1393
+ /**
1394
+ * Setup generation context and initialize streaming helper
1395
+ */
1396
+ setupGenerationContext(runtimeContext) {
1397
+ const contextId = runtimeContext?.contextId || "default";
1398
+ const taskId = runtimeContext?.metadata?.taskId || "unknown";
1399
+ const streamRequestId = runtimeContext?.metadata?.streamRequestId;
1400
+ const sessionId = streamRequestId || "fallback-session";
1401
+ this.streamRequestId = streamRequestId;
1402
+ this.streamHelper = streamRequestId ? getStreamHelper(streamRequestId) : void 0;
1403
+ if (streamRequestId && this.artifactComponents.length > 0) agentSessionManager.updateArtifactComponents(streamRequestId, this.artifactComponents);
1404
+ const conversationId = runtimeContext?.metadata?.conversationId;
1405
+ if (conversationId) this.setConversationId(conversationId);
1406
+ return {
1407
+ contextId,
1408
+ taskId,
1409
+ streamRequestId,
1410
+ sessionId
1411
+ };
1412
+ }
1413
+ /**
1414
+ * Load all tools and system prompts in parallel, then combine and sanitize them
1415
+ */
1416
+ async loadToolsAndPrompts(sessionId, streamRequestId, runtimeContext) {
1417
+ const [mcpTools, systemPromptResult, thinkingSystemPromptResult, functionTools, relationTools, defaultTools] = await tracer.startActiveSpan("agent.load_tools", { attributes: {
1418
+ "subAgent.name": this.config.name,
1419
+ "session.id": sessionId || "none"
1420
+ } }, async (childSpan) => {
1421
+ try {
1422
+ const result = await Promise.all([
1423
+ this.getMcpTools(sessionId, streamRequestId),
1424
+ this.buildSystemPrompt(runtimeContext, false),
1425
+ this.buildSystemPrompt(runtimeContext, true),
1426
+ this.getFunctionTools(sessionId, streamRequestId),
1427
+ Promise.resolve(this.getRelationTools(runtimeContext, sessionId)),
1428
+ this.getDefaultTools(streamRequestId)
1429
+ ]);
1430
+ childSpan.setStatus({ code: SpanStatusCode.OK });
1431
+ return result;
1432
+ } catch (err) {
1433
+ setSpanWithError$1(childSpan, err instanceof Error ? err : new Error(String(err)));
1434
+ throw err;
1435
+ } finally {
1436
+ childSpan.end();
1437
+ }
1438
+ });
1439
+ const systemPrompt = systemPromptResult.prompt;
1440
+ const thinkingSystemPrompt = thinkingSystemPromptResult.prompt;
1441
+ const contextBreakdown = systemPromptResult.breakdown;
1442
+ const allTools = {
1443
+ ...mcpTools,
1444
+ ...functionTools,
1445
+ ...relationTools,
1446
+ ...defaultTools
1447
+ };
1448
+ return {
1449
+ systemPrompt,
1450
+ thinkingSystemPrompt,
1451
+ sanitizedTools: this.sanitizeToolsForAISDK(allTools),
1452
+ contextBreakdown
1453
+ };
1454
+ }
1455
+ /**
1456
+ * Build conversation history based on configuration mode and filters
1457
+ */
1458
+ async buildConversationHistory(contextId, taskId, userMessage, streamRequestId, initialContextBreakdown) {
1459
+ let conversationHistory = "";
1460
+ const historyConfig = this.config.conversationHistoryConfig ?? createDefaultConversationHistoryConfig();
1461
+ if (historyConfig && historyConfig.mode !== "none") {
1462
+ if (historyConfig.mode === "full") {
1463
+ const filters = {
1464
+ delegationId: this.delegationId,
1465
+ isDelegated: this.isDelegatedAgent
1466
+ };
1467
+ conversationHistory = await getConversationHistoryWithCompression({
1468
+ tenantId: this.config.tenantId,
1469
+ projectId: this.config.projectId,
1470
+ conversationId: contextId,
1471
+ currentMessage: userMessage,
1472
+ options: historyConfig,
1473
+ filters,
1474
+ summarizerModel: this.getSummarizerModel(),
1475
+ streamRequestId,
1476
+ fullContextSize: initialContextBreakdown.total
1477
+ });
1478
+ } else if (historyConfig.mode === "scoped") conversationHistory = await getConversationHistoryWithCompression({
1479
+ tenantId: this.config.tenantId,
1480
+ projectId: this.config.projectId,
1481
+ conversationId: contextId,
1482
+ currentMessage: userMessage,
1483
+ options: historyConfig,
1484
+ filters: {
1485
+ subAgentId: this.config.id,
1486
+ taskId,
1487
+ delegationId: this.delegationId,
1488
+ isDelegated: this.isDelegatedAgent
1489
+ },
1490
+ summarizerModel: this.getSummarizerModel(),
1491
+ streamRequestId,
1492
+ fullContextSize: initialContextBreakdown.total
1493
+ });
1494
+ }
1495
+ const conversationHistoryTokens = estimateTokens(conversationHistory);
1496
+ const updatedContextBreakdown = {
1497
+ ...initialContextBreakdown,
1498
+ conversationHistory: conversationHistoryTokens
1499
+ };
1500
+ calculateBreakdownTotal(updatedContextBreakdown);
1501
+ return {
1502
+ conversationHistory,
1503
+ contextBreakdown: updatedContextBreakdown
1504
+ };
1505
+ }
1506
+ /**
1507
+ * Configure model settings, timeouts, and streaming behavior
1508
+ */
1509
+ configureModelSettings() {
1510
+ const primaryModelSettings = this.getPrimaryModel();
1511
+ const modelSettings = ModelFactory.prepareGenerationConfig(primaryModelSettings);
1512
+ const hasStructuredOutput = Boolean(this.config.dataComponents && this.config.dataComponents.length > 0);
1513
+ const shouldStreamPhase1 = this.getStreamingHelper() && !hasStructuredOutput;
1514
+ const configuredTimeout = modelSettings.maxDuration ? Math.min(modelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : shouldStreamPhase1 ? LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING : LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING;
1515
+ const timeoutMs = Math.min(configuredTimeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
1516
+ if (modelSettings.maxDuration && modelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
1517
+ requestedTimeout: modelSettings.maxDuration * 1e3,
1518
+ appliedTimeout: timeoutMs,
1519
+ maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS
1520
+ }, "Requested timeout exceeded maximum allowed, capping to 10 minutes");
1521
+ return {
1522
+ primaryModelSettings,
1523
+ modelSettings: {
1524
+ ...modelSettings,
1525
+ maxDuration: timeoutMs / 1e3
1526
+ },
1527
+ hasStructuredOutput,
1528
+ shouldStreamPhase1,
1529
+ timeoutMs
1530
+ };
1531
+ }
1532
+ /**
1533
+ * Build initial messages array with system prompt and user content
1534
+ */
1535
+ buildInitialMessages(systemPrompt, thinkingSystemPrompt, hasStructuredOutput, conversationHistory, userMessage) {
1536
+ const phase1SystemPrompt = hasStructuredOutput ? thinkingSystemPrompt : systemPrompt;
1537
+ const messages = [];
1538
+ messages.push({
1539
+ role: "system",
1540
+ content: phase1SystemPrompt
1541
+ });
1542
+ if (conversationHistory.trim() !== "") messages.push({
1543
+ role: "user",
1544
+ content: conversationHistory
1545
+ });
1546
+ messages.push({
1547
+ role: "user",
1548
+ content: userMessage
1549
+ });
1550
+ return messages;
1551
+ }
1552
+ /**
1553
+ * Setup compression for the current generation
1554
+ */
1555
+ setupCompression(messages, sessionId, contextId, primaryModelSettings) {
1556
+ const originalMessageCount = messages.length;
1557
+ const compressionConfigResult = getCompressionConfigForModel(primaryModelSettings);
1558
+ const compressionConfig = {
1559
+ hardLimit: compressionConfigResult.hardLimit,
1560
+ safetyBuffer: compressionConfigResult.safetyBuffer,
1561
+ enabled: compressionConfigResult.enabled
1562
+ };
1563
+ const compressor = compressionConfig.enabled ? new MidGenerationCompressor(sessionId, contextId, this.config.tenantId, this.config.projectId, compressionConfig, this.getSummarizerModel(), primaryModelSettings) : null;
1564
+ this.currentCompressor = compressor;
1565
+ return {
1566
+ originalMessageCount,
1567
+ compressor
1568
+ };
1569
+ }
1570
+ /**
1571
+ * Prepare step function for streaming with compression logic
1572
+ */
1573
+ async handlePrepareStepCompression(stepMessages, compressor, originalMessageCount, fullContextSize) {
1574
+ if (!compressor) return {};
1575
+ if (compressor.isCompressionNeeded(stepMessages)) {
1576
+ logger.info({ compressorState: compressor.getState() }, "Triggering layered mid-generation compression");
1577
+ const originalMessages = stepMessages.slice(0, originalMessageCount);
1578
+ const generatedMessages = stepMessages.slice(originalMessageCount);
1579
+ if (generatedMessages.length > 0) {
1580
+ const compressionResult = await compressor.safeCompress(generatedMessages, fullContextSize);
1581
+ if (Array.isArray(compressionResult.summary)) {
1582
+ const compressedMessages = compressionResult.summary;
1583
+ logger.info({
1584
+ originalTotal: stepMessages.length,
1585
+ compressed: originalMessages.length + compressedMessages.length,
1586
+ originalKept: originalMessages.length,
1587
+ generatedCompressed: compressedMessages.length
1588
+ }, "Simple compression fallback applied");
1589
+ return { messages: [...originalMessages, ...compressedMessages] };
1590
+ }
1591
+ const finalMessages = [...originalMessages];
1592
+ if (compressionResult.summary.text_messages && compressionResult.summary.text_messages.length > 0) finalMessages.push(...compressionResult.summary.text_messages);
1593
+ const summaryData = {
1594
+ high_level: compressionResult.summary?.high_level,
1595
+ user_intent: compressionResult.summary?.user_intent,
1596
+ decisions: compressionResult.summary?.decisions,
1597
+ open_questions: compressionResult.summary?.open_questions,
1598
+ next_steps: compressionResult.summary?.next_steps,
1599
+ related_artifacts: compressionResult.summary?.related_artifacts
1600
+ };
1601
+ if (summaryData.related_artifacts && summaryData.related_artifacts.length > 0) summaryData.related_artifacts = summaryData.related_artifacts.map((artifact) => ({
1602
+ ...artifact,
1603
+ artifact_reference: `<artifact:ref id="${artifact.id}" tool="${artifact.tool_call_id}" />`
1604
+ }));
1605
+ const summaryMessage = JSON.stringify(summaryData);
1606
+ finalMessages.push({
1607
+ role: "user",
1608
+ content: `Based on your research, here's what you've discovered: ${summaryMessage}
1609
+
1610
+ **IMPORTANT**: If you have enough information from this compressed research to answer my original question, please provide your answer now. Only continue with additional tool calls if you need critical missing information that wasn't captured in the research above. When referencing any artifacts from the compressed research, you MUST use <artifact:ref id="artifact_id" tool="tool_call_id" /> tags with the exact IDs from the related_artifacts above.`
1611
+ });
1612
+ logger.info({
1613
+ originalTotal: stepMessages.length,
1614
+ compressed: finalMessages.length,
1615
+ originalKept: originalMessages.length,
1616
+ generatedCompressed: generatedMessages.length
1617
+ }, "AI compression completed successfully");
1618
+ return { messages: finalMessages };
1619
+ }
1620
+ return {};
1621
+ }
1622
+ return {};
1623
+ }
1624
+ async handleStopWhenConditions(steps, includeThinkingComplete = false) {
1625
+ const last = steps.at(-1);
1626
+ if (last && "text" in last && last.text) try {
1627
+ await agentSessionManager.recordEvent(this.getStreamRequestId(), "agent_reasoning", this.config.id, { parts: [{
1628
+ type: "text",
1629
+ content: last.text
1630
+ }] });
1631
+ } catch (error) {
1632
+ logger.debug({ error }, "Failed to track agent reasoning");
1633
+ }
1634
+ if (!includeThinkingComplete && last && last["content"] && last["content"].length > 0) {
1635
+ const lastContent = last["content"][last["content"].length - 1];
1636
+ if (lastContent["type"] === "tool-error") {
1637
+ const error = lastContent["error"];
1638
+ if (error && typeof error === "object" && "name" in error && error.name === "connection_refused") return true;
1639
+ }
1640
+ }
1641
+ if (steps.length >= 2) {
1642
+ const previousStep = steps[steps.length - 2];
1643
+ if (previousStep && "toolCalls" in previousStep && previousStep.toolCalls) {
1644
+ const stopToolNames = includeThinkingComplete ? ["transfer_to_", "thinking_complete"] : ["transfer_to_"];
1645
+ if (previousStep.toolCalls.some((tc) => stopToolNames.some((toolName) => toolName.endsWith("_") ? tc.toolName.startsWith(toolName) : tc.toolName === toolName)) && "toolResults" in previousStep && previousStep.toolResults) return true;
1646
+ }
1647
+ }
1648
+ return steps.length >= this.getMaxGenerationSteps();
1649
+ }
1650
+ setupStreamParser(sessionId, contextId) {
1651
+ const streamHelper = this.getStreamingHelper();
1652
+ if (!streamHelper) throw new Error("Stream helper is unexpectedly undefined in streaming context");
1653
+ const session = toolSessionManager.getSession(sessionId);
1654
+ const artifactParserOptions = {
1655
+ sessionId,
1656
+ taskId: session?.taskId,
1657
+ projectId: session?.projectId,
1658
+ artifactComponents: this.artifactComponents,
1659
+ streamRequestId: this.getStreamRequestId(),
1660
+ subAgentId: this.config.id
1661
+ };
1662
+ return new IncrementalStreamParser(streamHelper, this.config.tenantId, contextId, artifactParserOptions);
1663
+ }
1664
+ buildTelemetryConfig(phase) {
1665
+ return {
1666
+ isEnabled: true,
1667
+ functionId: this.config.id,
1668
+ recordInputs: true,
1669
+ recordOutputs: true,
1670
+ metadata: {
1671
+ ...phase && { phase },
1672
+ subAgentId: this.config.id,
1673
+ subAgentName: this.config.name
1674
+ }
1675
+ };
1676
+ }
1677
+ buildBaseGenerationConfig(modelSettings, messages, sanitizedTools, compressor, originalMessageCount, timeoutMs, toolChoice = "auto", phase, includeThinkingComplete = false, fullContextSize) {
1678
+ return {
1679
+ ...modelSettings,
1680
+ toolChoice,
1681
+ messages,
1682
+ tools: sanitizedTools,
1683
+ prepareStep: async ({ messages: stepMessages }) => {
1684
+ return await this.handlePrepareStepCompression(stepMessages, compressor, originalMessageCount, fullContextSize);
1685
+ },
1686
+ stopWhen: async ({ steps }) => {
1687
+ return await this.handleStopWhenConditions(steps, includeThinkingComplete);
1688
+ },
1689
+ experimental_telemetry: this.buildTelemetryConfig(phase),
1690
+ abortSignal: AbortSignal.timeout(timeoutMs)
1691
+ };
1692
+ }
1693
+ buildReasoningFlow(response, sessionId) {
1694
+ const reasoningFlow = [];
1695
+ const compressionSummary = this.currentCompressor?.getCompressionSummary();
1696
+ if (compressionSummary) {
1697
+ const summaryContent = JSON.stringify(compressionSummary, null, 2);
1698
+ reasoningFlow.push({
1699
+ role: "assistant",
1700
+ content: `## Research Summary (Compressed)\n\nBased on tool executions, here's the comprehensive summary:\n\n\`\`\`json\n${summaryContent}\n\`\`\`\n\nThis summary represents all tool execution results in compressed form. Full details are preserved in artifacts.`
1701
+ });
1702
+ } else if (response.steps) response.steps.forEach((step) => {
1703
+ if (step.toolCalls && step.toolResults) step.toolCalls.forEach((call, index) => {
1704
+ const result = step.toolResults[index];
1705
+ if (result) {
1706
+ const storedResult = toolSessionManager.getToolResult(sessionId, result.toolCallId);
1707
+ if ((storedResult?.toolName || call.toolName) === "thinking_complete") return;
1708
+ const actualResult = storedResult?.result || result.result || result;
1709
+ const actualArgs = storedResult?.args || call.args;
1710
+ const cleanResult = actualResult && typeof actualResult === "object" && !Array.isArray(actualResult) ? Object.fromEntries(Object.entries(actualResult).filter(([key]) => key !== "_structureHints")) : actualResult;
1711
+ const input = actualArgs ? JSON.stringify(actualArgs, null, 2) : "No input";
1712
+ const output = typeof cleanResult === "string" ? cleanResult : JSON.stringify(cleanResult, null, 2);
1713
+ let structureHintsFormatted = "";
1714
+ if (actualResult?._structureHints && this.artifactComponents && this.artifactComponents.length > 0) {
1715
+ const hints = actualResult._structureHints;
1716
+ structureHintsFormatted = `
1722
1717
  ### 📊 Structure Hints for Artifact Creation
1723
1718
 
1724
1719
  **Terminal Field Paths (${hints.terminalPaths?.length || 0} found):**
@@ -1742,8 +1737,8 @@ ${hints.commonFields?.map((field) => ` • ${field}`).join("\n") || " None det
1742
1737
 
1743
1738
  **Forbidden Syntax:** ${hints.forbiddenSyntax || "Use these paths for artifact base selectors."}
1744
1739
  `;
1745
- }
1746
- const formattedResult = `## Tool: ${call.toolName}
1740
+ }
1741
+ const formattedResult = `## Tool: ${call.toolName}
1747
1742
 
1748
1743
  ### 🔧 TOOL_CALL_ID: ${result.toolCallId}
1749
1744
 
@@ -1752,185 +1747,179 @@ ${input}
1752
1747
 
1753
1748
  ### Output
1754
1749
  ${output}${structureHintsFormatted}`;
1755
- reasoningFlow.push({
1756
- role: "assistant",
1757
- content: formattedResult
1758
- });
1759
- }
1760
- });
1750
+ reasoningFlow.push({
1751
+ role: "assistant",
1752
+ content: formattedResult
1761
1753
  });
1762
- const componentSchemas = [];
1763
- if (this.config.dataComponents && this.config.dataComponents.length > 0) this.config.dataComponents.forEach((dc) => {
1764
- const propsSchema = jsonSchemaToZod(dc.props);
1765
- componentSchemas.push(z.object({
1766
- id: z.string(),
1767
- name: z.literal(dc.name),
1768
- props: propsSchema
1769
- }));
1770
- });
1771
- if (this.artifactComponents.length > 0) {
1772
- const artifactCreateSchemas = ArtifactCreateSchema.getSchemas(this.artifactComponents);
1773
- componentSchemas.push(...artifactCreateSchemas);
1774
- componentSchemas.push(ArtifactReferenceSchema.getSchema());
1775
- }
1776
- let dataComponentsSchema;
1777
- if (componentSchemas.length === 1) dataComponentsSchema = componentSchemas[0];
1778
- else dataComponentsSchema = z.union(componentSchemas);
1779
- const structuredModelSettings = ModelFactory.prepareGenerationConfig(this.getStructuredOutputModel());
1780
- const configuredPhase2Timeout = structuredModelSettings.maxDuration ? Math.min(structuredModelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS;
1781
- const phase2TimeoutMs = Math.min(configuredPhase2Timeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
1782
- if (structuredModelSettings.maxDuration && structuredModelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
1783
- requestedTimeout: structuredModelSettings.maxDuration * 1e3,
1784
- appliedTimeout: phase2TimeoutMs,
1785
- maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS,
1786
- phase: "structured_generation"
1787
- }, "Phase 2 requested timeout exceeded maximum allowed, capping to 10 minutes");
1788
- if (this.getStreamingHelper()) {
1789
- const phase2Messages = [{
1790
- role: "system",
1791
- content: await this.buildPhase2SystemPrompt(runtimeContext)
1792
- }];
1793
- if (conversationHistory.trim() !== "") phase2Messages.push({
1794
- role: "user",
1795
- content: conversationHistory
1796
- });
1797
- phase2Messages.push({
1798
- role: "user",
1799
- content: userMessage
1800
- });
1801
- phase2Messages.push(...reasoningFlow);
1802
- if (reasoningFlow.length > 0 && reasoningFlow[reasoningFlow.length - 1]?.role === "assistant") phase2Messages.push({
1803
- role: "user",
1804
- content: "Continue with the structured response."
1805
- });
1806
- const streamResult = streamObject({
1807
- ...structuredModelSettings,
1808
- messages: phase2Messages,
1809
- schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
1810
- experimental_telemetry: {
1811
- isEnabled: true,
1812
- functionId: this.config.id,
1813
- recordInputs: true,
1814
- recordOutputs: true,
1815
- metadata: {
1816
- phase: "structured_generation",
1817
- subAgentId: this.config.id,
1818
- subAgentName: this.config.name
1819
- }
1820
- },
1821
- abortSignal: AbortSignal.timeout(phase2TimeoutMs)
1822
- });
1823
- const streamHelper = this.getStreamingHelper();
1824
- if (!streamHelper) throw new Error("Stream helper is unexpectedly undefined in streaming context");
1825
- const session = toolSessionManager.getSession(sessionId);
1826
- const artifactParserOptions = {
1827
- sessionId,
1828
- taskId: session?.taskId,
1829
- projectId: session?.projectId,
1830
- artifactComponents: this.artifactComponents,
1831
- streamRequestId: this.getStreamRequestId(),
1832
- subAgentId: this.config.id
1833
- };
1834
- const parser = new IncrementalStreamParser(streamHelper, this.config.tenantId, contextId, artifactParserOptions);
1835
- for await (const delta of streamResult.partialObjectStream) if (delta) await parser.processObjectDelta(delta);
1836
- await parser.finalize();
1837
- const structuredResponse = await streamResult;
1838
- const collectedParts = parser.getCollectedParts();
1839
- if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
1840
- kind: part.kind,
1841
- ...part.kind === "text" && { text: part.text },
1842
- ...part.kind === "data" && { data: part.data }
1843
- })) };
1844
- response = {
1845
- ...response,
1846
- object: structuredResponse.object
1847
- };
1848
- textResponse = JSON.stringify(structuredResponse.object, null, 2);
1849
- } else {
1850
- const { withJsonPostProcessing } = await import("../utils/json-postprocessor.js");
1851
- const phase2Messages = [{
1852
- role: "system",
1853
- content: await this.buildPhase2SystemPrompt(runtimeContext)
1854
- }];
1855
- if (conversationHistory.trim() !== "") phase2Messages.push({
1856
- role: "user",
1857
- content: conversationHistory
1858
- });
1859
- phase2Messages.push({
1860
- role: "user",
1861
- content: userMessage
1862
- });
1863
- phase2Messages.push(...reasoningFlow);
1864
- if (reasoningFlow.length > 0 && reasoningFlow[reasoningFlow.length - 1]?.role === "assistant") phase2Messages.push({
1865
- role: "user",
1866
- content: "Continue with the structured response."
1867
- });
1868
- const structuredResponse = await generateObject(withJsonPostProcessing({
1869
- ...structuredModelSettings,
1870
- messages: phase2Messages,
1871
- schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
1872
- experimental_telemetry: {
1873
- isEnabled: true,
1874
- functionId: this.config.id,
1875
- recordInputs: true,
1876
- recordOutputs: true,
1877
- metadata: {
1878
- phase: "structured_generation",
1879
- subAgentId: this.config.id,
1880
- subAgentName: this.config.name
1881
- }
1882
- },
1883
- abortSignal: AbortSignal.timeout(phase2TimeoutMs)
1884
- }));
1885
- response = {
1886
- ...response,
1887
- object: structuredResponse.object
1888
- };
1889
- textResponse = JSON.stringify(structuredResponse.object, null, 2);
1890
- }
1891
- } else textResponse = response.text || "";
1892
- else textResponse = response.steps[response.steps.length - 1].text || "";
1893
- span.setStatus({ code: SpanStatusCode.OK });
1894
- span.end();
1895
- let formattedContent = response.formattedContent || null;
1896
- if (!formattedContent) {
1897
- const session = toolSessionManager.getSession(sessionId);
1898
- const responseFormatter = new ResponseFormatter(this.config.tenantId, {
1899
- sessionId,
1900
- taskId: session?.taskId,
1901
- projectId: session?.projectId,
1902
- contextId,
1903
- artifactComponents: this.artifactComponents,
1904
- streamRequestId: this.getStreamRequestId(),
1905
- subAgentId: this.config.id
1906
- });
1907
- if (response.object) formattedContent = await responseFormatter.formatObjectResponse(response.object, contextId);
1908
- else if (textResponse) formattedContent = await responseFormatter.formatResponse(textResponse, contextId);
1909
1754
  }
1910
- const formattedResponse = {
1911
- ...response,
1912
- formattedContent
1913
- };
1914
- if (streamRequestId) {
1915
- const generationType = response.object ? "object_generation" : "text_generation";
1916
- agentSessionManager.recordEvent(streamRequestId, "agent_generate", this.config.id, {
1917
- parts: (formattedContent?.parts || []).map((part) => ({
1918
- type: part.kind === "text" ? "text" : part.kind === "data" ? "tool_result" : "text",
1919
- content: part.text || JSON.stringify(part.data)
1920
- })),
1921
- generationType
1922
- });
1923
- }
1924
- this.currentCompressor = null;
1925
- return formattedResponse;
1926
- } catch (error) {
1927
- this.currentCompressor = null;
1928
- const errorToThrow = error instanceof Error ? error : new Error(String(error));
1929
- setSpanWithError$1(span, errorToThrow);
1930
- span.end();
1931
- throw errorToThrow;
1932
- }
1755
+ });
1756
+ });
1757
+ return reasoningFlow;
1758
+ }
1759
+ buildDataComponentsSchema() {
1760
+ const componentSchemas = [];
1761
+ if (this.config.dataComponents && this.config.dataComponents.length > 0) this.config.dataComponents.forEach((dc) => {
1762
+ const propsSchema = jsonSchemaToZod(dc.props);
1763
+ componentSchemas.push(z.object({
1764
+ id: z.string(),
1765
+ name: z.literal(dc.name),
1766
+ props: propsSchema
1767
+ }));
1768
+ });
1769
+ if (this.artifactComponents.length > 0) {
1770
+ const artifactCreateSchemas = ArtifactCreateSchema.getSchemas(this.artifactComponents);
1771
+ componentSchemas.push(...artifactCreateSchemas);
1772
+ componentSchemas.push(ArtifactReferenceSchema.getSchema());
1773
+ }
1774
+ let dataComponentsSchema;
1775
+ if (componentSchemas.length === 1) dataComponentsSchema = componentSchemas[0];
1776
+ else dataComponentsSchema = z.union(componentSchemas);
1777
+ return dataComponentsSchema;
1778
+ }
1779
+ calculatePhase2Timeout(structuredModelSettings) {
1780
+ const configuredPhase2Timeout = structuredModelSettings.maxDuration ? Math.min(structuredModelSettings.maxDuration * 1e3, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) : LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS;
1781
+ const phase2TimeoutMs = Math.min(configuredPhase2Timeout, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS);
1782
+ if (structuredModelSettings.maxDuration && structuredModelSettings.maxDuration * 1e3 > LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS) logger.warn({
1783
+ requestedTimeout: structuredModelSettings.maxDuration * 1e3,
1784
+ appliedTimeout: phase2TimeoutMs,
1785
+ maxAllowed: LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS,
1786
+ phase: "structured_generation"
1787
+ }, "Phase 2 requested timeout exceeded maximum allowed, capping to 10 minutes");
1788
+ return phase2TimeoutMs;
1789
+ }
1790
+ async buildPhase2Messages(runtimeContext, conversationHistory, userMessage, reasoningFlow) {
1791
+ const phase2Messages = [{
1792
+ role: "system",
1793
+ content: await this.buildPhase2SystemPrompt(runtimeContext)
1794
+ }];
1795
+ if (conversationHistory.trim() !== "") phase2Messages.push({
1796
+ role: "user",
1797
+ content: conversationHistory
1798
+ });
1799
+ phase2Messages.push({
1800
+ role: "user",
1801
+ content: userMessage
1933
1802
  });
1803
+ phase2Messages.push(...reasoningFlow);
1804
+ if (reasoningFlow.length > 0 && reasoningFlow[reasoningFlow.length - 1]?.role === "assistant") phase2Messages.push({
1805
+ role: "user",
1806
+ content: "Continue with the structured response."
1807
+ });
1808
+ return phase2Messages;
1809
+ }
1810
+ async executeStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, sessionId, contextId, response) {
1811
+ const streamResult = streamObject({
1812
+ ...structuredModelSettings,
1813
+ messages: phase2Messages,
1814
+ schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
1815
+ experimental_telemetry: this.buildTelemetryConfig("structured_generation"),
1816
+ abortSignal: AbortSignal.timeout(phase2TimeoutMs)
1817
+ });
1818
+ const parser = this.setupStreamParser(sessionId, contextId);
1819
+ for await (const delta of streamResult.partialObjectStream) if (delta) await parser.processObjectDelta(delta);
1820
+ await parser.finalize();
1821
+ const structuredResponse = await streamResult;
1822
+ const collectedParts = parser.getCollectedParts();
1823
+ if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
1824
+ kind: part.kind,
1825
+ ...part.kind === "text" && { text: part.text },
1826
+ ...part.kind === "data" && { data: part.data }
1827
+ })) };
1828
+ return {
1829
+ ...response,
1830
+ object: structuredResponse.object,
1831
+ textResponse: JSON.stringify(structuredResponse.object, null, 2)
1832
+ };
1833
+ }
1834
+ async executeNonStreamingPhase2(structuredModelSettings, phase2Messages, dataComponentsSchema, phase2TimeoutMs, response) {
1835
+ const structuredResponse = await generateObject(withJsonPostProcessing({
1836
+ ...structuredModelSettings,
1837
+ messages: phase2Messages,
1838
+ schema: z.object({ dataComponents: z.array(dataComponentsSchema) }),
1839
+ experimental_telemetry: this.buildTelemetryConfig("structured_generation"),
1840
+ abortSignal: AbortSignal.timeout(phase2TimeoutMs)
1841
+ }));
1842
+ return {
1843
+ ...response,
1844
+ object: structuredResponse.object,
1845
+ textResponse: JSON.stringify(structuredResponse.object, null, 2)
1846
+ };
1847
+ }
1848
+ async formatFinalResponse(response, textResponse, sessionId, contextId) {
1849
+ let formattedContent = response.formattedContent || null;
1850
+ if (!formattedContent) {
1851
+ const session = toolSessionManager.getSession(sessionId);
1852
+ const responseFormatter = new ResponseFormatter(this.config.tenantId, {
1853
+ sessionId,
1854
+ taskId: session?.taskId,
1855
+ projectId: session?.projectId,
1856
+ contextId,
1857
+ artifactComponents: this.artifactComponents,
1858
+ streamRequestId: this.getStreamRequestId(),
1859
+ subAgentId: this.config.id
1860
+ });
1861
+ if (response.object) formattedContent = await responseFormatter.formatObjectResponse(response.object, contextId);
1862
+ else if (textResponse) formattedContent = await responseFormatter.formatResponse(textResponse, contextId);
1863
+ }
1864
+ return {
1865
+ ...response,
1866
+ formattedContent
1867
+ };
1868
+ }
1869
+ handleGenerationError(error, span) {
1870
+ if (this.currentCompressor) this.currentCompressor.fullCleanup();
1871
+ this.currentCompressor = null;
1872
+ const errorToThrow = error instanceof Error ? error : new Error(String(error));
1873
+ setSpanWithError$1(span, errorToThrow);
1874
+ span.end();
1875
+ throw errorToThrow;
1876
+ }
1877
+ /**
1878
+ * Public cleanup method for external lifecycle management (e.g., session cleanup)
1879
+ * Performs full cleanup of compression state when agent/session is ending
1880
+ */
1881
+ cleanupCompression() {
1882
+ if (this.currentCompressor) {
1883
+ this.currentCompressor.fullCleanup();
1884
+ this.currentCompressor = null;
1885
+ }
1886
+ }
1887
+ async processStreamEvents(streamResult, parser) {
1888
+ for await (const event of streamResult.fullStream) switch (event.type) {
1889
+ case "text-delta":
1890
+ await parser.processTextChunk(event.text);
1891
+ break;
1892
+ case "tool-call":
1893
+ parser.markToolResult();
1894
+ break;
1895
+ case "tool-result":
1896
+ parser.markToolResult();
1897
+ break;
1898
+ case "finish":
1899
+ if (event.finishReason === "tool-calls") parser.markToolResult();
1900
+ break;
1901
+ case "error": {
1902
+ if (event.error instanceof Error) throw event.error;
1903
+ const errorMessage = event.error?.error?.message;
1904
+ throw new Error(errorMessage);
1905
+ }
1906
+ }
1907
+ await parser.finalize();
1908
+ }
1909
+ formatStreamingResponse(response, parser) {
1910
+ const collectedParts = parser.getCollectedParts();
1911
+ if (collectedParts.length > 0) response.formattedContent = { parts: collectedParts.map((part) => ({
1912
+ kind: part.kind,
1913
+ ...part.kind === "text" && { text: part.text },
1914
+ ...part.kind === "data" && { data: part.data }
1915
+ })) };
1916
+ const streamedContent = parser.getAllStreamedContent();
1917
+ if (streamedContent.length > 0) response.streamedContent = { parts: streamedContent.map((part) => ({
1918
+ kind: part.kind,
1919
+ ...part.kind === "text" && { text: part.text },
1920
+ ...part.kind === "data" && { data: part.data }
1921
+ })) };
1922
+ return response;
1934
1923
  }
1935
1924
  };
1936
1925