@townco/agent 0.1.120 → 0.1.122
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 +19 -1
- package/dist/acp-server/session-storage.d.ts +7 -1
- package/dist/acp-server/session-storage.js +11 -1
- package/dist/runner/agent-runner.d.ts +2 -1
- package/dist/runner/langchain/index.js +25 -111
- package/dist/runner/langchain/tools/subagent.js +151 -188
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/dist/runner/langchain/tools/artifacts.d.ts +0 -68
- package/dist/runner/langchain/tools/artifacts.js +0 -474
- package/dist/runner/langchain/tools/conversation_search.d.ts +0 -22
- package/dist/runner/langchain/tools/conversation_search.js +0 -137
- package/dist/runner/langchain/tools/generate_image.d.ts +0 -47
- package/dist/runner/langchain/tools/generate_image.js +0 -175
- package/dist/runner/langchain/tools/port-utils.d.ts +0 -8
- package/dist/runner/langchain/tools/port-utils.js +0 -35
|
@@ -676,12 +676,23 @@ export class AgentAcpAdapter {
|
|
|
676
676
|
sources: session.sources,
|
|
677
677
|
nextId: session.sourceCounter + 1,
|
|
678
678
|
};
|
|
679
|
-
|
|
679
|
+
// Pass session metadata for subagent sessions
|
|
680
|
+
const metadata = {
|
|
681
|
+
...(session.parentSessionId && {
|
|
682
|
+
parentSessionId: session.parentSessionId,
|
|
683
|
+
}),
|
|
684
|
+
...(session.isSubagentSession && {
|
|
685
|
+
isSubagentSession: session.isSubagentSession,
|
|
686
|
+
}),
|
|
687
|
+
};
|
|
688
|
+
await this.storage.saveSession(sessionId, session.messages, session.context, citations, Object.keys(metadata).length > 0 ? metadata : undefined);
|
|
680
689
|
logger.debug("Saved session to disk", {
|
|
681
690
|
sessionId,
|
|
682
691
|
messageCount: session.messages.length,
|
|
683
692
|
contextCount: session.context.length,
|
|
684
693
|
citationsCount: session.sources.length,
|
|
694
|
+
isSubagentSession: session.isSubagentSession,
|
|
695
|
+
parentSessionId: session.parentSessionId,
|
|
685
696
|
});
|
|
686
697
|
}
|
|
687
698
|
catch (error) {
|
|
@@ -1046,6 +1057,8 @@ export class AgentAcpAdapter {
|
|
|
1046
1057
|
// If session not found (e.g., after server restart), create a new one
|
|
1047
1058
|
if (!session) {
|
|
1048
1059
|
logger.info(`Session ${params.sessionId} not found, creating new session`);
|
|
1060
|
+
const parentSessionId = params._meta?.parentSessionId;
|
|
1061
|
+
const isSubagentSession = params._meta?.isSubagentSession === true;
|
|
1049
1062
|
session = {
|
|
1050
1063
|
pendingPrompt: null,
|
|
1051
1064
|
messages: [],
|
|
@@ -1054,6 +1067,9 @@ export class AgentAcpAdapter {
|
|
|
1054
1067
|
isCancelled: false,
|
|
1055
1068
|
sourceCounter: 0,
|
|
1056
1069
|
sources: [],
|
|
1070
|
+
// Extract subagent metadata from _meta (only add if defined)
|
|
1071
|
+
...(parentSessionId && { parentSessionId }),
|
|
1072
|
+
...(isSubagentSession && { isSubagentSession }),
|
|
1057
1073
|
};
|
|
1058
1074
|
this.sessions.set(params.sessionId, session);
|
|
1059
1075
|
}
|
|
@@ -1299,6 +1315,8 @@ export class AgentAcpAdapter {
|
|
|
1299
1315
|
...(this.agentDir ? { agentDir: this.agentDir } : {}),
|
|
1300
1316
|
// Pass resolved context messages to agent
|
|
1301
1317
|
contextMessages,
|
|
1318
|
+
// Pass context entries for hook execution (compaction tracking)
|
|
1319
|
+
contextEntries: session.context,
|
|
1302
1320
|
// Pass abort signal for cancellation
|
|
1303
1321
|
abortSignal: session.pendingPrompt?.signal,
|
|
1304
1322
|
// Pass emitUpdate callback for file change events
|
|
@@ -142,6 +142,10 @@ export interface SessionMetadata {
|
|
|
142
142
|
agentName: string;
|
|
143
143
|
/** E2B sandbox ID for persistent sandbox reconnection */
|
|
144
144
|
sandboxId?: string | undefined;
|
|
145
|
+
/** Parent session ID for subagent sessions (enables session linking) */
|
|
146
|
+
parentSessionId?: string | undefined;
|
|
147
|
+
/** Flag indicating this is a subagent session (for UI filtering) */
|
|
148
|
+
isSubagentSession?: boolean | undefined;
|
|
145
149
|
}
|
|
146
150
|
/**
|
|
147
151
|
* Complete session data stored in JSON files
|
|
@@ -207,7 +211,7 @@ export declare class SessionStorage {
|
|
|
207
211
|
* Save a session to disk
|
|
208
212
|
* Uses atomic write (write to temp file, then rename)
|
|
209
213
|
*/
|
|
210
|
-
saveSession(sessionId: string, messages: SessionMessage[], context: ContextEntry[], citations?: CitationStorage): Promise<void>;
|
|
214
|
+
saveSession(sessionId: string, messages: SessionMessage[], context: ContextEntry[], citations?: CitationStorage, extraMetadata?: Partial<SessionMetadata>): Promise<void>;
|
|
211
215
|
/**
|
|
212
216
|
* Load a session from disk
|
|
213
217
|
*/
|
|
@@ -242,6 +246,8 @@ export declare class SessionStorage {
|
|
|
242
246
|
updatedAt: string;
|
|
243
247
|
messageCount: number;
|
|
244
248
|
firstUserMessage?: string;
|
|
249
|
+
parentSessionId?: string;
|
|
250
|
+
isSubagentSession?: boolean;
|
|
245
251
|
}>>;
|
|
246
252
|
/**
|
|
247
253
|
* Get the directory for storing large content files for a session (artifacts folder)
|
|
@@ -135,6 +135,8 @@ const sessionMetadataSchema = z.object({
|
|
|
135
135
|
updatedAt: z.string(),
|
|
136
136
|
agentName: z.string(),
|
|
137
137
|
sandboxId: z.string().optional(),
|
|
138
|
+
parentSessionId: z.string().optional(),
|
|
139
|
+
isSubagentSession: z.boolean().optional(),
|
|
138
140
|
});
|
|
139
141
|
// Citation schemas - matches SourceSchema from packages/ui/src/core/schemas/source.ts
|
|
140
142
|
const persistedCitationSourceSchema = z.object({
|
|
@@ -200,7 +202,7 @@ export class SessionStorage {
|
|
|
200
202
|
* Save a session to disk
|
|
201
203
|
* Uses atomic write (write to temp file, then rename)
|
|
202
204
|
*/
|
|
203
|
-
async saveSession(sessionId, messages, context, citations) {
|
|
205
|
+
async saveSession(sessionId, messages, context, citations, extraMetadata) {
|
|
204
206
|
// Debug: log subagent data being saved
|
|
205
207
|
const messagesWithSubagents = messages.filter((msg) => msg.content.some((block) => block.type === "tool_call" &&
|
|
206
208
|
"subagentMessages" in block &&
|
|
@@ -239,6 +241,7 @@ export class SessionStorage {
|
|
|
239
241
|
createdAt: existingSession?.metadata.createdAt || now,
|
|
240
242
|
updatedAt: now,
|
|
241
243
|
agentName: this.agentName,
|
|
244
|
+
...extraMetadata,
|
|
242
245
|
},
|
|
243
246
|
...(citations && { citations }),
|
|
244
247
|
};
|
|
@@ -386,6 +389,13 @@ export class SessionStorage {
|
|
|
386
389
|
if (firstUserText && "text" in firstUserText) {
|
|
387
390
|
entry.firstUserMessage = firstUserText.text.slice(0, 100);
|
|
388
391
|
}
|
|
392
|
+
// Include sub-agent metadata if present
|
|
393
|
+
if (session.metadata.parentSessionId) {
|
|
394
|
+
entry.parentSessionId = session.metadata.parentSessionId;
|
|
395
|
+
}
|
|
396
|
+
if (session.metadata.isSubagentSession) {
|
|
397
|
+
entry.isSubagentSession = session.metadata.isSubagentSession;
|
|
398
|
+
}
|
|
389
399
|
sessions.push(entry);
|
|
390
400
|
}
|
|
391
401
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PromptRequest, PromptResponse, SessionNotification } from "@agentclientprotocol/sdk";
|
|
2
2
|
import type { Span } from "@opentelemetry/api";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import type { ContentBlock } from "../acp-server/session-storage.js";
|
|
4
|
+
import type { ContentBlock, ContextEntry } from "../acp-server/session-storage.js";
|
|
5
5
|
export declare const zAgentRunnerParams: z.ZodObject<{
|
|
6
6
|
displayName: z.ZodOptional<z.ZodString>;
|
|
7
7
|
version: z.ZodOptional<z.ZodString>;
|
|
@@ -99,6 +99,7 @@ export type InvokeRequest = Omit<PromptRequest, "_meta"> & {
|
|
|
99
99
|
agentDir?: string;
|
|
100
100
|
sessionMeta?: Record<string, unknown>;
|
|
101
101
|
contextMessages?: SessionMessage[];
|
|
102
|
+
contextEntries?: ContextEntry[];
|
|
102
103
|
configOverrides?: ConfigOverrides;
|
|
103
104
|
/** Abort signal for cancellation - tools can listen for this to stop early */
|
|
104
105
|
abortSignal?: AbortSignal;
|
|
@@ -412,122 +412,36 @@ export class LangchainAgent {
|
|
|
412
412
|
baseContextTokens,
|
|
413
413
|
modelContextWindow,
|
|
414
414
|
});
|
|
415
|
-
//
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const noSession = req.sessionMeta?.[SUBAGENT_MODE_KEY] === true; // Subagents don't have session storage
|
|
419
|
-
// Track cumulative tool output tokens in this turn for proper context calculation
|
|
420
|
-
let cumulativeToolOutputTokens = 0;
|
|
415
|
+
// Hook execution removed from tool wrapper - hooks are now executed only at the adapter layer
|
|
416
|
+
// The adapter has proper MidTurnRestartError handling that can restart the turn
|
|
417
|
+
// Executing hooks here in the runner was causing restart signals to be caught as tool failures
|
|
421
418
|
// Counter for subagent calls - used to create unique source ID ranges
|
|
422
419
|
// Each subagent call gets a unique offset (1000, 2000, 3000, etc.)
|
|
423
420
|
// to ensure sources never conflict with parent's sources (typically < 100)
|
|
424
421
|
let subagentCallCounter = 0;
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
cumulativeToolOutputTokens += outputTokens;
|
|
441
|
-
return result;
|
|
442
|
-
}
|
|
443
|
-
_logger.info("Tool wrapper: compacting large tool result", {
|
|
444
|
-
toolName: originalTool.name,
|
|
445
|
-
originalTokens: outputTokens,
|
|
446
|
-
cumulativeToolOutputTokens,
|
|
447
|
-
});
|
|
448
|
-
// Calculate current context including all tool outputs so far in this turn
|
|
449
|
-
// Uses accurate baseContextTokens calculated earlier (system prompt, tool overhead, MCP overhead, message history)
|
|
450
|
-
// This ensures we account for multiple large tool calls in the same turn
|
|
451
|
-
const currentTokens = baseContextTokens + cumulativeToolOutputTokens;
|
|
452
|
-
// Build proper hook context with all required fields
|
|
453
|
-
const hookContext = {
|
|
454
|
-
session: {
|
|
455
|
-
messages: req.contextMessages || [],
|
|
456
|
-
context: [],
|
|
457
|
-
requestParams: {
|
|
458
|
-
hookSettings: hooks.find((h) => h.type === "tool_response")
|
|
459
|
-
?.setting,
|
|
460
|
-
},
|
|
461
|
-
},
|
|
462
|
-
currentTokens,
|
|
463
|
-
maxTokens: modelContextWindow,
|
|
464
|
-
percentage: (currentTokens / modelContextWindow) * 100,
|
|
465
|
-
model: this.definition.model,
|
|
466
|
-
agent: this.definition,
|
|
467
|
-
toolResponse: {
|
|
468
|
-
toolCallId: "pending",
|
|
469
|
-
toolName: originalTool.name,
|
|
470
|
-
toolInput: input,
|
|
471
|
-
rawOutput,
|
|
472
|
-
outputTokens,
|
|
473
|
-
},
|
|
474
|
-
};
|
|
475
|
-
// Call the tool response compactor directly
|
|
476
|
-
const hookResult = await toolResponseCompactor(hookContext);
|
|
477
|
-
// Extract modified output from metadata
|
|
478
|
-
if (hookResult?.metadata?.modifiedOutput) {
|
|
479
|
-
const modifiedOutput = hookResult.metadata
|
|
480
|
-
.modifiedOutput;
|
|
481
|
-
const compactedTokens = countToolResultTokens(modifiedOutput);
|
|
482
|
-
// Update cumulative total with the compacted size (not original!)
|
|
483
|
-
cumulativeToolOutputTokens += compactedTokens;
|
|
484
|
-
_logger.info("Tool wrapper: compaction complete", {
|
|
485
|
-
toolName: originalTool.name,
|
|
486
|
-
originalTokens: outputTokens,
|
|
487
|
-
compactedTokens,
|
|
488
|
-
reduction: `${((1 - compactedTokens / outputTokens) * 100).toFixed(1)}%`,
|
|
489
|
-
totalCumulativeTokens: cumulativeToolOutputTokens,
|
|
490
|
-
});
|
|
491
|
-
// Include compaction metadata in the output for the adapter to extract
|
|
492
|
-
// Also include original content so adapter can store it
|
|
493
|
-
const originalContentStr = typeof rawOutput === "object" &&
|
|
494
|
-
rawOutput !== null &&
|
|
495
|
-
"content" in rawOutput
|
|
496
|
-
? String(rawOutput.content)
|
|
497
|
-
: JSON.stringify(rawOutput);
|
|
498
|
-
const outputWithMeta = {
|
|
499
|
-
...modifiedOutput,
|
|
500
|
-
_compactionMeta: {
|
|
501
|
-
action: hookResult.metadata.action,
|
|
502
|
-
originalTokens: hookResult.metadata.originalTokens,
|
|
503
|
-
finalTokens: hookResult.metadata.finalTokens,
|
|
504
|
-
tokensSaved: hookResult.metadata.tokensSaved,
|
|
505
|
-
originalContent: originalContentStr,
|
|
506
|
-
},
|
|
507
|
-
};
|
|
508
|
-
// Always return JSON string to preserve metadata
|
|
509
|
-
return JSON.stringify(outputWithMeta);
|
|
510
|
-
}
|
|
511
|
-
// No compaction happened, count original size
|
|
512
|
-
cumulativeToolOutputTokens += outputTokens;
|
|
513
|
-
return result;
|
|
514
|
-
};
|
|
515
|
-
// Create new tool with wrapped function
|
|
516
|
-
// biome-ignore lint/suspicious/noExplicitAny: Need to pass function with dynamic signature
|
|
517
|
-
const wrappedTool = tool(wrappedFunc, {
|
|
518
|
-
name: originalTool.name,
|
|
519
|
-
description: originalTool.description,
|
|
520
|
-
// biome-ignore lint/suspicious/noExplicitAny: Accessing internal schema property
|
|
521
|
-
schema: originalTool.schema,
|
|
522
|
-
});
|
|
523
|
-
// Preserve metadata
|
|
524
|
-
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
525
|
-
wrappedTool.prettyName = originalTool.prettyName;
|
|
526
|
-
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
527
|
-
wrappedTool.icon = originalTool.icon;
|
|
528
|
-
return wrappedTool;
|
|
422
|
+
// Simple tool wrapper that just passes through to the original tool
|
|
423
|
+
// All hook execution (compaction, restart logic) happens at the adapter layer
|
|
424
|
+
const wrappedTools = enabledTools.map((originalTool) => {
|
|
425
|
+
const wrappedFunc = async (input) => {
|
|
426
|
+
// Execute the original tool and return raw result
|
|
427
|
+
const result = await originalTool.invoke(input);
|
|
428
|
+
return result;
|
|
429
|
+
};
|
|
430
|
+
// Create new tool with wrapped function
|
|
431
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to pass function with dynamic signature
|
|
432
|
+
const wrappedTool = tool(wrappedFunc, {
|
|
433
|
+
name: originalTool.name,
|
|
434
|
+
description: originalTool.description,
|
|
435
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing internal schema property
|
|
436
|
+
schema: originalTool.schema,
|
|
529
437
|
});
|
|
530
|
-
|
|
438
|
+
// Preserve metadata
|
|
439
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
440
|
+
wrappedTool.prettyName = originalTool.prettyName;
|
|
441
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
442
|
+
wrappedTool.icon = originalTool.icon;
|
|
443
|
+
return wrappedTool;
|
|
444
|
+
});
|
|
531
445
|
// Filter tools if running in subagent mode
|
|
532
446
|
const isSubagent = req.sessionMeta?.[SUBAGENT_MODE_KEY] === true;
|
|
533
447
|
const filteredTools = isSubagent
|