@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.
@@ -11,6 +11,7 @@ import { TaskState, dbResultToMcpTool, generateId, getAgentById, getAgentWithDef
11
11
  const logger = getLogger("generateTaskHandler");
12
12
  const createTaskHandler = (config, credentialStoreRegistry) => {
13
13
  return async (task) => {
14
+ let agent;
14
15
  try {
15
16
  const userMessage = task.input.parts.filter((part) => part.text).map((part) => part.text).join(" ");
16
17
  if (!userMessage.trim()) return {
@@ -161,7 +162,7 @@ const createTaskHandler = (config, credentialStoreRegistry) => {
161
162
  }
162
163
  return mcpTool;
163
164
  })) ?? [];
164
- const agent = new Agent({
165
+ agent = new Agent({
165
166
  id: config.subAgentId,
166
167
  tenantId: config.tenantId,
167
168
  projectId: config.projectId,
@@ -360,6 +361,7 @@ const createTaskHandler = (config, credentialStoreRegistry) => {
360
361
  ...config.apiKey ? { apiKey: config.apiKey } : {}
361
362
  }
362
363
  });
364
+ agent.cleanupCompression();
363
365
  const stepContents = response.steps && Array.isArray(response.steps) ? response.steps.flatMap((step) => {
364
366
  return step.content && Array.isArray(step.content) ? step.content : [];
365
367
  }) : [];
@@ -410,7 +412,8 @@ const createTaskHandler = (config, credentialStoreRegistry) => {
410
412
  parts: [{
411
413
  kind: "data",
412
414
  data: artifactData
413
- }]
415
+ }],
416
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
414
417
  }]
415
418
  };
416
419
  }
@@ -431,11 +434,17 @@ const createTaskHandler = (config, credentialStoreRegistry) => {
431
434
  status: { state: TaskState.Completed },
432
435
  artifacts: [{
433
436
  artifactId: generateId(),
434
- parts
437
+ parts,
438
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
435
439
  }]
436
440
  };
437
441
  } catch (error) {
438
442
  console.error("Task handler error:", error);
443
+ try {
444
+ if (agent) agent.cleanupCompression();
445
+ } catch (cleanupError) {
446
+ logger.warn({ cleanupError }, "Failed to cleanup agent compression on error");
447
+ }
439
448
  const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
440
449
  const isConnectionRefused = errorMessage.includes("Connection refused. Please check if the MCP server is running.");
441
450
  return {
@@ -42,6 +42,10 @@ declare const executionLimitsDefaults: {
42
42
  readonly STREAM_TEXT_GAP_THRESHOLD_MS: 2000;
43
43
  readonly STREAM_MAX_LIFETIME_MS: 600000;
44
44
  readonly CONVERSATION_HISTORY_DEFAULT_LIMIT: 50;
45
+ readonly CONVERSATION_ARTIFACTS_LIMIT: 12;
46
+ readonly COMPRESSION_HARD_LIMIT: 120000;
47
+ readonly COMPRESSION_SAFETY_BUFFER: 20000;
48
+ readonly COMPRESSION_ENABLED: true;
45
49
  };
46
50
  //#endregion
47
51
  export { executionLimitsDefaults };
@@ -41,7 +41,11 @@ const executionLimitsDefaults = {
41
41
  STREAM_BUFFER_MAX_SIZE_BYTES: 5242880,
42
42
  STREAM_TEXT_GAP_THRESHOLD_MS: 2e3,
43
43
  STREAM_MAX_LIFETIME_MS: 6e5,
44
- CONVERSATION_HISTORY_DEFAULT_LIMIT: 50
44
+ CONVERSATION_HISTORY_DEFAULT_LIMIT: 50,
45
+ CONVERSATION_ARTIFACTS_LIMIT: 12,
46
+ COMPRESSION_HARD_LIMIT: 12e4,
47
+ COMPRESSION_SAFETY_BUFFER: 2e4,
48
+ COMPRESSION_ENABLED: true
45
49
  };
46
50
 
47
51
  //#endregion
@@ -1,6 +1,6 @@
1
1
  import { executionLimitsDefaults } from "./defaults.js";
2
2
 
3
3
  //#region src/constants/execution-limits/index.d.ts
4
- declare const AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS: 3, AGENT_EXECUTION_MAX_GENERATION_STEPS: 5, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING: 270000, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING: 90000, LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS: 90000, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS: 600000, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT: 30000, FUNCTION_TOOL_SANDBOX_VCPUS_DEFAULT: 4, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS: 300000, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT: 50, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES: 1048576, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS: 30000, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS: 60000, MCP_TOOL_REQUEST_TIMEOUT_MS_DEFAULT: 60000, DELEGATION_TOOL_BACKOFF_INITIAL_INTERVAL_MS: 100, DELEGATION_TOOL_BACKOFF_MAX_INTERVAL_MS: 10000, DELEGATION_TOOL_BACKOFF_EXPONENT: 2, DELEGATION_TOOL_BACKOFF_MAX_ELAPSED_TIME_MS: 20000, A2A_BACKOFF_INITIAL_INTERVAL_MS: 500, A2A_BACKOFF_MAX_INTERVAL_MS: 60000, A2A_BACKOFF_EXPONENT: 1.5, A2A_BACKOFF_MAX_ELAPSED_TIME_MS: 30000, ARTIFACT_GENERATION_MAX_RETRIES: 3, ARTIFACT_SESSION_MAX_PENDING: 100, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES: 3, ARTIFACT_GENERATION_BACKOFF_INITIAL_MS: 1000, ARTIFACT_GENERATION_BACKOFF_MAX_MS: 10000, SESSION_TOOL_RESULT_CACHE_TIMEOUT_MS: 300000, SESSION_CLEANUP_INTERVAL_MS: 60000, STATUS_UPDATE_DEFAULT_NUM_EVENTS: 1, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS: 2, STREAM_PARSER_MAX_SNAPSHOT_SIZE: 100, STREAM_PARSER_MAX_STREAMED_SIZE: 1000, STREAM_PARSER_MAX_COLLECTED_PARTS: 10000, STREAM_BUFFER_MAX_SIZE_BYTES: 5242880, STREAM_TEXT_GAP_THRESHOLD_MS: 2000, STREAM_MAX_LIFETIME_MS: 600000, CONVERSATION_HISTORY_DEFAULT_LIMIT: 50;
4
+ declare const AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS: 3, AGENT_EXECUTION_MAX_GENERATION_STEPS: 5, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING: 270000, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING: 90000, LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS: 90000, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS: 600000, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT: 30000, FUNCTION_TOOL_SANDBOX_VCPUS_DEFAULT: 4, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS: 300000, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT: 50, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES: 1048576, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS: 30000, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS: 60000, MCP_TOOL_REQUEST_TIMEOUT_MS_DEFAULT: 60000, DELEGATION_TOOL_BACKOFF_INITIAL_INTERVAL_MS: 100, DELEGATION_TOOL_BACKOFF_MAX_INTERVAL_MS: 10000, DELEGATION_TOOL_BACKOFF_EXPONENT: 2, DELEGATION_TOOL_BACKOFF_MAX_ELAPSED_TIME_MS: 20000, A2A_BACKOFF_INITIAL_INTERVAL_MS: 500, A2A_BACKOFF_MAX_INTERVAL_MS: 60000, A2A_BACKOFF_EXPONENT: 1.5, A2A_BACKOFF_MAX_ELAPSED_TIME_MS: 30000, ARTIFACT_GENERATION_MAX_RETRIES: 3, ARTIFACT_SESSION_MAX_PENDING: 100, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES: 3, ARTIFACT_GENERATION_BACKOFF_INITIAL_MS: 1000, ARTIFACT_GENERATION_BACKOFF_MAX_MS: 10000, SESSION_TOOL_RESULT_CACHE_TIMEOUT_MS: 300000, SESSION_CLEANUP_INTERVAL_MS: 60000, STATUS_UPDATE_DEFAULT_NUM_EVENTS: 1, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS: 2, STREAM_PARSER_MAX_SNAPSHOT_SIZE: 100, STREAM_PARSER_MAX_STREAMED_SIZE: 1000, STREAM_PARSER_MAX_COLLECTED_PARTS: 10000, STREAM_BUFFER_MAX_SIZE_BYTES: 5242880, STREAM_TEXT_GAP_THRESHOLD_MS: 2000, STREAM_MAX_LIFETIME_MS: 600000, CONVERSATION_HISTORY_DEFAULT_LIMIT: 50, CONVERSATION_ARTIFACTS_LIMIT: 12, COMPRESSION_HARD_LIMIT: 120000, COMPRESSION_SAFETY_BUFFER: 20000, COMPRESSION_ENABLED: true;
5
5
  //#endregion
6
- export { A2A_BACKOFF_EXPONENT, A2A_BACKOFF_INITIAL_INTERVAL_MS, A2A_BACKOFF_MAX_ELAPSED_TIME_MS, A2A_BACKOFF_MAX_INTERVAL_MS, AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS, AGENT_EXECUTION_MAX_GENERATION_STEPS, ARTIFACT_GENERATION_BACKOFF_INITIAL_MS, ARTIFACT_GENERATION_BACKOFF_MAX_MS, ARTIFACT_GENERATION_MAX_RETRIES, ARTIFACT_SESSION_MAX_PENDING, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES, CONVERSATION_HISTORY_DEFAULT_LIMIT, DELEGATION_TOOL_BACKOFF_EXPONENT, DELEGATION_TOOL_BACKOFF_INITIAL_INTERVAL_MS, DELEGATION_TOOL_BACKOFF_MAX_ELAPSED_TIME_MS, DELEGATION_TOOL_BACKOFF_MAX_INTERVAL_MS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS, 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, MCP_TOOL_REQUEST_TIMEOUT_MS_DEFAULT, SESSION_CLEANUP_INTERVAL_MS, SESSION_TOOL_RESULT_CACHE_TIMEOUT_MS, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS, STATUS_UPDATE_DEFAULT_NUM_EVENTS, STREAM_BUFFER_MAX_SIZE_BYTES, STREAM_MAX_LIFETIME_MS, STREAM_PARSER_MAX_COLLECTED_PARTS, STREAM_PARSER_MAX_SNAPSHOT_SIZE, STREAM_PARSER_MAX_STREAMED_SIZE, STREAM_TEXT_GAP_THRESHOLD_MS, executionLimitsDefaults };
6
+ export { A2A_BACKOFF_EXPONENT, A2A_BACKOFF_INITIAL_INTERVAL_MS, A2A_BACKOFF_MAX_ELAPSED_TIME_MS, A2A_BACKOFF_MAX_INTERVAL_MS, AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS, AGENT_EXECUTION_MAX_GENERATION_STEPS, ARTIFACT_GENERATION_BACKOFF_INITIAL_MS, ARTIFACT_GENERATION_BACKOFF_MAX_MS, ARTIFACT_GENERATION_MAX_RETRIES, ARTIFACT_SESSION_MAX_PENDING, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES, COMPRESSION_ENABLED, COMPRESSION_HARD_LIMIT, COMPRESSION_SAFETY_BUFFER, CONVERSATION_ARTIFACTS_LIMIT, CONVERSATION_HISTORY_DEFAULT_LIMIT, DELEGATION_TOOL_BACKOFF_EXPONENT, DELEGATION_TOOL_BACKOFF_INITIAL_INTERVAL_MS, DELEGATION_TOOL_BACKOFF_MAX_ELAPSED_TIME_MS, DELEGATION_TOOL_BACKOFF_MAX_INTERVAL_MS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS, 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, MCP_TOOL_REQUEST_TIMEOUT_MS_DEFAULT, SESSION_CLEANUP_INTERVAL_MS, SESSION_TOOL_RESULT_CACHE_TIMEOUT_MS, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS, STATUS_UPDATE_DEFAULT_NUM_EVENTS, STREAM_BUFFER_MAX_SIZE_BYTES, STREAM_MAX_LIFETIME_MS, STREAM_PARSER_MAX_COLLECTED_PARTS, STREAM_PARSER_MAX_SNAPSHOT_SIZE, STREAM_PARSER_MAX_STREAMED_SIZE, STREAM_TEXT_GAP_THRESHOLD_MS, executionLimitsDefaults };
@@ -4,13 +4,18 @@ import { loadEnvironmentFiles } from "@inkeep/agents-core";
4
4
 
5
5
  //#region src/constants/execution-limits/index.ts
6
6
  loadEnvironmentFiles();
7
- const constantsSchema = z.object(Object.fromEntries(Object.keys(executionLimitsDefaults).map((key) => [`AGENTS_${key}`, z.coerce.number().optional()])));
7
+ const constantsSchema = z.object(Object.fromEntries(Object.keys(executionLimitsDefaults).map((key) => {
8
+ const defaultValue = executionLimitsDefaults[key];
9
+ const envKey = `AGENTS_${key}`;
10
+ if (typeof defaultValue === "boolean") return [envKey, z.coerce.boolean().optional()];
11
+ else return [envKey, z.coerce.number().optional()];
12
+ })));
8
13
  const parseConstants = () => {
9
14
  const envOverrides = constantsSchema.parse(process.env);
10
15
  return Object.fromEntries(Object.entries(executionLimitsDefaults).map(([key, defaultValue]) => [key, envOverrides[`AGENTS_${key}`] ?? defaultValue]));
11
16
  };
12
17
  const constants = parseConstants();
13
- const { AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS, AGENT_EXECUTION_MAX_GENERATION_STEPS, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING, LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_VCPUS_DEFAULT, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS, MCP_TOOL_REQUEST_TIMEOUT_MS_DEFAULT, DELEGATION_TOOL_BACKOFF_INITIAL_INTERVAL_MS, DELEGATION_TOOL_BACKOFF_MAX_INTERVAL_MS, DELEGATION_TOOL_BACKOFF_EXPONENT, DELEGATION_TOOL_BACKOFF_MAX_ELAPSED_TIME_MS, A2A_BACKOFF_INITIAL_INTERVAL_MS, A2A_BACKOFF_MAX_INTERVAL_MS, A2A_BACKOFF_EXPONENT, A2A_BACKOFF_MAX_ELAPSED_TIME_MS, ARTIFACT_GENERATION_MAX_RETRIES, ARTIFACT_SESSION_MAX_PENDING, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES, ARTIFACT_GENERATION_BACKOFF_INITIAL_MS, ARTIFACT_GENERATION_BACKOFF_MAX_MS, SESSION_TOOL_RESULT_CACHE_TIMEOUT_MS, SESSION_CLEANUP_INTERVAL_MS, STATUS_UPDATE_DEFAULT_NUM_EVENTS, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS, STREAM_PARSER_MAX_SNAPSHOT_SIZE, STREAM_PARSER_MAX_STREAMED_SIZE, STREAM_PARSER_MAX_COLLECTED_PARTS, STREAM_BUFFER_MAX_SIZE_BYTES, STREAM_TEXT_GAP_THRESHOLD_MS, STREAM_MAX_LIFETIME_MS, CONVERSATION_HISTORY_DEFAULT_LIMIT } = constants;
18
+ const { AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS, AGENT_EXECUTION_MAX_GENERATION_STEPS, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_STREAMING, LLM_GENERATION_FIRST_CALL_TIMEOUT_MS_NON_STREAMING, LLM_GENERATION_SUBSEQUENT_CALL_TIMEOUT_MS, LLM_GENERATION_MAX_ALLOWED_TIMEOUT_MS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_VCPUS_DEFAULT, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS, MCP_TOOL_REQUEST_TIMEOUT_MS_DEFAULT, DELEGATION_TOOL_BACKOFF_INITIAL_INTERVAL_MS, DELEGATION_TOOL_BACKOFF_MAX_INTERVAL_MS, DELEGATION_TOOL_BACKOFF_EXPONENT, DELEGATION_TOOL_BACKOFF_MAX_ELAPSED_TIME_MS, A2A_BACKOFF_INITIAL_INTERVAL_MS, A2A_BACKOFF_MAX_INTERVAL_MS, A2A_BACKOFF_EXPONENT, A2A_BACKOFF_MAX_ELAPSED_TIME_MS, ARTIFACT_GENERATION_MAX_RETRIES, ARTIFACT_SESSION_MAX_PENDING, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES, ARTIFACT_GENERATION_BACKOFF_INITIAL_MS, ARTIFACT_GENERATION_BACKOFF_MAX_MS, SESSION_TOOL_RESULT_CACHE_TIMEOUT_MS, SESSION_CLEANUP_INTERVAL_MS, STATUS_UPDATE_DEFAULT_NUM_EVENTS, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS, STREAM_PARSER_MAX_SNAPSHOT_SIZE, STREAM_PARSER_MAX_STREAMED_SIZE, STREAM_PARSER_MAX_COLLECTED_PARTS, STREAM_BUFFER_MAX_SIZE_BYTES, STREAM_TEXT_GAP_THRESHOLD_MS, STREAM_MAX_LIFETIME_MS, CONVERSATION_HISTORY_DEFAULT_LIMIT, CONVERSATION_ARTIFACTS_LIMIT, COMPRESSION_HARD_LIMIT, COMPRESSION_SAFETY_BUFFER, COMPRESSION_ENABLED } = constants;
14
19
 
15
20
  //#endregion
16
- export { A2A_BACKOFF_EXPONENT, A2A_BACKOFF_INITIAL_INTERVAL_MS, A2A_BACKOFF_MAX_ELAPSED_TIME_MS, A2A_BACKOFF_MAX_INTERVAL_MS, AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS, AGENT_EXECUTION_MAX_GENERATION_STEPS, ARTIFACT_GENERATION_BACKOFF_INITIAL_MS, ARTIFACT_GENERATION_BACKOFF_MAX_MS, ARTIFACT_GENERATION_MAX_RETRIES, ARTIFACT_SESSION_MAX_PENDING, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES, CONVERSATION_HISTORY_DEFAULT_LIMIT, DELEGATION_TOOL_BACKOFF_EXPONENT, DELEGATION_TOOL_BACKOFF_INITIAL_INTERVAL_MS, DELEGATION_TOOL_BACKOFF_MAX_ELAPSED_TIME_MS, DELEGATION_TOOL_BACKOFF_MAX_INTERVAL_MS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS, 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, MCP_TOOL_REQUEST_TIMEOUT_MS_DEFAULT, SESSION_CLEANUP_INTERVAL_MS, SESSION_TOOL_RESULT_CACHE_TIMEOUT_MS, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS, STATUS_UPDATE_DEFAULT_NUM_EVENTS, STREAM_BUFFER_MAX_SIZE_BYTES, STREAM_MAX_LIFETIME_MS, STREAM_PARSER_MAX_COLLECTED_PARTS, STREAM_PARSER_MAX_SNAPSHOT_SIZE, STREAM_PARSER_MAX_STREAMED_SIZE, STREAM_TEXT_GAP_THRESHOLD_MS, executionLimitsDefaults };
21
+ export { A2A_BACKOFF_EXPONENT, A2A_BACKOFF_INITIAL_INTERVAL_MS, A2A_BACKOFF_MAX_ELAPSED_TIME_MS, A2A_BACKOFF_MAX_INTERVAL_MS, AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS, AGENT_EXECUTION_MAX_GENERATION_STEPS, ARTIFACT_GENERATION_BACKOFF_INITIAL_MS, ARTIFACT_GENERATION_BACKOFF_MAX_MS, ARTIFACT_GENERATION_MAX_RETRIES, ARTIFACT_SESSION_MAX_PENDING, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES, COMPRESSION_ENABLED, COMPRESSION_HARD_LIMIT, COMPRESSION_SAFETY_BUFFER, CONVERSATION_ARTIFACTS_LIMIT, CONVERSATION_HISTORY_DEFAULT_LIMIT, DELEGATION_TOOL_BACKOFF_EXPONENT, DELEGATION_TOOL_BACKOFF_INITIAL_INTERVAL_MS, DELEGATION_TOOL_BACKOFF_MAX_ELAPSED_TIME_MS, DELEGATION_TOOL_BACKOFF_MAX_INTERVAL_MS, FUNCTION_TOOL_EXECUTION_TIMEOUT_MS_DEFAULT, FUNCTION_TOOL_SANDBOX_CLEANUP_INTERVAL_MS, FUNCTION_TOOL_SANDBOX_MAX_OUTPUT_SIZE_BYTES, FUNCTION_TOOL_SANDBOX_MAX_USE_COUNT, FUNCTION_TOOL_SANDBOX_POOL_TTL_MS, FUNCTION_TOOL_SANDBOX_QUEUE_WAIT_TIMEOUT_MS, 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, MCP_TOOL_REQUEST_TIMEOUT_MS_DEFAULT, SESSION_CLEANUP_INTERVAL_MS, SESSION_TOOL_RESULT_CACHE_TIMEOUT_MS, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS, STATUS_UPDATE_DEFAULT_NUM_EVENTS, STREAM_BUFFER_MAX_SIZE_BYTES, STREAM_MAX_LIFETIME_MS, STREAM_PARSER_MAX_COLLECTED_PARTS, STREAM_PARSER_MAX_SNAPSHOT_SIZE, STREAM_PARSER_MAX_STREAMED_SIZE, STREAM_TEXT_GAP_THRESHOLD_MS, executionLimitsDefaults };
@@ -1,7 +1,7 @@
1
1
  import { AgentConversationHistoryConfig, Artifact, ConversationHistoryConfig, ConversationScopeOptions } from "@inkeep/agents-core";
2
2
 
3
3
  //#region src/data/conversations.d.ts
4
-
4
+ declare const compressionLocks: Map<string, Promise<any>>;
5
5
  /**
6
6
  * Creates default conversation history configuration
7
7
  * @param mode - The conversation history mode ('full' | 'scoped' | 'none')
@@ -64,7 +64,33 @@ declare function getFormattedConversationHistory({
64
64
  conversationId,
65
65
  currentMessage,
66
66
  options,
67
- filters
67
+ filters,
68
+ sessionId,
69
+ summarizerModel
70
+ }: {
71
+ tenantId: string;
72
+ projectId: string;
73
+ conversationId: string;
74
+ currentMessage?: string;
75
+ options?: ConversationHistoryConfig;
76
+ filters?: ConversationScopeOptions;
77
+ sessionId?: string;
78
+ summarizerModel?: any;
79
+ }): Promise<string>;
80
+ /**
81
+ * Modern conversation history retrieval with compression support
82
+ * Replaces getFormattedConversationHistory with built-in compression when needed
83
+ */
84
+ declare function getConversationHistoryWithCompression({
85
+ tenantId,
86
+ projectId,
87
+ conversationId,
88
+ currentMessage,
89
+ options,
90
+ filters,
91
+ summarizerModel,
92
+ streamRequestId,
93
+ fullContextSize
68
94
  }: {
69
95
  tenantId: string;
70
96
  projectId: string;
@@ -72,7 +98,21 @@ declare function getFormattedConversationHistory({
72
98
  currentMessage?: string;
73
99
  options?: ConversationHistoryConfig;
74
100
  filters?: ConversationScopeOptions;
101
+ summarizerModel?: any;
102
+ streamRequestId?: string;
103
+ fullContextSize?: number;
75
104
  }): Promise<string>;
105
+ /**
106
+ * Apply conversation compression using the BaseCompressor infrastructure
107
+ */
108
+ declare function compressConversationIfNeeded(messages: any[], params: {
109
+ conversationId: string;
110
+ tenantId: string;
111
+ projectId: string;
112
+ summarizerModel: any;
113
+ streamRequestId?: string;
114
+ fullContextSize?: number;
115
+ }): Promise<any[]>;
76
116
  /**
77
117
  * Get artifacts that are within the scope of the conversation history
78
118
  * Only returns artifacts from messages that are actually visible to the LLM
@@ -85,4 +125,4 @@ declare function getConversationScopedArtifacts(params: {
85
125
  historyConfig: AgentConversationHistoryConfig;
86
126
  }): Promise<Artifact[]>;
87
127
  //#endregion
88
- export { createDefaultConversationHistoryConfig, getConversationScopedArtifacts, getFormattedConversationHistory, getFullConversationContext, getScopedHistory, getUserFacingHistory, saveA2AMessageResponse };
128
+ export { compressConversationIfNeeded, compressionLocks, createDefaultConversationHistoryConfig, getConversationHistoryWithCompression, getConversationScopedArtifacts, getFormattedConversationHistory, getFullConversationContext, getScopedHistory, getUserFacingHistory, saveA2AMessageResponse };
@@ -1,8 +1,13 @@
1
+ import { getLogger } from "../logger.js";
1
2
  import dbClient_default from "./db/dbClient.js";
2
- import { CONVERSATION_HISTORY_DEFAULT_LIMIT as CONVERSATION_HISTORY_DEFAULT_LIMIT$1 } from "../constants/execution-limits/index.js";
3
+ import { CONVERSATION_ARTIFACTS_LIMIT, CONVERSATION_HISTORY_DEFAULT_LIMIT as CONVERSATION_HISTORY_DEFAULT_LIMIT$1 } from "../constants/execution-limits/index.js";
4
+ import { getCompressionConfigForModel } from "../utils/model-context-utils.js";
5
+ import { ConversationCompressor } from "../services/ConversationCompressor.js";
3
6
  import { CONVERSATION_HISTORY_MAX_OUTPUT_TOKENS_DEFAULT, createMessage, generateId, getConversationHistory } from "@inkeep/agents-core";
4
7
 
5
8
  //#region src/data/conversations.ts
9
+ const logger = getLogger("conversations");
10
+ const compressionLocks = /* @__PURE__ */ new Map();
6
11
  /**
7
12
  * Creates default conversation history configuration
8
13
  * @param mode - The conversation history mode ('full' | 'scoped' | 'none')
@@ -64,14 +69,39 @@ async function saveA2AMessageResponse(response, params) {
64
69
  */
65
70
  async function getScopedHistory({ tenantId, projectId, conversationId, filters, options }) {
66
71
  try {
67
- const messages = await getConversationHistory(dbClient_default)({
72
+ const allMessages = await getConversationHistory(dbClient_default)({
68
73
  scopes: {
69
74
  tenantId,
70
75
  projectId
71
76
  },
72
77
  conversationId,
73
- options
78
+ options: {
79
+ ...options,
80
+ limit: 1e4,
81
+ includeInternal: true,
82
+ maxOutputTokens: void 0
83
+ }
74
84
  });
85
+ const compressionSummaries = allMessages.filter((msg) => msg.messageType === "compression_summary" && msg.metadata?.compressionType === "conversation_history");
86
+ const latestCompressionSummary = compressionSummaries.length > 0 ? compressionSummaries.reduce((latest, current) => new Date(current.createdAt) > new Date(latest.createdAt) ? current : latest) : null;
87
+ let messages;
88
+ if (latestCompressionSummary) {
89
+ const summaryDate = new Date(latestCompressionSummary.createdAt);
90
+ messages = [latestCompressionSummary, ...allMessages.filter((msg) => new Date(msg.createdAt) > summaryDate && msg.messageType !== "compression_summary")];
91
+ logger.debug({
92
+ conversationId,
93
+ latestCompressionSummaryId: latestCompressionSummary.id,
94
+ summaryDate: summaryDate.toISOString(),
95
+ messagesAfterCompression: messages.length - 1,
96
+ totalMessages: allMessages.length
97
+ }, "Retrieved conversation with compression summary");
98
+ } else {
99
+ messages = allMessages;
100
+ logger.debug({
101
+ conversationId,
102
+ totalMessages: messages.length
103
+ }, "Retrieved conversation without compression summary");
104
+ }
75
105
  if (!filters || !filters.subAgentId && !filters.taskId && !filters.delegationId && filters.isDelegated === void 0) return messages;
76
106
  return messages.filter((msg) => {
77
107
  if (msg.role === "user") return true;
@@ -139,7 +169,7 @@ async function getFullConversationContext(tenantId, projectId, conversationId, m
139
169
  /**
140
170
  * Get formatted conversation history for a2a
141
171
  */
142
- async function getFormattedConversationHistory({ tenantId, projectId, conversationId, currentMessage, options, filters }) {
172
+ async function getFormattedConversationHistory({ tenantId, projectId, conversationId, currentMessage, options, filters, sessionId, summarizerModel }) {
143
173
  const conversationHistory = await getScopedHistory({
144
174
  tenantId,
145
175
  projectId,
@@ -152,12 +182,284 @@ async function getFormattedConversationHistory({ tenantId, projectId, conversati
152
182
  if (conversationHistory[conversationHistory.length - 1].content.text === currentMessage) messagesToFormat = conversationHistory.slice(0, -1);
153
183
  }
154
184
  if (!messagesToFormat.length) return "";
155
- return `<conversation_history>\n${messagesToFormat.map((msg) => {
185
+ let finalMessagesToFormat = messagesToFormat;
186
+ if (sessionId && summarizerModel) finalMessagesToFormat = await compressConversationIfNeeded(messagesToFormat, {
187
+ conversationId,
188
+ tenantId,
189
+ projectId,
190
+ summarizerModel,
191
+ streamRequestId: sessionId
192
+ });
193
+ return `<conversation_history>\n${finalMessagesToFormat.map((msg) => {
194
+ let roleLabel;
195
+ if (msg.role === "user") roleLabel = "user";
196
+ else if (msg.role === "agent" && (msg.messageType === "a2a-request" || msg.messageType === "a2a-response")) roleLabel = `${msg.fromSubAgentId || msg.fromExternalAgentId || "unknown"} to ${msg.toSubAgentId || msg.toExternalAgentId || "unknown"}`;
197
+ else if (msg.role === "agent" && msg.messageType === "chat") roleLabel = `${msg.fromSubAgentId || "unknown"} to User`;
198
+ else if (msg.role === "assistant" && msg.messageType === "tool-result") roleLabel = `${msg.fromSubAgentId || "unknown"} tool: ${msg.metadata?.a2a_metadata?.toolName || "unknown"}`;
199
+ else roleLabel = msg.role || "system";
200
+ return `${roleLabel}: """${msg.content.text}"""`;
201
+ }).join("\n")}\n</conversation_history>\n`;
202
+ }
203
+ /**
204
+ * Modern conversation history retrieval with compression support
205
+ * Replaces getFormattedConversationHistory with built-in compression when needed
206
+ */
207
+ async function getConversationHistoryWithCompression({ tenantId, projectId, conversationId, currentMessage, options, filters, summarizerModel, streamRequestId, fullContextSize }) {
208
+ const conversationHistory = await getScopedHistory({
209
+ tenantId,
210
+ projectId,
211
+ conversationId,
212
+ filters,
213
+ options: {
214
+ ...options ?? createDefaultConversationHistoryConfig(),
215
+ includeInternal: true,
216
+ maxOutputTokens: void 0
217
+ }
218
+ });
219
+ let messagesToFormat = conversationHistory;
220
+ if (currentMessage && conversationHistory.length > 0) {
221
+ if (conversationHistory[conversationHistory.length - 1].content.text === currentMessage) messagesToFormat = conversationHistory.slice(0, -1);
222
+ }
223
+ if (!messagesToFormat.length) return "";
224
+ if (summarizerModel) {
225
+ const compressionInfo = getCompressionConfigForModel(summarizerModel, .5);
226
+ const estimatedTokens = messagesToFormat.reduce((total, msg) => {
227
+ const text = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
228
+ return total + Math.ceil(text.length / 4);
229
+ }, 0);
230
+ const remaining = compressionInfo.hardLimit - estimatedTokens;
231
+ const compressionNeeded = remaining <= compressionInfo.safetyBuffer;
232
+ const contextWindowUtilization = compressionInfo.modelContextInfo.contextWindow ? (estimatedTokens / compressionInfo.modelContextInfo.contextWindow * 100).toFixed(1) : "unknown";
233
+ logger.info({
234
+ conversationId,
235
+ model: summarizerModel.model,
236
+ modelContextWindow: compressionInfo.modelContextInfo.contextWindow,
237
+ currentTokens: estimatedTokens,
238
+ hardLimit: compressionInfo.hardLimit,
239
+ safetyBuffer: compressionInfo.safetyBuffer,
240
+ remaining,
241
+ compressionNeeded,
242
+ contextWindowUtilization: `${contextWindowUtilization}%`,
243
+ messageCount: messagesToFormat.length,
244
+ source: compressionInfo.source
245
+ }, "Conversation history fetch - model context analysis");
246
+ const compressionSummary = messagesToFormat.find((msg) => msg.messageType === "compression_summary" && msg.metadata?.compressionType === "conversation_history");
247
+ if (compressionSummary) {
248
+ const messagesAfterCompression = messagesToFormat.filter((msg) => new Date(msg.createdAt) > new Date(compressionSummary.createdAt) && msg.messageType !== "compression_summary");
249
+ if (messagesAfterCompression.length >= 10) {
250
+ logger.info({
251
+ conversationId,
252
+ messagesAfterLastCompression: messagesAfterCompression.length,
253
+ lastCompressionDate: compressionSummary.createdAt
254
+ }, "Checking if re-compression needed for new messages");
255
+ const newMessagesCompressed = await compressConversationIfNeeded(messagesAfterCompression, {
256
+ conversationId,
257
+ tenantId,
258
+ projectId,
259
+ summarizerModel,
260
+ streamRequestId,
261
+ fullContextSize
262
+ });
263
+ if (newMessagesCompressed.length === 1 && newMessagesCompressed[0].messageType === "compression_summary") {
264
+ messagesToFormat = [compressionSummary, ...newMessagesCompressed];
265
+ logger.info({
266
+ conversationId,
267
+ totalCompressedMessages: messagesToFormat.length
268
+ }, "Re-compression completed - combined with existing summary");
269
+ } else messagesToFormat = [compressionSummary, ...messagesAfterCompression];
270
+ }
271
+ } else {
272
+ messagesToFormat.length;
273
+ messagesToFormat = await compressConversationIfNeeded(messagesToFormat, {
274
+ conversationId,
275
+ tenantId,
276
+ projectId,
277
+ summarizerModel,
278
+ streamRequestId,
279
+ fullContextSize
280
+ });
281
+ }
282
+ const compressedTokens = messagesToFormat.reduce((total, msg) => {
283
+ const text = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
284
+ return total + Math.ceil(text.length / 4);
285
+ }, 0);
286
+ const compressionSummaryMessages = messagesToFormat.filter((msg) => msg.messageType === "compression_summary");
287
+ if (compressionSummaryMessages.length > 0) logger.info({
288
+ conversationId,
289
+ finalMessages: messagesToFormat.length,
290
+ compressionSummaries: compressionSummaryMessages.length,
291
+ finalTokens: compressedTokens,
292
+ contextWindowUtilization: compressionInfo.modelContextInfo.contextWindow ? `${(compressedTokens / compressionInfo.modelContextInfo.contextWindow * 100).toFixed(1)}%` : "unknown"
293
+ }, "Final conversation history with compression summaries");
294
+ }
295
+ return formatMessagesAsConversationHistory(messagesToFormat);
296
+ }
297
+ /**
298
+ * Apply conversation compression using the BaseCompressor infrastructure
299
+ */
300
+ async function compressConversationIfNeeded(messages, params) {
301
+ const { conversationId, tenantId, projectId, summarizerModel, streamRequestId } = params;
302
+ const lockKey = `${conversationId}_${tenantId}_${projectId}`;
303
+ if (compressionLocks.has(lockKey)) {
304
+ logger.debug({ conversationId }, "Waiting for existing compression to complete");
305
+ await compressionLocks.get(lockKey);
306
+ return messages;
307
+ }
308
+ const compressionPromise = performActualCompression(messages, params);
309
+ compressionLocks.set(lockKey, compressionPromise);
310
+ try {
311
+ return await compressionPromise;
312
+ } finally {
313
+ compressionLocks.delete(lockKey);
314
+ }
315
+ }
316
+ async function performActualCompression(messages, params) {
317
+ const { conversationId, tenantId, projectId, summarizerModel, streamRequestId } = params;
318
+ const compressor = new ConversationCompressor(streamRequestId || conversationId, conversationId, tenantId, projectId, void 0, summarizerModel);
319
+ if (!compressor.isCompressionNeeded(messages)) return messages;
320
+ logger.info({
321
+ conversationId,
322
+ messageCount: messages.length
323
+ }, "Applying conversation-level compression");
324
+ try {
325
+ const compressionResult = await compressor.safeCompress(messages, params.fullContextSize);
326
+ if (compressionResult.summary) {
327
+ const compressionMessage = await createMessage(dbClient_default)({
328
+ id: generateId(),
329
+ tenantId,
330
+ projectId,
331
+ conversationId,
332
+ role: "system",
333
+ content: { text: buildCompressionSummaryMessage(compressionResult.summary, compressionResult.artifactIds) },
334
+ visibility: "internal",
335
+ messageType: "compression_summary",
336
+ metadata: { a2a_metadata: {
337
+ compressionType: "conversation_history",
338
+ artifactIds: compressionResult.artifactIds,
339
+ originalMessageCount: messages.length,
340
+ compressedAt: (/* @__PURE__ */ new Date()).toISOString(),
341
+ summaryData: compressionResult.summary
342
+ } }
343
+ });
344
+ logger.debug({
345
+ conversationId,
346
+ originalMessageCount: messages.length,
347
+ artifactCount: compressionResult.artifactIds?.length || 0,
348
+ compressionMessageId: compressionMessage.id
349
+ }, "Conversation compression saved to messages table");
350
+ compressor.fullCleanup();
351
+ return [compressionMessage];
352
+ }
353
+ compressor.fullCleanup();
354
+ return messages;
355
+ } catch (error) {
356
+ logger.error({
357
+ conversationId,
358
+ error: error instanceof Error ? error.message : String(error)
359
+ }, "Conversation compression failed, using original messages");
360
+ compressor.fullCleanup();
361
+ return messages;
362
+ }
363
+ }
364
+ /**
365
+ * Build a summary message for compressed conversation content
366
+ */
367
+ function buildCompressionSummaryMessage(summary, artifactIds) {
368
+ const parts = [];
369
+ parts.push("=== CONVERSATION SUMMARY ===");
370
+ parts.push("Previous conversation has been compressed to save context space.");
371
+ parts.push("");
372
+ if (summary.conversation_overview) parts.push(`📋 Overview: ${summary.conversation_overview}`);
373
+ if (summary.user_goals?.primary) {
374
+ parts.push(`🎯 Primary Goal: ${summary.user_goals.primary}`);
375
+ if (summary.user_goals.secondary && summary.user_goals.secondary.length > 0) {
376
+ parts.push(`🎯 Secondary Goals:`);
377
+ summary.user_goals.secondary.forEach((goal) => parts.push(` • ${goal}`));
378
+ }
379
+ }
380
+ if (summary.key_outcomes) {
381
+ if (summary.key_outcomes.completed && summary.key_outcomes.completed.length > 0) {
382
+ parts.push(`✅ Completed:`);
383
+ summary.key_outcomes.completed.forEach((item) => parts.push(` • ${item}`));
384
+ }
385
+ if (summary.key_outcomes.discoveries && summary.key_outcomes.discoveries.length > 0) {
386
+ parts.push(`💡 Key Discoveries:`);
387
+ summary.key_outcomes.discoveries.forEach((discovery) => parts.push(` • ${discovery}`));
388
+ }
389
+ if (summary.key_outcomes.partial && summary.key_outcomes.partial.length > 0) {
390
+ parts.push(`⏳ In Progress:`);
391
+ summary.key_outcomes.partial.forEach((item) => parts.push(` • ${item}`));
392
+ }
393
+ }
394
+ if (summary.context_for_continuation) {
395
+ if (summary.context_for_continuation.current_state) parts.push(`📍 Current State: ${summary.context_for_continuation.current_state}`);
396
+ if (summary.context_for_continuation.next_logical_steps && summary.context_for_continuation.next_logical_steps.length > 0) {
397
+ parts.push(`📝 Next Steps:`);
398
+ summary.context_for_continuation.next_logical_steps.forEach((step) => parts.push(` • ${step}`));
399
+ }
400
+ if (summary.context_for_continuation.important_context && summary.context_for_continuation.important_context.length > 0) {
401
+ parts.push(`🔑 Key Context:`);
402
+ summary.context_for_continuation.important_context.forEach((context) => parts.push(` • ${context}`));
403
+ }
404
+ }
405
+ if (summary.technical_context) {
406
+ if (summary.technical_context.technologies && summary.technical_context.technologies.length > 0) parts.push(`🔧 Technologies: ${summary.technical_context.technologies.join(", ")}`);
407
+ if (summary.technical_context.issues_encountered && summary.technical_context.issues_encountered.length > 0) {
408
+ parts.push(`⚠️ Issues Encountered:`);
409
+ summary.technical_context.issues_encountered.forEach((issue) => parts.push(` • ${issue}`));
410
+ }
411
+ if (summary.technical_context.solutions_applied && summary.technical_context.solutions_applied.length > 0) {
412
+ parts.push(`✨ Solutions Applied:`);
413
+ summary.technical_context.solutions_applied.forEach((solution) => parts.push(` • ${solution}`));
414
+ }
415
+ }
416
+ if (summary.high_level) parts.push(`📋 Overview: ${summary.high_level}`);
417
+ if (summary.user_intent) parts.push(`🎯 User Goal: ${summary.user_intent}`);
418
+ if (summary.decisions && summary.decisions.length > 0) {
419
+ parts.push(`✅ Key Decisions Made:`);
420
+ summary.decisions.forEach((decision) => parts.push(` • ${decision}`));
421
+ }
422
+ if (summary.next_steps && summary.next_steps.length > 0) {
423
+ parts.push(`📝 Planned Next Steps:`);
424
+ summary.next_steps.forEach((step) => parts.push(` • ${step}`));
425
+ }
426
+ if (summary.open_questions && summary.open_questions.length > 0) {
427
+ parts.push(`❓ Outstanding Questions:`);
428
+ summary.open_questions.forEach((question) => parts.push(` • ${question}`));
429
+ }
430
+ if (summary.conversation_artifacts && summary.conversation_artifacts.length > 0) {
431
+ parts.push(`💾 Research Artifacts: ${summary.conversation_artifacts.length} created from previous work`);
432
+ summary.conversation_artifacts.forEach((artifact) => {
433
+ parts.push(` [ARTIFACT: ${artifact.id}]`);
434
+ parts.push(` 📋 ${artifact.name || "Research Data"}`);
435
+ if (artifact.content_summary) parts.push(` 📝 ${artifact.content_summary}`);
436
+ if (artifact.tool_name && artifact.tool_name !== "unknown") parts.push(` 🔧 Source: ${artifact.tool_name}`);
437
+ parts.push(` 🔗 Reference: <artifact:ref id="${artifact.id}" tool_call_id="${artifact.tool_call_id}" />`);
438
+ parts.push("");
439
+ });
440
+ } else if (artifactIds && artifactIds.length > 0) {
441
+ parts.push(`💾 Research Artifacts: ${artifactIds.length} created from previous work`);
442
+ artifactIds.forEach((artifactId) => {
443
+ parts.push(` [ARTIFACT: ${artifactId}]`);
444
+ parts.push(` 🔗 Reference: <artifact:ref id="${artifactId}" />`);
445
+ });
446
+ }
447
+ parts.push("");
448
+ parts.push("=== END SUMMARY ===");
449
+ parts.push("Recent conversation continues below...");
450
+ return parts.join("\n");
451
+ }
452
+ /**
453
+ * Format messages into conversation history string (extracted from legacy method)
454
+ */
455
+ function formatMessagesAsConversationHistory(messages) {
456
+ return `<conversation_history>\n${messages.map((msg) => {
156
457
  let roleLabel;
157
458
  if (msg.role === "user") roleLabel = "user";
158
459
  else if (msg.role === "agent" && (msg.messageType === "a2a-request" || msg.messageType === "a2a-response")) roleLabel = `${msg.fromSubAgentId || msg.fromExternalAgentId || "unknown"} to ${msg.toSubAgentId || msg.toExternalAgentId || "unknown"}`;
159
460
  else if (msg.role === "agent" && msg.messageType === "chat") roleLabel = `${msg.fromSubAgentId || "unknown"} to User`;
160
461
  else if (msg.role === "assistant" && msg.messageType === "tool-result") roleLabel = `${msg.fromSubAgentId || "unknown"} tool: ${msg.metadata?.a2a_metadata?.toolName || "unknown"}`;
462
+ else if (msg.role === "system") roleLabel = "system";
161
463
  else roleLabel = msg.role || "system";
162
464
  return `${roleLabel}: """${msg.content.text}"""`;
163
465
  }).join("\n")}\n</conversation_history>\n`;
@@ -194,14 +496,19 @@ async function getConversationScopedArtifacts(params) {
194
496
  });
195
497
  referenceArtifacts.push(...artifacts);
196
498
  }
197
- (await import("../logger.js")).getLogger("conversations").debug({
499
+ const logger$1 = (await import("../logger.js")).getLogger("conversations");
500
+ const ARTIFACT_COUNT_LIMIT = CONVERSATION_ARTIFACTS_LIMIT;
501
+ const limitedArtifacts = referenceArtifacts.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, ARTIFACT_COUNT_LIMIT);
502
+ logger$1.debug({
198
503
  conversationId,
199
504
  visibleMessages: visibleMessages.length,
200
505
  visibleTasks: visibleTaskIds.length,
201
- artifacts: referenceArtifacts.length,
506
+ totalArtifacts: referenceArtifacts.length,
507
+ limitedArtifacts: limitedArtifacts.length,
508
+ artifactLimit: ARTIFACT_COUNT_LIMIT,
202
509
  historyMode: historyConfig.mode
203
- }, "Loaded conversation-scoped artifacts");
204
- return referenceArtifacts;
510
+ }, "Loaded conversation-scoped artifacts with count limit");
511
+ return limitedArtifacts;
205
512
  } catch (error) {
206
513
  (await import("../logger.js")).getLogger("conversations").error({
207
514
  error: error instanceof Error ? error.message : "Unknown error",
@@ -212,4 +519,4 @@ async function getConversationScopedArtifacts(params) {
212
519
  }
213
520
 
214
521
  //#endregion
215
- export { createDefaultConversationHistoryConfig, getConversationScopedArtifacts, getFormattedConversationHistory, getFullConversationContext, getScopedHistory, getUserFacingHistory, saveA2AMessageResponse };
522
+ export { compressConversationIfNeeded, compressionLocks, createDefaultConversationHistoryConfig, getConversationHistoryWithCompression, getConversationScopedArtifacts, getFormattedConversationHistory, getFullConversationContext, getScopedHistory, getUserFacingHistory, saveA2AMessageResponse };
@@ -2,8 +2,8 @@ import { getLogger } from "../logger.js";
2
2
  import { flushBatchProcessor } from "../instrumentation.js";
3
3
  import dbClient_default from "../data/db/dbClient.js";
4
4
  import { AGENT_EXECUTION_MAX_CONSECUTIVE_ERRORS } from "../constants/execution-limits/index.js";
5
- import { registerStreamHelper, unregisterStreamHelper } from "../utils/stream-registry.js";
6
5
  import { tracer } from "../utils/tracer.js";
6
+ import { registerStreamHelper, unregisterStreamHelper } from "../utils/stream-registry.js";
7
7
  import { agentSessionManager } from "../services/AgentSession.js";
8
8
  import { resolveModelConfig } from "../utils/model-resolver.js";
9
9
  import { agentInitializingOp, completionOp, errorOp } from "../utils/agent-operations.js";
@@ -128,7 +128,7 @@ interface CompressionEventData {
128
128
  artifactCount: number;
129
129
  contextSizeBefore: number;
130
130
  contextSizeAfter: number;
131
- compressionType: 'mid_generation' | 'full_conversation';
131
+ compressionType: 'mid_generation' | 'conversation_level';
132
132
  }
133
133
  interface ErrorEventData {
134
134
  message: string;
@@ -2,10 +2,10 @@ import { getLogger } from "../logger.js";
2
2
  import dbClient_default from "../data/db/dbClient.js";
3
3
  import { ARTIFACT_GENERATION_BACKOFF_INITIAL_MS, ARTIFACT_GENERATION_BACKOFF_MAX_MS, ARTIFACT_GENERATION_MAX_RETRIES, ARTIFACT_SESSION_MAX_PENDING, ARTIFACT_SESSION_MAX_PREVIOUS_SUMMARIES, STATUS_UPDATE_DEFAULT_INTERVAL_SECONDS, STATUS_UPDATE_DEFAULT_NUM_EVENTS } from "../constants/execution-limits/index.js";
4
4
  import { toolSessionManager } from "../agents/ToolSessionManager.js";
5
+ import { setSpanWithError as setSpanWithError$1, tracer } from "../utils/tracer.js";
5
6
  import { getFormattedConversationHistory } from "../data/conversations.js";
6
7
  import { defaultStatusSchemas } from "../utils/default-status-schemas.js";
7
8
  import { getStreamHelper } from "../utils/stream-registry.js";
8
- import { setSpanWithError as setSpanWithError$1, tracer } from "../utils/tracer.js";
9
9
  import { ArtifactService } from "./ArtifactService.js";
10
10
  import { ArtifactParser } from "./ArtifactParser.js";
11
11
  import { z } from "@hono/zod-openapi";
@@ -529,7 +529,8 @@ var ArtifactService = class ArtifactService {
529
529
  full: fullData
530
530
  }
531
531
  }],
532
- metadata: artifact.metadata || {}
532
+ metadata: artifact.metadata || {},
533
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
533
534
  };
534
535
  const result = await upsertLedgerArtifact(dbClient_default)({
535
536
  scopes: {