@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.
- package/dist/a2a/handlers.js +2 -1
- package/dist/agents/Agent.d.ts +47 -1
- package/dist/agents/Agent.js +558 -569
- package/dist/agents/generateTaskHandler.js +12 -3
- package/dist/constants/execution-limits/defaults.d.ts +4 -0
- package/dist/constants/execution-limits/defaults.js +5 -1
- package/dist/constants/execution-limits/index.d.ts +2 -2
- package/dist/constants/execution-limits/index.js +8 -3
- package/dist/data/conversations.d.ts +43 -3
- package/dist/data/conversations.js +317 -10
- package/dist/handlers/executionHandler.js +1 -1
- package/dist/services/AgentSession.d.ts +1 -1
- package/dist/services/AgentSession.js +1 -1
- package/dist/services/ArtifactService.js +2 -1
- package/dist/services/BaseCompressor.d.ts +183 -0
- package/dist/services/BaseCompressor.js +504 -0
- package/dist/services/ConversationCompressor.d.ts +32 -0
- package/dist/services/ConversationCompressor.js +91 -0
- package/dist/services/MidGenerationCompressor.d.ts +35 -63
- package/dist/services/MidGenerationCompressor.js +40 -311
- package/dist/tools/distill-conversation-history-tool.d.ts +62 -0
- package/dist/tools/distill-conversation-history-tool.js +206 -0
- package/dist/tools/distill-conversation-tool.js +20 -35
- package/dist/utils/model-context-utils.d.ts +10 -2
- package/dist/utils/model-context-utils.js +79 -47
- package/package.json +2 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BaseCompressor, CompressionConfig, CompressionResult } from "./BaseCompressor.js";
|
|
2
|
+
import { ModelSettings } from "@inkeep/agents-core";
|
|
3
|
+
|
|
4
|
+
//#region src/services/ConversationCompressor.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Conversation-level compressor
|
|
8
|
+
* Compresses entire conversations for long-term storage or analysis
|
|
9
|
+
*/
|
|
10
|
+
declare class ConversationCompressor extends BaseCompressor {
|
|
11
|
+
constructor(sessionId: string, conversationId: string, tenantId: string, projectId: string, config?: CompressionConfig, summarizerModel?: ModelSettings, baseModel?: ModelSettings);
|
|
12
|
+
/**
|
|
13
|
+
* Get compression type for this compressor
|
|
14
|
+
*/
|
|
15
|
+
getCompressionType(): 'conversation_level';
|
|
16
|
+
/**
|
|
17
|
+
* Check if compression is needed based on total context size exceeding conversation limits
|
|
18
|
+
*/
|
|
19
|
+
isCompressionNeeded(messages: any[]): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Perform conversation-level compression
|
|
22
|
+
* Unlike mid-generation, this compresses ALL messages in the conversation
|
|
23
|
+
*/
|
|
24
|
+
compress(messages: any[]): Promise<CompressionResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Override createConversationSummary for conversation-level compression
|
|
27
|
+
* Uses specialized conversation history distillation instead of the base implementation
|
|
28
|
+
*/
|
|
29
|
+
protected createConversationSummary(messages: any[], toolCallToArtifactMap: Record<string, string>): Promise<any>;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { ConversationCompressor };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { getLogger } from "../logger.js";
|
|
2
|
+
import { distillConversationHistory } from "../tools/distill-conversation-history-tool.js";
|
|
3
|
+
import { BaseCompressor, getModelAwareCompressionConfig } from "./BaseCompressor.js";
|
|
4
|
+
|
|
5
|
+
//#region src/services/ConversationCompressor.ts
|
|
6
|
+
const logger = getLogger("ConversationCompressor");
|
|
7
|
+
/**
|
|
8
|
+
* Conversation-level compressor
|
|
9
|
+
* Compresses entire conversations for long-term storage or analysis
|
|
10
|
+
*/
|
|
11
|
+
var ConversationCompressor = class extends BaseCompressor {
|
|
12
|
+
constructor(sessionId, conversationId, tenantId, projectId, config, summarizerModel, baseModel) {
|
|
13
|
+
const compressionConfig = config || getModelAwareCompressionConfig(summarizerModel, .5);
|
|
14
|
+
super(sessionId, conversationId, tenantId, projectId, compressionConfig, summarizerModel, baseModel);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get compression type for this compressor
|
|
18
|
+
*/
|
|
19
|
+
getCompressionType() {
|
|
20
|
+
return "conversation_level";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if compression is needed based on total context size exceeding conversation limits
|
|
24
|
+
*/
|
|
25
|
+
isCompressionNeeded(messages) {
|
|
26
|
+
const contextSize = this.calculateContextSize(messages);
|
|
27
|
+
const remaining = this.config.hardLimit - contextSize;
|
|
28
|
+
const needsCompression = remaining <= this.config.safetyBuffer;
|
|
29
|
+
logger.debug({
|
|
30
|
+
sessionId: this.sessionId,
|
|
31
|
+
contextSize,
|
|
32
|
+
hardLimit: this.config.hardLimit,
|
|
33
|
+
safetyBuffer: this.config.safetyBuffer,
|
|
34
|
+
remaining,
|
|
35
|
+
needsCompression
|
|
36
|
+
}, "Checking conversation compression criteria");
|
|
37
|
+
return needsCompression;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Perform conversation-level compression
|
|
41
|
+
* Unlike mid-generation, this compresses ALL messages in the conversation
|
|
42
|
+
*/
|
|
43
|
+
async compress(messages) {
|
|
44
|
+
const contextSizeBefore = this.calculateContextSize(messages);
|
|
45
|
+
logger.info({
|
|
46
|
+
sessionId: this.sessionId,
|
|
47
|
+
messageCount: messages.length,
|
|
48
|
+
contextSize: contextSizeBefore
|
|
49
|
+
}, "CONVERSATION COMPRESSION: Starting compression");
|
|
50
|
+
const toolCallToArtifactMap = await this.saveToolResultsAsArtifacts(messages, 0);
|
|
51
|
+
const summary = await this.createConversationSummary(messages, toolCallToArtifactMap);
|
|
52
|
+
const contextSizeAfter = this.estimateTokens(JSON.stringify(summary));
|
|
53
|
+
this.recordCompressionEvent({
|
|
54
|
+
reason: "automatic",
|
|
55
|
+
messageCount: messages.length,
|
|
56
|
+
artifactCount: Object.keys(toolCallToArtifactMap).length,
|
|
57
|
+
contextSizeBefore,
|
|
58
|
+
contextSizeAfter,
|
|
59
|
+
compressionType: this.getCompressionType()
|
|
60
|
+
});
|
|
61
|
+
logger.info({
|
|
62
|
+
sessionId: this.sessionId,
|
|
63
|
+
artifactsCreated: Object.keys(toolCallToArtifactMap).length,
|
|
64
|
+
messageCount: messages.length,
|
|
65
|
+
contextSizeBefore,
|
|
66
|
+
contextSizeAfter,
|
|
67
|
+
compressionRatio: contextSizeAfter / contextSizeBefore,
|
|
68
|
+
artifactIds: Object.values(toolCallToArtifactMap)
|
|
69
|
+
}, "CONVERSATION COMPRESSION: Compression completed successfully");
|
|
70
|
+
return {
|
|
71
|
+
artifactIds: Object.values(toolCallToArtifactMap),
|
|
72
|
+
summary
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Override createConversationSummary for conversation-level compression
|
|
77
|
+
* Uses specialized conversation history distillation instead of the base implementation
|
|
78
|
+
*/
|
|
79
|
+
async createConversationSummary(messages, toolCallToArtifactMap) {
|
|
80
|
+
if (!this.summarizerModel) throw new Error("Summarizer model is required for conversation history compression");
|
|
81
|
+
return await distillConversationHistory({
|
|
82
|
+
messages,
|
|
83
|
+
conversationId: this.conversationId,
|
|
84
|
+
summarizerModel: this.summarizerModel,
|
|
85
|
+
toolCallToArtifactMap
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { ConversationCompressor };
|
|
@@ -1,91 +1,63 @@
|
|
|
1
|
+
import { BaseCompressor, CompressionConfig, CompressionResult } from "./BaseCompressor.js";
|
|
1
2
|
import { ModelSettings } from "@inkeep/agents-core";
|
|
2
3
|
|
|
3
4
|
//#region src/services/MidGenerationCompressor.d.ts
|
|
4
|
-
|
|
5
|
-
hardLimit: number;
|
|
6
|
-
safetyBuffer: number;
|
|
7
|
-
enabled?: boolean;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Get compression config from environment variables
|
|
11
|
-
*/
|
|
12
|
-
declare function getCompressionConfigFromEnv(): CompressionConfig;
|
|
5
|
+
|
|
13
6
|
/**
|
|
14
|
-
*
|
|
15
|
-
* Compresses context when generate() steps get too large
|
|
7
|
+
* Mid-generation compressor
|
|
8
|
+
* Compresses context when generate() steps get too large with manual compression support
|
|
16
9
|
*/
|
|
17
|
-
declare class MidGenerationCompressor {
|
|
18
|
-
private sessionId;
|
|
19
|
-
private conversationId;
|
|
20
|
-
private tenantId;
|
|
21
|
-
private projectId;
|
|
22
|
-
private config;
|
|
23
|
-
private summarizerModel?;
|
|
24
|
-
private baseModel?;
|
|
10
|
+
declare class MidGenerationCompressor extends BaseCompressor {
|
|
25
11
|
private shouldCompress;
|
|
26
|
-
private processedToolCalls;
|
|
27
12
|
private lastProcessedMessageIndex;
|
|
28
|
-
|
|
29
|
-
constructor(sessionId: string, conversationId: string, tenantId: string, projectId: string, config: CompressionConfig, summarizerModel?: ModelSettings | undefined, baseModel?: ModelSettings | undefined);
|
|
30
|
-
/**
|
|
31
|
-
* Get the hard limit for compression decisions
|
|
32
|
-
*/
|
|
33
|
-
getHardLimit(): number;
|
|
34
|
-
/**
|
|
35
|
-
* Estimate tokens (4 chars = 1 token)
|
|
36
|
-
*/
|
|
37
|
-
private estimateTokens;
|
|
13
|
+
constructor(sessionId: string, conversationId: string, tenantId: string, projectId: string, config?: CompressionConfig, summarizerModel?: ModelSettings, baseModel?: ModelSettings);
|
|
38
14
|
/**
|
|
39
|
-
*
|
|
15
|
+
* Get compression type for this compressor
|
|
40
16
|
*/
|
|
41
|
-
|
|
17
|
+
getCompressionType(): 'mid_generation';
|
|
42
18
|
/**
|
|
43
19
|
* Manual compression request from LLM tool
|
|
44
20
|
*/
|
|
45
21
|
requestManualCompression(reason?: string): void;
|
|
46
22
|
/**
|
|
47
23
|
* Check if compression is needed (either automatic or manual)
|
|
24
|
+
* Supports manual compression requests unique to mid-generation
|
|
48
25
|
*/
|
|
49
26
|
isCompressionNeeded(messages: any[]): boolean;
|
|
50
27
|
/**
|
|
51
|
-
* Perform compression
|
|
52
|
-
|
|
53
|
-
compress(messages: any[]): Promise<{
|
|
54
|
-
artifactIds: string[];
|
|
55
|
-
summary: any;
|
|
56
|
-
}>;
|
|
57
|
-
/**
|
|
58
|
-
* 1. Save NEW tool results as artifacts (only process messages since last compression)
|
|
28
|
+
* Perform mid-generation compression with incremental processing
|
|
29
|
+
* Uses base class functionality with mid-generation specific logic
|
|
59
30
|
*/
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* 3. Create conversation summary with artifact references
|
|
63
|
-
*/
|
|
64
|
-
private createConversationSummary;
|
|
65
|
-
/**
|
|
66
|
-
* Extract text messages and convert tool calls to descriptive text
|
|
67
|
-
* Avoids API tool-call/tool-result pairing issues while preserving context
|
|
68
|
-
*/
|
|
69
|
-
private extractTextMessages;
|
|
70
|
-
/**
|
|
71
|
-
* Check if tool result data is effectively empty
|
|
72
|
-
*/
|
|
73
|
-
private isEmpty;
|
|
74
|
-
/**
|
|
75
|
-
* Recursively remove _structureHints from an object
|
|
76
|
-
*/
|
|
77
|
-
private removeStructureHints;
|
|
31
|
+
compress(messages: any[]): Promise<CompressionResult>;
|
|
78
32
|
/**
|
|
79
33
|
* Get current state for debugging
|
|
80
34
|
*/
|
|
81
35
|
getState(): {
|
|
82
36
|
shouldCompress: boolean;
|
|
37
|
+
lastProcessedMessageIndex: number;
|
|
83
38
|
config: CompressionConfig;
|
|
39
|
+
processedToolCalls: string[];
|
|
40
|
+
cumulativeSummary: {
|
|
41
|
+
type: "conversation_summary_v1";
|
|
42
|
+
high_level: string;
|
|
43
|
+
user_intent: string;
|
|
44
|
+
decisions: string[];
|
|
45
|
+
open_questions: string[];
|
|
46
|
+
next_steps: {
|
|
47
|
+
for_agent: string[];
|
|
48
|
+
for_user: string[];
|
|
49
|
+
};
|
|
50
|
+
session_id?: string | null | undefined;
|
|
51
|
+
related_artifacts?: {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
tool_name: string;
|
|
55
|
+
tool_call_id: string;
|
|
56
|
+
content_type: string;
|
|
57
|
+
key_findings: string[];
|
|
58
|
+
}[] | undefined;
|
|
59
|
+
} | null;
|
|
84
60
|
};
|
|
85
|
-
/**
|
|
86
|
-
* Get the current compression summary
|
|
87
|
-
*/
|
|
88
|
-
getCompressionSummary(): any;
|
|
89
61
|
}
|
|
90
62
|
//#endregion
|
|
91
|
-
export {
|
|
63
|
+
export { MidGenerationCompressor };
|
|
@@ -1,73 +1,24 @@
|
|
|
1
1
|
import { getLogger } from "../logger.js";
|
|
2
|
-
import {
|
|
3
|
-
import { distillConversation } from "../tools/distill-conversation-tool.js";
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
2
|
+
import { BaseCompressor, getModelAwareCompressionConfig } from "./BaseCompressor.js";
|
|
5
3
|
|
|
6
4
|
//#region src/services/MidGenerationCompressor.ts
|
|
7
5
|
const logger = getLogger("MidGenerationCompressor");
|
|
8
6
|
/**
|
|
9
|
-
*
|
|
7
|
+
* Mid-generation compressor
|
|
8
|
+
* Compresses context when generate() steps get too large with manual compression support
|
|
10
9
|
*/
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
hardLimit: parseInt(process.env.AGENTS_COMPRESSION_HARD_LIMIT || "120000"),
|
|
14
|
-
safetyBuffer: parseInt(process.env.AGENTS_COMPRESSION_SAFETY_BUFFER || "20000"),
|
|
15
|
-
enabled: process.env.AGENTS_COMPRESSION_ENABLED !== "false"
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Simple mid-generation compressor
|
|
20
|
-
* Compresses context when generate() steps get too large
|
|
21
|
-
*/
|
|
22
|
-
var MidGenerationCompressor = class {
|
|
10
|
+
var MidGenerationCompressor = class extends BaseCompressor {
|
|
23
11
|
shouldCompress = false;
|
|
24
|
-
processedToolCalls = /* @__PURE__ */ new Set();
|
|
25
12
|
lastProcessedMessageIndex = 0;
|
|
26
|
-
cumulativeSummary = null;
|
|
27
13
|
constructor(sessionId, conversationId, tenantId, projectId, config, summarizerModel, baseModel) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.tenantId = tenantId;
|
|
31
|
-
this.projectId = projectId;
|
|
32
|
-
this.config = config;
|
|
33
|
-
this.summarizerModel = summarizerModel;
|
|
34
|
-
this.baseModel = baseModel;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Get the hard limit for compression decisions
|
|
38
|
-
*/
|
|
39
|
-
getHardLimit() {
|
|
40
|
-
return this.config.hardLimit;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Estimate tokens (4 chars = 1 token)
|
|
44
|
-
*/
|
|
45
|
-
estimateTokens(content) {
|
|
46
|
-
const text = typeof content === "string" ? content : JSON.stringify(content);
|
|
47
|
-
return Math.ceil(text.length / 4);
|
|
14
|
+
const compressionConfig = config || getModelAwareCompressionConfig(summarizerModel);
|
|
15
|
+
super(sessionId, conversationId, tenantId, projectId, compressionConfig, summarizerModel, baseModel);
|
|
48
16
|
}
|
|
49
17
|
/**
|
|
50
|
-
*
|
|
18
|
+
* Get compression type for this compressor
|
|
51
19
|
*/
|
|
52
|
-
|
|
53
|
-
return
|
|
54
|
-
let msgTokens = 0;
|
|
55
|
-
if (Array.isArray(msg.content)) {
|
|
56
|
-
for (const block of msg.content) if (block.type === "text") msgTokens += this.estimateTokens(block.text || "");
|
|
57
|
-
else if (block.type === "tool-call") msgTokens += this.estimateTokens(JSON.stringify({
|
|
58
|
-
toolCallId: block.toolCallId,
|
|
59
|
-
toolName: block.toolName,
|
|
60
|
-
input: block.input
|
|
61
|
-
}));
|
|
62
|
-
else if (block.type === "tool-result") msgTokens += this.estimateTokens(JSON.stringify({
|
|
63
|
-
toolCallId: block.toolCallId,
|
|
64
|
-
toolName: block.toolName,
|
|
65
|
-
output: block.output
|
|
66
|
-
}));
|
|
67
|
-
} else if (typeof msg.content === "string") msgTokens += this.estimateTokens(msg.content);
|
|
68
|
-
else if (msg.content) msgTokens += this.estimateTokens(JSON.stringify(msg.content));
|
|
69
|
-
return total + msgTokens;
|
|
70
|
-
}, 0);
|
|
20
|
+
getCompressionType() {
|
|
21
|
+
return "mid_generation";
|
|
71
22
|
}
|
|
72
23
|
/**
|
|
73
24
|
* Manual compression request from LLM tool
|
|
@@ -81,14 +32,27 @@ var MidGenerationCompressor = class {
|
|
|
81
32
|
}
|
|
82
33
|
/**
|
|
83
34
|
* Check if compression is needed (either automatic or manual)
|
|
35
|
+
* Supports manual compression requests unique to mid-generation
|
|
84
36
|
*/
|
|
85
37
|
isCompressionNeeded(messages) {
|
|
86
38
|
if (this.shouldCompress) return true;
|
|
87
39
|
const contextSize = this.calculateContextSize(messages);
|
|
88
|
-
|
|
40
|
+
const remaining = this.config.hardLimit - contextSize;
|
|
41
|
+
const needsCompression = remaining <= this.config.safetyBuffer;
|
|
42
|
+
logger.debug({
|
|
43
|
+
sessionId: this.sessionId,
|
|
44
|
+
contextSize,
|
|
45
|
+
hardLimit: this.config.hardLimit,
|
|
46
|
+
safetyBuffer: this.config.safetyBuffer,
|
|
47
|
+
remaining,
|
|
48
|
+
needsCompression,
|
|
49
|
+
manualRequest: this.shouldCompress
|
|
50
|
+
}, "Checking mid-generation compression criteria");
|
|
51
|
+
return needsCompression;
|
|
89
52
|
}
|
|
90
53
|
/**
|
|
91
|
-
* Perform compression
|
|
54
|
+
* Perform mid-generation compression with incremental processing
|
|
55
|
+
* Uses base class functionality with mid-generation specific logic
|
|
92
56
|
*/
|
|
93
57
|
async compress(messages) {
|
|
94
58
|
const contextSizeBefore = this.calculateContextSize(messages);
|
|
@@ -96,280 +60,45 @@ var MidGenerationCompressor = class {
|
|
|
96
60
|
sessionId: this.sessionId,
|
|
97
61
|
messageCount: messages.length,
|
|
98
62
|
contextSize: contextSizeBefore
|
|
99
|
-
}, "COMPRESSION: Starting compression");
|
|
100
|
-
const
|
|
101
|
-
if (Array.isArray(msg.content)) return count + msg.content.filter((block) => block.type === "tool-result").length;
|
|
102
|
-
return count;
|
|
103
|
-
}, 0);
|
|
104
|
-
logger.debug({ toolResultCount }, "Tool results found for compression");
|
|
105
|
-
const toolCallToArtifactMap = await this.saveToolResultsAsArtifacts(messages);
|
|
63
|
+
}, "MID-GENERATION COMPRESSION: Starting compression");
|
|
64
|
+
const toolCallToArtifactMap = await this.saveToolResultsAsArtifacts(messages, this.lastProcessedMessageIndex);
|
|
106
65
|
const summary = await this.createConversationSummary(messages, toolCallToArtifactMap);
|
|
107
66
|
const contextSizeAfter = this.estimateTokens(JSON.stringify(summary));
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
contextSizeAfter,
|
|
117
|
-
compressionType: "mid_generation"
|
|
118
|
-
});
|
|
119
|
-
}
|
|
67
|
+
this.recordCompressionEvent({
|
|
68
|
+
reason: this.shouldCompress ? "manual" : "automatic",
|
|
69
|
+
messageCount: messages.length,
|
|
70
|
+
artifactCount: Object.keys(toolCallToArtifactMap).length,
|
|
71
|
+
contextSizeBefore,
|
|
72
|
+
contextSizeAfter,
|
|
73
|
+
compressionType: this.getCompressionType()
|
|
74
|
+
});
|
|
120
75
|
this.shouldCompress = false;
|
|
76
|
+
this.lastProcessedMessageIndex = messages.length;
|
|
121
77
|
logger.info({
|
|
122
78
|
sessionId: this.sessionId,
|
|
123
79
|
artifactsCreated: Object.keys(toolCallToArtifactMap).length,
|
|
124
80
|
messageCount: messages.length,
|
|
125
81
|
contextSizeBefore,
|
|
126
82
|
contextSizeAfter,
|
|
83
|
+
compressionRatio: contextSizeAfter / contextSizeBefore,
|
|
127
84
|
artifactIds: Object.values(toolCallToArtifactMap)
|
|
128
|
-
}, "COMPRESSION: Compression completed successfully");
|
|
85
|
+
}, "MID-GENERATION COMPRESSION: Compression completed successfully");
|
|
129
86
|
return {
|
|
130
87
|
artifactIds: Object.values(toolCallToArtifactMap),
|
|
131
88
|
summary
|
|
132
89
|
};
|
|
133
90
|
}
|
|
134
91
|
/**
|
|
135
|
-
* 1. Save NEW tool results as artifacts (only process messages since last compression)
|
|
136
|
-
*/
|
|
137
|
-
async saveToolResultsAsArtifacts(messages) {
|
|
138
|
-
const session = agentSessionManager.getSession(this.sessionId);
|
|
139
|
-
if (!session) throw new Error(`No session found: ${this.sessionId}`);
|
|
140
|
-
const toolCallToArtifactMap = {};
|
|
141
|
-
const newMessages = messages.slice(this.lastProcessedMessageIndex);
|
|
142
|
-
logger.debug({
|
|
143
|
-
totalMessages: messages.length,
|
|
144
|
-
newMessages: newMessages.length,
|
|
145
|
-
startIndex: this.lastProcessedMessageIndex
|
|
146
|
-
}, "Starting compression artifact processing");
|
|
147
|
-
for (const message of newMessages) if (Array.isArray(message.content)) {
|
|
148
|
-
for (const block of message.content) if (block.type === "tool-result") {
|
|
149
|
-
if (block.toolName === "get_reference_artifact" || block.toolName === "thinking_complete") {
|
|
150
|
-
logger.debug({
|
|
151
|
-
toolCallId: block.toolCallId,
|
|
152
|
-
toolName: block.toolName
|
|
153
|
-
}, "Skipping special tool - not creating artifacts");
|
|
154
|
-
this.processedToolCalls.add(block.toolCallId);
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
if (this.processedToolCalls.has(block.toolCallId)) {
|
|
158
|
-
logger.debug({
|
|
159
|
-
toolCallId: block.toolCallId,
|
|
160
|
-
toolName: block.toolName
|
|
161
|
-
}, "Skipping already processed tool call");
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
const artifactId = `compress_${block.toolName || "tool"}_${block.toolCallId || Date.now()}_${randomUUID().slice(0, 8)}`;
|
|
165
|
-
logger.debug({
|
|
166
|
-
artifactId,
|
|
167
|
-
toolName: block.toolName,
|
|
168
|
-
toolCallId: block.toolCallId
|
|
169
|
-
}, "Saving compression artifact");
|
|
170
|
-
let toolInput = null;
|
|
171
|
-
if (Array.isArray(message.content)) toolInput = message.content.find((b) => b.type === "tool-call" && b.toolCallId === block.toolCallId)?.input;
|
|
172
|
-
const cleanToolResult = this.removeStructureHints(block.output);
|
|
173
|
-
const toolResultData = {
|
|
174
|
-
toolName: block.toolName,
|
|
175
|
-
toolInput,
|
|
176
|
-
toolResult: cleanToolResult,
|
|
177
|
-
compressedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
178
|
-
};
|
|
179
|
-
if (this.isEmpty(toolResultData)) {
|
|
180
|
-
logger.debug({
|
|
181
|
-
toolName: block.toolName,
|
|
182
|
-
toolCallId: block.toolCallId
|
|
183
|
-
}, "Skipping empty tool result");
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
const artifactData = {
|
|
187
|
-
artifactId,
|
|
188
|
-
taskId: `task_${this.conversationId}-${this.sessionId}`,
|
|
189
|
-
toolCallId: block.toolCallId,
|
|
190
|
-
artifactType: "tool_result",
|
|
191
|
-
pendingGeneration: true,
|
|
192
|
-
tenantId: this.tenantId,
|
|
193
|
-
projectId: this.projectId,
|
|
194
|
-
contextId: this.conversationId,
|
|
195
|
-
subAgentId: this.sessionId,
|
|
196
|
-
metadata: {
|
|
197
|
-
toolCallId: block.toolCallId,
|
|
198
|
-
toolName: block.toolName,
|
|
199
|
-
compressionReason: "mid_generation_context_limit"
|
|
200
|
-
},
|
|
201
|
-
summaryData: {
|
|
202
|
-
toolName: block.toolName,
|
|
203
|
-
note: "Compressed tool result - see full data for details"
|
|
204
|
-
},
|
|
205
|
-
data: toolResultData
|
|
206
|
-
};
|
|
207
|
-
const fullData = artifactData.data;
|
|
208
|
-
if (!(fullData && typeof fullData === "object" && Object.keys(fullData).length > 0 && fullData.toolResult && (typeof fullData.toolResult !== "object" || Object.keys(fullData.toolResult).length > 0))) {
|
|
209
|
-
logger.debug({
|
|
210
|
-
artifactId,
|
|
211
|
-
toolName: block.toolName,
|
|
212
|
-
toolCallId: block.toolCallId
|
|
213
|
-
}, "Skipping empty compression artifact");
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
session.recordEvent("artifact_saved", this.sessionId, artifactData);
|
|
217
|
-
this.processedToolCalls.add(block.toolCallId);
|
|
218
|
-
toolCallToArtifactMap[block.toolCallId] = artifactId;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
this.lastProcessedMessageIndex = messages.length;
|
|
222
|
-
logger.debug({
|
|
223
|
-
totalArtifactsCreated: Object.keys(toolCallToArtifactMap).length,
|
|
224
|
-
newMessageIndex: this.lastProcessedMessageIndex
|
|
225
|
-
}, "Compression artifact processing completed");
|
|
226
|
-
return toolCallToArtifactMap;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* 3. Create conversation summary with artifact references
|
|
230
|
-
*/
|
|
231
|
-
async createConversationSummary(messages, toolCallToArtifactMap) {
|
|
232
|
-
const textMessages = this.extractTextMessages(messages, toolCallToArtifactMap);
|
|
233
|
-
logger.debug({
|
|
234
|
-
sessionId: this.sessionId,
|
|
235
|
-
messageCount: messages.length,
|
|
236
|
-
textMessageCount: textMessages.length,
|
|
237
|
-
artifactCount: Object.keys(toolCallToArtifactMap).length,
|
|
238
|
-
sampleMessages: messages.slice(0, 2).map((m) => ({
|
|
239
|
-
role: m.role,
|
|
240
|
-
contentType: typeof m.content,
|
|
241
|
-
contentPreview: typeof m.content === "string" ? m.content.substring(0, 100) : "array/object"
|
|
242
|
-
}))
|
|
243
|
-
}, "Starting distillation with debug info");
|
|
244
|
-
const summary = await distillConversation({
|
|
245
|
-
messages,
|
|
246
|
-
conversationId: this.conversationId,
|
|
247
|
-
currentSummary: this.cumulativeSummary,
|
|
248
|
-
summarizerModel: this.summarizerModel,
|
|
249
|
-
toolCallToArtifactMap
|
|
250
|
-
});
|
|
251
|
-
this.cumulativeSummary = summary;
|
|
252
|
-
logger.debug({
|
|
253
|
-
sessionId: this.sessionId,
|
|
254
|
-
summaryGenerated: !!summary,
|
|
255
|
-
summaryHighLevel: summary?.high_level,
|
|
256
|
-
artifactsCount: summary?.related_artifacts?.length || 0
|
|
257
|
-
}, "Distillation completed");
|
|
258
|
-
return {
|
|
259
|
-
text_messages: textMessages,
|
|
260
|
-
summary
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Extract text messages and convert tool calls to descriptive text
|
|
265
|
-
* Avoids API tool-call/tool-result pairing issues while preserving context
|
|
266
|
-
*/
|
|
267
|
-
extractTextMessages(messages, toolCallToArtifactMap) {
|
|
268
|
-
const textMessages = [];
|
|
269
|
-
const toolCallPairs = /* @__PURE__ */ new Map();
|
|
270
|
-
for (const message of messages) if (Array.isArray(message.content)) {
|
|
271
|
-
for (const block of message.content) if (block.type === "tool-call") if (!toolCallPairs.has(block.toolCallId)) toolCallPairs.set(block.toolCallId, {
|
|
272
|
-
call: block,
|
|
273
|
-
result: null
|
|
274
|
-
});
|
|
275
|
-
else toolCallPairs.get(block.toolCallId).call = block;
|
|
276
|
-
else if (block.type === "tool-result") if (!toolCallPairs.has(block.toolCallId)) toolCallPairs.set(block.toolCallId, {
|
|
277
|
-
call: null,
|
|
278
|
-
result: block
|
|
279
|
-
});
|
|
280
|
-
else toolCallPairs.get(block.toolCallId).result = block;
|
|
281
|
-
}
|
|
282
|
-
for (const message of messages) if (message.role === "assistant" && typeof message.content === "string") textMessages.push({
|
|
283
|
-
role: message.role,
|
|
284
|
-
content: message.content
|
|
285
|
-
});
|
|
286
|
-
else if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
287
|
-
const textParts = [];
|
|
288
|
-
const toolCallsInMessage = /* @__PURE__ */ new Set();
|
|
289
|
-
const preservedBlocks = [];
|
|
290
|
-
for (const block of message.content) if (block.type === "text") textParts.push(block.text);
|
|
291
|
-
else if (block.type === "tool-call" && block.toolName === "thinking_complete") {} else if (block.type === "tool-call") toolCallsInMessage.add(block.toolCallId);
|
|
292
|
-
for (const toolCallId of toolCallsInMessage) {
|
|
293
|
-
const pair = toolCallPairs.get(toolCallId);
|
|
294
|
-
const artifactId = toolCallToArtifactMap[toolCallId];
|
|
295
|
-
if (pair?.call) {
|
|
296
|
-
const args = JSON.stringify(pair.call.input);
|
|
297
|
-
const artifactText = artifactId ? ` Results compressed into artifact: ${artifactId}.` : " Results were compressed but not saved.";
|
|
298
|
-
textParts.push(`I called ${pair.call.toolName}(${args}).${artifactText}`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
if (preservedBlocks.length > 0 && textParts.length > 0) {
|
|
302
|
-
const content = [...preservedBlocks];
|
|
303
|
-
if (textParts.length > 0) content.push({
|
|
304
|
-
type: "text",
|
|
305
|
-
text: textParts.join("\n\n")
|
|
306
|
-
});
|
|
307
|
-
textMessages.push({
|
|
308
|
-
role: message.role,
|
|
309
|
-
content
|
|
310
|
-
});
|
|
311
|
-
} else if (preservedBlocks.length > 0) textMessages.push({
|
|
312
|
-
role: message.role,
|
|
313
|
-
content: preservedBlocks
|
|
314
|
-
});
|
|
315
|
-
else if (textParts.length > 0) textMessages.push({
|
|
316
|
-
role: message.role,
|
|
317
|
-
content: textParts.join("\n\n")
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
return textMessages;
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Check if tool result data is effectively empty
|
|
324
|
-
*/
|
|
325
|
-
isEmpty(toolResultData) {
|
|
326
|
-
if (!toolResultData || typeof toolResultData !== "object") return true;
|
|
327
|
-
const { toolResult } = toolResultData;
|
|
328
|
-
if (!toolResult) return true;
|
|
329
|
-
if (typeof toolResult === "object" && !Array.isArray(toolResult)) {
|
|
330
|
-
const keys = Object.keys(toolResult);
|
|
331
|
-
if (keys.length === 0) return true;
|
|
332
|
-
return keys.every((key) => {
|
|
333
|
-
const value = toolResult[key];
|
|
334
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
335
|
-
if (Array.isArray(value) && value.length === 0) return true;
|
|
336
|
-
if (typeof value === "object" && Object.keys(value).length === 0) return true;
|
|
337
|
-
return false;
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
if (Array.isArray(toolResult) && toolResult.length === 0) return true;
|
|
341
|
-
if (typeof toolResult === "string" && toolResult.trim() === "") return true;
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Recursively remove _structureHints from an object
|
|
346
|
-
*/
|
|
347
|
-
removeStructureHints(obj) {
|
|
348
|
-
if (obj === null || obj === void 0) return obj;
|
|
349
|
-
if (Array.isArray(obj)) return obj.map((item) => this.removeStructureHints(item));
|
|
350
|
-
if (typeof obj === "object") {
|
|
351
|
-
const cleaned = {};
|
|
352
|
-
for (const [key, value] of Object.entries(obj)) if (key !== "_structureHints") cleaned[key] = this.removeStructureHints(value);
|
|
353
|
-
return cleaned;
|
|
354
|
-
}
|
|
355
|
-
return obj;
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
92
|
* Get current state for debugging
|
|
359
93
|
*/
|
|
360
94
|
getState() {
|
|
361
95
|
return {
|
|
96
|
+
...super.getState(),
|
|
362
97
|
shouldCompress: this.shouldCompress,
|
|
363
|
-
|
|
98
|
+
lastProcessedMessageIndex: this.lastProcessedMessageIndex
|
|
364
99
|
};
|
|
365
100
|
}
|
|
366
|
-
/**
|
|
367
|
-
* Get the current compression summary
|
|
368
|
-
*/
|
|
369
|
-
getCompressionSummary() {
|
|
370
|
-
return this.cumulativeSummary;
|
|
371
|
-
}
|
|
372
101
|
};
|
|
373
102
|
|
|
374
103
|
//#endregion
|
|
375
|
-
export { MidGenerationCompressor
|
|
104
|
+
export { MidGenerationCompressor };
|