@townco/agent 0.1.80 → 0.1.82
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/acp-server/adapter.js +2 -6
- package/dist/runner/hooks/executor.js +4 -0
- package/dist/runner/hooks/predefined/compaction-tool.js +28 -6
- package/dist/runner/hooks/predefined/tool-response-compactor.js +13 -1
- package/dist/runner/hooks/types.d.ts +3 -0
- package/dist/runner/langchain/index.js +58 -6
- package/dist/runner/langchain/tools/filesystem.d.ts +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/context-size-calculator.d.ts +1 -2
- package/dist/utils/context-size-calculator.js +2 -6
- package/package.json +6 -6
|
@@ -602,8 +602,7 @@ export class AgentAcpAdapter {
|
|
|
602
602
|
const context_size = calculateContextSize(contextMessages, this.agent.definition.systemPrompt ?? undefined, undefined, // No LLM-reported tokens yet
|
|
603
603
|
this.currentToolOverheadTokens, // Include tool overhead
|
|
604
604
|
this.currentMcpOverheadTokens, // Include MCP overhead
|
|
605
|
-
getModelContextWindow(this.agent.definition.model)
|
|
606
|
-
true);
|
|
605
|
+
getModelContextWindow(this.agent.definition.model));
|
|
607
606
|
const contextSnapshot = createContextSnapshot(session.messages.length - 1, // Exclude the newly added user message (it will be passed separately via prompt)
|
|
608
607
|
new Date().toISOString(), previousContext, context_size);
|
|
609
608
|
session.context.push(contextSnapshot);
|
|
@@ -1215,13 +1214,10 @@ export class AgentAcpAdapter {
|
|
|
1215
1214
|
}
|
|
1216
1215
|
}
|
|
1217
1216
|
// Calculate context size with LLM-reported tokens from this turn
|
|
1218
|
-
// Exclude tool results - they're only sent during the turn they were received,
|
|
1219
|
-
// not in subsequent turns (only messages are sent)
|
|
1220
1217
|
const context_size = calculateContextSize(contextMessages, this.agent.definition.systemPrompt ?? undefined, turnTokenUsage.inputTokens, // Final LLM-reported tokens from this turn
|
|
1221
1218
|
this.currentToolOverheadTokens, // Include tool overhead
|
|
1222
1219
|
this.currentMcpOverheadTokens, // Include MCP overhead
|
|
1223
|
-
getModelContextWindow(this.agent.definition.model)
|
|
1224
|
-
true);
|
|
1220
|
+
getModelContextWindow(this.agent.definition.model));
|
|
1225
1221
|
const contextSnapshot = createContextSnapshot(session.messages.length, new Date().toISOString(), previousContext, context_size);
|
|
1226
1222
|
session.context.push(contextSnapshot);
|
|
1227
1223
|
await this.saveSessionToDisk(params.sessionId, session);
|
|
@@ -163,6 +163,7 @@ export class HookExecutor {
|
|
|
163
163
|
currentPercentage: percentage,
|
|
164
164
|
callback: hook.callback,
|
|
165
165
|
triggeredAt,
|
|
166
|
+
toolCallId: toolResponse.toolCallId,
|
|
166
167
|
}, notifications);
|
|
167
168
|
try {
|
|
168
169
|
// Load and execute callback
|
|
@@ -200,6 +201,7 @@ export class HookExecutor {
|
|
|
200
201
|
callback: hook.callback,
|
|
201
202
|
metadata: result.metadata,
|
|
202
203
|
completedAt: Date.now(),
|
|
204
|
+
toolCallId: toolResponse.toolCallId,
|
|
203
205
|
}, notifications);
|
|
204
206
|
return response;
|
|
205
207
|
}
|
|
@@ -210,6 +212,7 @@ export class HookExecutor {
|
|
|
210
212
|
callback: hook.callback,
|
|
211
213
|
metadata: { action: "no_action_needed" },
|
|
212
214
|
completedAt: Date.now(),
|
|
215
|
+
toolCallId: toolResponse.toolCallId,
|
|
213
216
|
}, notifications);
|
|
214
217
|
return { notifications };
|
|
215
218
|
}
|
|
@@ -221,6 +224,7 @@ export class HookExecutor {
|
|
|
221
224
|
callback: hook.callback,
|
|
222
225
|
error: error instanceof Error ? error.message : String(error),
|
|
223
226
|
completedAt: Date.now(),
|
|
227
|
+
toolCallId: toolResponse.toolCallId,
|
|
224
228
|
}, notifications);
|
|
225
229
|
logger.error("Tool response hook execution failed", {
|
|
226
230
|
callback: hook.callback,
|
|
@@ -24,14 +24,36 @@ export const compactionTool = async (ctx) => {
|
|
|
24
24
|
});
|
|
25
25
|
// Build the conversation history to compact
|
|
26
26
|
const messagesToCompact = ctx.session.messages;
|
|
27
|
-
// Convert session messages to text for context
|
|
27
|
+
// Convert session messages to text for context, including tool calls and results
|
|
28
28
|
const conversationText = messagesToCompact
|
|
29
29
|
.map((msg) => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const parts = [];
|
|
31
|
+
for (const block of msg.content) {
|
|
32
|
+
if (block.type === "text") {
|
|
33
|
+
parts.push(block.text);
|
|
34
|
+
}
|
|
35
|
+
else if (block.type === "tool_call") {
|
|
36
|
+
// Include tool call info
|
|
37
|
+
parts.push(`[Tool: ${block.title}]`);
|
|
38
|
+
if (block.rawInput) {
|
|
39
|
+
parts.push(`Input: ${JSON.stringify(block.rawInput, null, 2)}`);
|
|
40
|
+
}
|
|
41
|
+
if (block.rawOutput) {
|
|
42
|
+
// Summarize large outputs to avoid overwhelming the compaction LLM
|
|
43
|
+
const outputStr = JSON.stringify(block.rawOutput);
|
|
44
|
+
if (outputStr.length > 2000) {
|
|
45
|
+
parts.push(`Output: [Large output - ${outputStr.length} chars]`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
parts.push(`Output: ${outputStr}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (block.error) {
|
|
52
|
+
parts.push(`Error: ${block.error}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return `${msg.role.toUpperCase()}:\n${parts.join("\n")}`;
|
|
35
57
|
})
|
|
36
58
|
.join("\n\n---\n\n");
|
|
37
59
|
// Create system prompt for compaction
|
|
@@ -56,7 +56,19 @@ export const toolResponseCompactor = async (ctx) => {
|
|
|
56
56
|
}
|
|
57
57
|
// Response would exceed threshold, need to compact or truncate
|
|
58
58
|
// Determine target size: fit within available space, but cap at compactionLimit for truncation
|
|
59
|
-
|
|
59
|
+
// IMPORTANT: If context is already over threshold, availableSpace will be negative
|
|
60
|
+
// In that case, use a minimum reasonable target size (e.g., 10% of the output or 1000 tokens)
|
|
61
|
+
const minTargetSize = Math.max(Math.floor(outputTokens * 0.1), 1000);
|
|
62
|
+
const targetSize = availableSpace > 0
|
|
63
|
+
? Math.min(availableSpace, compactionLimit)
|
|
64
|
+
: minTargetSize;
|
|
65
|
+
logger.info("Calculated target size for compaction", {
|
|
66
|
+
availableSpace,
|
|
67
|
+
compactionLimit,
|
|
68
|
+
minTargetSize,
|
|
69
|
+
targetSize,
|
|
70
|
+
contextAlreadyOverThreshold: availableSpace <= 0,
|
|
71
|
+
});
|
|
60
72
|
// Case 2: Huge response, must truncate (too large for LLM compaction)
|
|
61
73
|
if (outputTokens >= compactionLimit) {
|
|
62
74
|
logger.warn("Tool response exceeds compaction capacity, truncating", {
|
|
@@ -168,16 +168,19 @@ export type HookNotification = {
|
|
|
168
168
|
currentPercentage: number;
|
|
169
169
|
callback: string;
|
|
170
170
|
triggeredAt: number;
|
|
171
|
+
toolCallId?: string;
|
|
171
172
|
} | {
|
|
172
173
|
type: "hook_completed";
|
|
173
174
|
hookType: HookType;
|
|
174
175
|
callback: string;
|
|
175
176
|
metadata?: HookResult["metadata"];
|
|
176
177
|
completedAt: number;
|
|
178
|
+
toolCallId?: string;
|
|
177
179
|
} | {
|
|
178
180
|
type: "hook_error";
|
|
179
181
|
hookType: HookType;
|
|
180
182
|
callback: string;
|
|
181
183
|
error: string;
|
|
182
184
|
completedAt: number;
|
|
185
|
+
toolCallId?: string;
|
|
183
186
|
};
|
|
@@ -472,6 +472,7 @@ export class LangchainAgent {
|
|
|
472
472
|
}
|
|
473
473
|
const agent = createAgent(agentConfig);
|
|
474
474
|
// Build messages from context history if available, otherwise use just the prompt
|
|
475
|
+
// Type includes tool messages for sending tool results
|
|
475
476
|
let messages;
|
|
476
477
|
// Helper to convert content blocks to LangChain format
|
|
477
478
|
// LangChain expects image_url type with data URL, not Claude's native image+source format
|
|
@@ -539,11 +540,62 @@ export class LangchainAgent {
|
|
|
539
540
|
};
|
|
540
541
|
if (req.contextMessages && req.contextMessages.length > 0) {
|
|
541
542
|
// Use context messages (already resolved from context entries)
|
|
542
|
-
// Convert to LangChain format
|
|
543
|
-
messages =
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
543
|
+
// Convert to LangChain format, including tool calls and their results
|
|
544
|
+
messages = [];
|
|
545
|
+
for (const msg of req.contextMessages) {
|
|
546
|
+
if (msg.role === "user") {
|
|
547
|
+
messages.push({
|
|
548
|
+
type: "human",
|
|
549
|
+
content: convertContentBlocks(msg.content),
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
else if (msg.role === "assistant") {
|
|
553
|
+
// Check if message has tool calls
|
|
554
|
+
const toolCalls = msg.content.filter((block) => block.type === "tool_call");
|
|
555
|
+
const textBlocks = msg.content.filter((block) => block.type === "text" || block.type === "image");
|
|
556
|
+
if (toolCalls.length > 0) {
|
|
557
|
+
// Build AI message with tool_use blocks
|
|
558
|
+
const aiContent = [];
|
|
559
|
+
// Add any text content first
|
|
560
|
+
for (const block of textBlocks) {
|
|
561
|
+
if (block.type === "text") {
|
|
562
|
+
aiContent.push({ type: "text", text: block.text });
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// Add tool_use blocks
|
|
566
|
+
for (const tc of toolCalls) {
|
|
567
|
+
if (tc.type === "tool_call") {
|
|
568
|
+
aiContent.push({
|
|
569
|
+
type: "tool_use",
|
|
570
|
+
id: tc.id,
|
|
571
|
+
name: tc.title,
|
|
572
|
+
input: tc.rawInput || {},
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
messages.push({ type: "ai", content: aiContent });
|
|
577
|
+
// Add tool result messages for each tool call that has output
|
|
578
|
+
for (const tc of toolCalls) {
|
|
579
|
+
if (tc.type === "tool_call" && tc.rawOutput) {
|
|
580
|
+
messages.push({
|
|
581
|
+
type: "tool",
|
|
582
|
+
tool_call_id: tc.id,
|
|
583
|
+
content: typeof tc.rawOutput === "string"
|
|
584
|
+
? tc.rawOutput
|
|
585
|
+
: JSON.stringify(tc.rawOutput),
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
// No tool calls - simple AI message
|
|
592
|
+
messages.push({
|
|
593
|
+
type: "ai",
|
|
594
|
+
content: convertContentBlocks(msg.content),
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
547
599
|
// Add the current prompt as the final human message
|
|
548
600
|
const promptContent = convertContentBlocks(req.prompt);
|
|
549
601
|
messages.push({
|
|
@@ -1059,7 +1111,7 @@ const makeMcpToolsClient = (mcpConfigs) => {
|
|
|
1059
1111
|
// Whether to throw on errors if a tool fails to load (optional, default: true)
|
|
1060
1112
|
throwOnLoadError: true,
|
|
1061
1113
|
// Whether to prefix tool names with the server name (optional, default: false)
|
|
1062
|
-
prefixToolNameWithServerName:
|
|
1114
|
+
prefixToolNameWithServerName: true,
|
|
1063
1115
|
// Optional additional prefix for tool names (optional, default: "")
|
|
1064
1116
|
additionalToolNamePrefix: "",
|
|
1065
1117
|
// Use standardized content block format in tool outputs
|
|
@@ -5,8 +5,8 @@ export declare function makeFilesystemTools(workingDirectory: string): readonly
|
|
|
5
5
|
glob: z.ZodOptional<z.ZodString>;
|
|
6
6
|
output_mode: z.ZodOptional<z.ZodEnum<{
|
|
7
7
|
content: "content";
|
|
8
|
-
files_with_matches: "files_with_matches";
|
|
9
8
|
count: "count";
|
|
9
|
+
files_with_matches: "files_with_matches";
|
|
10
10
|
}>>;
|
|
11
11
|
"-B": z.ZodOptional<z.ZodNumber>;
|
|
12
12
|
"-A": z.ZodOptional<z.ZodNumber>;
|
|
@@ -20,7 +20,7 @@ export declare function makeFilesystemTools(workingDirectory: string): readonly
|
|
|
20
20
|
pattern: string;
|
|
21
21
|
path?: string | undefined;
|
|
22
22
|
glob?: string | undefined;
|
|
23
|
-
output_mode?: "content" | "
|
|
23
|
+
output_mode?: "content" | "count" | "files_with_matches" | undefined;
|
|
24
24
|
"-B"?: number | undefined;
|
|
25
25
|
"-A"?: number | undefined;
|
|
26
26
|
"-C"?: number | undefined;
|
|
@@ -33,7 +33,7 @@ export declare function makeFilesystemTools(workingDirectory: string): readonly
|
|
|
33
33
|
pattern: string;
|
|
34
34
|
path?: string | undefined;
|
|
35
35
|
glob?: string | undefined;
|
|
36
|
-
output_mode?: "content" | "
|
|
36
|
+
output_mode?: "content" | "count" | "files_with_matches" | undefined;
|
|
37
37
|
"-B"?: number | undefined;
|
|
38
38
|
"-A"?: number | undefined;
|
|
39
39
|
"-C"?: number | undefined;
|