@gajae-code/agent-core 0.11.2 → 0.11.3
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/CHANGELOG.md +5 -0
- package/package.json +4 -4
- package/src/compaction/pruning.ts +32 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.11.3] - 2026-07-19
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Pre-compaction pruning now preserves bounded, actionable error evidence instead of discarding it, while enforcing exact positive-savings admission and accounting so a prune is only applied when it demonstrably reduces context cost (#2635).
|
|
9
|
+
|
|
5
10
|
## [0.11.1] - 2026-07-16
|
|
6
11
|
|
|
7
12
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/agent-core",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.3",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"fmt": "biome format --write ."
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@gajae-code/ai": "0.11.
|
|
36
|
-
"@gajae-code/natives": "0.11.
|
|
37
|
-
"@gajae-code/utils": "0.11.
|
|
35
|
+
"@gajae-code/ai": "0.11.3",
|
|
36
|
+
"@gajae-code/natives": "0.11.3",
|
|
37
|
+
"@gajae-code/utils": "0.11.3",
|
|
38
38
|
"@opentelemetry/api": "^1.9.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { ToolCall, ToolResultMessage } from "@gajae-code/ai";
|
|
12
|
+
import { sanitizeText } from "@gajae-code/utils";
|
|
12
13
|
import type { AgentMessage } from "../types";
|
|
13
|
-
import { estimateEntryTokens } from "./compaction";
|
|
14
|
+
import { estimateEntryTokens, estimateTextTokensHeuristic } from "./compaction";
|
|
14
15
|
import type { SessionEntry, SessionMessageEntry } from "./entries";
|
|
15
16
|
|
|
16
17
|
export interface PruneConfig {
|
|
@@ -48,6 +49,7 @@ export interface PruneResult {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
const DIGEST_NOTICE_TOKEN_CAP_MULTIPLIER = 1.25;
|
|
52
|
+
const ERROR_DIGEST_NOTICE_MIN_CHARS = 240;
|
|
51
53
|
|
|
52
54
|
function createGenericPrunedNotice(tokens: number): string {
|
|
53
55
|
return `[Output truncated - ${tokens} tokens]`;
|
|
@@ -66,6 +68,17 @@ function firstErrorLine(text: string): string | undefined {
|
|
|
66
68
|
?.trim();
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
function firstNonEmptyLine(text: string): string | undefined {
|
|
72
|
+
return text
|
|
73
|
+
.split(/\r?\n/)
|
|
74
|
+
.find(line => line.trim().length > 0)
|
|
75
|
+
?.trim();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function lastNonEmptyLine(text: string): string | undefined {
|
|
79
|
+
return text.trim().split(/\r?\n/).filter(Boolean).at(-1)?.trim();
|
|
80
|
+
}
|
|
81
|
+
|
|
69
82
|
function truncateField(value: string, maxLength: number): string {
|
|
70
83
|
if (value.length <= maxLength) return value;
|
|
71
84
|
if (maxLength <= 1) return "…";
|
|
@@ -74,7 +87,7 @@ function truncateField(value: string, maxLength: number): string {
|
|
|
74
87
|
|
|
75
88
|
function resultDigest(message: ToolResultMessage): string | undefined {
|
|
76
89
|
const toolName = message.toolName.toLowerCase();
|
|
77
|
-
const text = firstTextContent(message);
|
|
90
|
+
const text = sanitizeText(firstTextContent(message));
|
|
78
91
|
if (toolName === "bash") {
|
|
79
92
|
const details = message as { details?: { exitCode?: unknown } };
|
|
80
93
|
const exitCode =
|
|
@@ -99,7 +112,12 @@ function resultDigest(message: ToolResultMessage): string | undefined {
|
|
|
99
112
|
.join("; ") || "search digest unavailable"
|
|
100
113
|
);
|
|
101
114
|
}
|
|
102
|
-
return undefined;
|
|
115
|
+
if (message.isError !== true) return undefined;
|
|
116
|
+
if (text.trim().length === 0) return "error=tool result failed without text";
|
|
117
|
+
const error = firstErrorLine(text);
|
|
118
|
+
if (error) return `error=${error}`;
|
|
119
|
+
const summary = firstNonEmptyLine(text) ?? lastNonEmptyLine(text);
|
|
120
|
+
return summary ? `summary=${summary}` : undefined;
|
|
103
121
|
}
|
|
104
122
|
|
|
105
123
|
function createPrunedNotice(tokens: number, message?: ToolResultMessage): string {
|
|
@@ -110,7 +128,9 @@ function createPrunedNotice(tokens: number, message?: ToolResultMessage): string
|
|
|
110
128
|
const maxTokens = Math.max(genericTokens, Math.floor(genericTokens * DIGEST_NOTICE_TOKEN_CAP_MULTIPLIER));
|
|
111
129
|
const prefix = `[Output truncated - ${tokens} tokens; `;
|
|
112
130
|
const suffix = "]";
|
|
113
|
-
const
|
|
131
|
+
const digestChars = maxTokens * 4 - prefix.length - suffix.length;
|
|
132
|
+
const maxChars =
|
|
133
|
+
message?.isError === true ? Math.max(ERROR_DIGEST_NOTICE_MIN_CHARS, digestChars) : Math.max(0, digestChars);
|
|
114
134
|
return `${prefix}${truncateField(digest, maxChars)}${suffix}`;
|
|
115
135
|
}
|
|
116
136
|
|
|
@@ -122,8 +142,7 @@ function getToolResultMessage(entry: SessionEntry): ToolResultMessage | undefine
|
|
|
122
142
|
}
|
|
123
143
|
|
|
124
144
|
function estimatePrunedSavings(tokens: number, notice: string): number {
|
|
125
|
-
|
|
126
|
-
return Math.max(0, tokens - noticeTokens);
|
|
145
|
+
return tokens - estimateTextTokensHeuristic(notice);
|
|
127
146
|
}
|
|
128
147
|
|
|
129
148
|
export interface AssistantArgumentPruneResult {
|
|
@@ -670,11 +689,17 @@ function collectToolOutputPruneCandidates(
|
|
|
670
689
|
}
|
|
671
690
|
|
|
672
691
|
const notice = createPrunedNotice(tokens, message);
|
|
692
|
+
const savings = estimatePrunedSavings(tokens, notice);
|
|
693
|
+
const errorNoticeGrows = message.isError === true && notice.length > firstTextContent(message).length;
|
|
694
|
+
if (savings <= 0 || errorNoticeGrows) {
|
|
695
|
+
accumulatedTokens += tokens;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
673
698
|
candidates.push({
|
|
674
699
|
entry: entry as SessionMessageEntry,
|
|
675
700
|
tokens,
|
|
676
701
|
notice,
|
|
677
|
-
savings
|
|
702
|
+
savings,
|
|
678
703
|
});
|
|
679
704
|
accumulatedTokens += tokens;
|
|
680
705
|
}
|