@townco/agent 0.1.101 → 0.1.104
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.d.ts +10 -0
- package/dist/acp-server/adapter.js +101 -31
- package/dist/definition/index.d.ts +17 -4
- package/dist/definition/index.js +19 -2
- package/dist/runner/agent-runner.d.ts +6 -2
- package/dist/runner/hooks/executor.d.ts +5 -3
- package/dist/runner/hooks/executor.js +190 -150
- package/dist/runner/hooks/loader.d.ts +13 -1
- package/dist/runner/hooks/loader.js +27 -0
- package/dist/runner/hooks/predefined/compaction-tool.d.ts +3 -1
- package/dist/runner/hooks/predefined/compaction-tool.js +38 -2
- package/dist/runner/hooks/predefined/context-validator.d.ts +57 -0
- package/dist/runner/hooks/predefined/context-validator.js +92 -0
- package/dist/runner/hooks/predefined/document-context-extractor/chunk-manager.js +2 -2
- package/dist/runner/hooks/predefined/document-context-extractor/content-extractor.js +29 -0
- package/dist/runner/hooks/predefined/document-context-extractor/relevance-scorer.js +29 -0
- package/dist/runner/hooks/predefined/mid-turn-compaction.d.ts +17 -0
- package/dist/runner/hooks/predefined/mid-turn-compaction.js +224 -0
- package/dist/runner/hooks/predefined/token-utils.d.ts +11 -0
- package/dist/runner/hooks/predefined/token-utils.js +13 -0
- package/dist/runner/hooks/predefined/tool-response-compactor.js +155 -25
- package/dist/runner/hooks/registry.js +2 -0
- package/dist/runner/hooks/types.d.ts +37 -4
- package/dist/runner/index.d.ts +6 -2
- package/dist/runner/langchain/index.js +60 -8
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as acp from "@agentclientprotocol/sdk";
|
|
2
2
|
import type { AgentRunner } from "../runner";
|
|
3
|
+
import { type ContextEntry } from "./session-storage.js";
|
|
3
4
|
/**
|
|
4
5
|
* ACP extension key for subagent mode indicator
|
|
5
6
|
* Following ACP extensibility pattern with namespaced key
|
|
@@ -23,6 +24,15 @@ export interface CitationSource {
|
|
|
23
24
|
toolCallId: string;
|
|
24
25
|
sourceName?: string;
|
|
25
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Error thrown when mid-turn compaction requires the turn to be restarted
|
|
29
|
+
* with compacted context. The adapter catches this error and restarts
|
|
30
|
+
* the invocation with the updated context.
|
|
31
|
+
*/
|
|
32
|
+
export declare class MidTurnRestartError extends Error {
|
|
33
|
+
newContextEntry: ContextEntry;
|
|
34
|
+
constructor(message: string, newContextEntry: ContextEntry);
|
|
35
|
+
}
|
|
26
36
|
/** Adapts an Agent to speak the ACP protocol */
|
|
27
37
|
export declare class AgentAcpAdapter implements acp.Agent {
|
|
28
38
|
private connection;
|
|
@@ -13,6 +13,19 @@ const logger = createLogger("adapter");
|
|
|
13
13
|
* Following ACP extensibility pattern with namespaced key
|
|
14
14
|
*/
|
|
15
15
|
export const SUBAGENT_MODE_KEY = "town.com/isSubagent";
|
|
16
|
+
/**
|
|
17
|
+
* Error thrown when mid-turn compaction requires the turn to be restarted
|
|
18
|
+
* with compacted context. The adapter catches this error and restarts
|
|
19
|
+
* the invocation with the updated context.
|
|
20
|
+
*/
|
|
21
|
+
export class MidTurnRestartError extends Error {
|
|
22
|
+
newContextEntry;
|
|
23
|
+
constructor(message, newContextEntry) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "MidTurnRestartError";
|
|
26
|
+
this.newContextEntry = newContextEntry;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
16
29
|
/**
|
|
17
30
|
* Create a context snapshot based on the previous context
|
|
18
31
|
* Preserves full messages from previous context and adds new pointers
|
|
@@ -1456,31 +1469,35 @@ export class AgentAcpAdapter {
|
|
|
1456
1469
|
typeof rawOutput === "object" &&
|
|
1457
1470
|
"_compactionMeta" in rawOutput) {
|
|
1458
1471
|
const compactionMeta = rawOutput._compactionMeta;
|
|
1459
|
-
//
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1472
|
+
// Determine if actual compaction occurred:
|
|
1473
|
+
// 1. Action is a compaction action (not "none" or "no_action_needed")
|
|
1474
|
+
// 2. Tokens were actually saved (finalTokens < originalTokens)
|
|
1475
|
+
const isCompactionAction = compactionMeta.action === "compacted" ||
|
|
1476
|
+
compactionMeta.action === "truncated" ||
|
|
1477
|
+
compactionMeta.action === "compacted_then_truncated";
|
|
1478
|
+
const actuallyCompacted = isCompactionAction &&
|
|
1479
|
+
compactionMeta.originalTokens !== undefined &&
|
|
1480
|
+
compactionMeta.finalTokens !== undefined &&
|
|
1481
|
+
compactionMeta.finalTokens < compactionMeta.originalTokens;
|
|
1482
|
+
// Store in _meta for UI persistence only if actual compaction occurred
|
|
1483
|
+
if (actuallyCompacted) {
|
|
1484
|
+
if (!toolCallBlock._meta) {
|
|
1485
|
+
toolCallBlock._meta = {};
|
|
1486
|
+
}
|
|
1464
1487
|
toolCallBlock._meta.compactionAction = compactionMeta.action;
|
|
1465
|
-
}
|
|
1466
|
-
if (compactionMeta.originalTokens !== undefined) {
|
|
1467
1488
|
toolCallBlock._meta.originalTokens =
|
|
1468
1489
|
compactionMeta.originalTokens;
|
|
1469
|
-
}
|
|
1470
|
-
if (compactionMeta.finalTokens !== undefined) {
|
|
1471
1490
|
toolCallBlock._meta.finalTokens = compactionMeta.finalTokens;
|
|
1472
1491
|
}
|
|
1473
|
-
// Store original content only if compaction or truncation actually occurred
|
|
1474
|
-
const wasCompacted = compactionMeta.action === "compacted" ||
|
|
1475
|
-
compactionMeta.action === "truncated" ||
|
|
1476
|
-
compactionMeta.action === "compacted_then_truncated";
|
|
1477
1492
|
if (compactionMeta.originalContent &&
|
|
1478
|
-
|
|
1493
|
+
actuallyCompacted &&
|
|
1479
1494
|
this.storage) {
|
|
1480
1495
|
try {
|
|
1481
1496
|
const toolName = toolCallBlock.title || "unknown";
|
|
1482
1497
|
const originalContentPath = this.storage.saveToolOriginal(params.sessionId, toolName, outputMsg.toolCallId, compactionMeta.originalContent);
|
|
1483
|
-
|
|
1498
|
+
// _meta is guaranteed to be initialized since actuallyCompacted is true
|
|
1499
|
+
toolCallBlock._meta.originalContentPath =
|
|
1500
|
+
originalContentPath;
|
|
1484
1501
|
logger.info("Saved original content to artifacts", {
|
|
1485
1502
|
toolCallId: outputMsg.toolCallId,
|
|
1486
1503
|
toolName,
|
|
@@ -1577,23 +1594,25 @@ export class AgentAcpAdapter {
|
|
|
1577
1594
|
if (!toolCallBlock._meta) {
|
|
1578
1595
|
toolCallBlock._meta = {};
|
|
1579
1596
|
}
|
|
1580
|
-
// Store compaction action and stats
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1597
|
+
// Store compaction action and stats only when actual compaction occurred
|
|
1598
|
+
// Skip "none" and "no_action_needed" which mean no compaction was performed
|
|
1599
|
+
// Also verify that tokens were actually saved (finalTokens < originalTokens)
|
|
1600
|
+
const action = hookResult.metadata.action;
|
|
1601
|
+
const originalTokens = hookResult.metadata.originalTokens;
|
|
1602
|
+
const finalTokens = hookResult.metadata.finalTokens;
|
|
1603
|
+
const actuallyCompacted = action &&
|
|
1604
|
+
action !== "none" &&
|
|
1605
|
+
action !== "no_action_needed" &&
|
|
1606
|
+
originalTokens !== undefined &&
|
|
1607
|
+
finalTokens !== undefined &&
|
|
1608
|
+
finalTokens < originalTokens;
|
|
1609
|
+
if (actuallyCompacted) {
|
|
1610
|
+
toolCallBlock._meta.compactionAction = action;
|
|
1611
|
+
toolCallBlock._meta.originalTokens = originalTokens;
|
|
1612
|
+
toolCallBlock._meta.finalTokens = finalTokens;
|
|
1584
1613
|
}
|
|
1585
|
-
if
|
|
1586
|
-
|
|
1587
|
-
.originalTokens;
|
|
1588
|
-
}
|
|
1589
|
-
if (hookResult.metadata.finalTokens !== undefined) {
|
|
1590
|
-
toolCallBlock._meta.finalTokens = hookResult.metadata
|
|
1591
|
-
.finalTokens;
|
|
1592
|
-
}
|
|
1593
|
-
// Store original content if compaction occurred (action !== "none")
|
|
1594
|
-
if (hookResult.metadata.action &&
|
|
1595
|
-
hookResult.metadata.action !== "none" &&
|
|
1596
|
-
this.storage) {
|
|
1614
|
+
// Store original content only if actual compaction occurred
|
|
1615
|
+
if (actuallyCompacted && this.storage) {
|
|
1597
1616
|
try {
|
|
1598
1617
|
const toolName = toolCallBlock.title || "unknown";
|
|
1599
1618
|
const originalContentPath = this.storage.saveToolOriginal(params.sessionId, toolName, outputMsg.toolCallId, originalContentStr);
|
|
@@ -1615,6 +1634,34 @@ export class AgentAcpAdapter {
|
|
|
1615
1634
|
}
|
|
1616
1635
|
}
|
|
1617
1636
|
}
|
|
1637
|
+
// Apply context compaction if hook returned a new context entry
|
|
1638
|
+
// This happens when compaction_tool runs in the tool_response chain
|
|
1639
|
+
if (hookResult.newContextEntry) {
|
|
1640
|
+
session.context.push(hookResult.newContextEntry);
|
|
1641
|
+
logger.info("Context compacted by tool_response hook, new context entry added", {
|
|
1642
|
+
toolCallId: outputMsg.toolCallId,
|
|
1643
|
+
contextSize: hookResult.newContextEntry.context_size?.totalEstimated,
|
|
1644
|
+
compactedUpTo: hookResult.newContextEntry.compactedUpTo,
|
|
1645
|
+
});
|
|
1646
|
+
// Check if mid-turn compaction requires a restart
|
|
1647
|
+
if (hookResult.metadata?.requiresRestart) {
|
|
1648
|
+
logger.warn("Mid-turn compaction requires restart - aborting current turn", {
|
|
1649
|
+
toolCallId: outputMsg.toolCallId,
|
|
1650
|
+
tokensSaved: hookResult.metadata.tokensSaved,
|
|
1651
|
+
summaryTokens: hookResult.metadata.summaryTokens,
|
|
1652
|
+
});
|
|
1653
|
+
// Store the pending tool output in session for replay after restart
|
|
1654
|
+
// We'll include this as part of the compacted context
|
|
1655
|
+
session.pendingToolOutput = {
|
|
1656
|
+
toolCallId: outputMsg.toolCallId,
|
|
1657
|
+
toolName: toolCallBlock.title || "unknown",
|
|
1658
|
+
rawOutput,
|
|
1659
|
+
};
|
|
1660
|
+
// Throw an error to abort the current turn
|
|
1661
|
+
// The handlePrompt method will catch this and restart
|
|
1662
|
+
throw new MidTurnRestartError("Context compacted mid-turn, restart required", hookResult.newContextEntry);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1618
1665
|
}
|
|
1619
1666
|
}
|
|
1620
1667
|
// Store the (potentially modified) output
|
|
@@ -1877,6 +1924,29 @@ export class AgentAcpAdapter {
|
|
|
1877
1924
|
await saveCancelledMessage();
|
|
1878
1925
|
return { stopReason: "cancelled" };
|
|
1879
1926
|
}
|
|
1927
|
+
// Handle mid-turn compaction restart
|
|
1928
|
+
if (err instanceof MidTurnRestartError) {
|
|
1929
|
+
logger.warn("Mid-turn compaction restart triggered - rebuilding context and restarting invocation", {
|
|
1930
|
+
sessionId: params.sessionId,
|
|
1931
|
+
newContextSize: err.newContextEntry.context_size?.totalEstimated,
|
|
1932
|
+
});
|
|
1933
|
+
// Clear the pending tool output since it's now part of the context
|
|
1934
|
+
delete session.pendingToolOutput;
|
|
1935
|
+
// Notify UI that a restart is happening
|
|
1936
|
+
this.connection.sessionUpdate({
|
|
1937
|
+
sessionId: params.sessionId,
|
|
1938
|
+
update: {
|
|
1939
|
+
sessionUpdate: "agent_message_chunk",
|
|
1940
|
+
content: {
|
|
1941
|
+
type: "text",
|
|
1942
|
+
text: "\n\n---\n*Context compacted mid-turn. Continuing from summary...*\n\n",
|
|
1943
|
+
},
|
|
1944
|
+
},
|
|
1945
|
+
});
|
|
1946
|
+
// Recursive call with the updated session (already modified in place)
|
|
1947
|
+
// _promptImpl will resolve the updated context from session.context
|
|
1948
|
+
return this._promptImpl(params);
|
|
1949
|
+
}
|
|
1880
1950
|
throw err;
|
|
1881
1951
|
}
|
|
1882
1952
|
// Store the complete assistant response in session messages
|
|
@@ -12,6 +12,11 @@ export declare const McpConfigSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
12
12
|
url: z.ZodString;
|
|
13
13
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
14
14
|
}, z.core.$strip>, z.ZodString]>;
|
|
15
|
+
/** Individual callback configuration schema. */
|
|
16
|
+
export declare const CallbackConfigSchema: z.ZodObject<{
|
|
17
|
+
name: z.ZodString;
|
|
18
|
+
setting: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
19
|
+
}, z.core.$strip>;
|
|
15
20
|
/** Hook configuration schema. */
|
|
16
21
|
export declare const HookConfigSchema: z.ZodObject<{
|
|
17
22
|
type: z.ZodEnum<{
|
|
@@ -22,8 +27,12 @@ export declare const HookConfigSchema: z.ZodObject<{
|
|
|
22
27
|
threshold: z.ZodNumber;
|
|
23
28
|
}, z.core.$strip>, z.ZodObject<{
|
|
24
29
|
maxTokensSize: z.ZodOptional<z.ZodNumber>;
|
|
25
|
-
}, z.core.$strip>]>>;
|
|
26
|
-
callback: z.ZodString
|
|
30
|
+
}, z.core.$strip>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
31
|
+
callback: z.ZodOptional<z.ZodString>;
|
|
32
|
+
callbacks: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
33
|
+
name: z.ZodString;
|
|
34
|
+
setting: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
35
|
+
}, z.core.$strip>>>;
|
|
27
36
|
}, z.core.$strip>;
|
|
28
37
|
/** Initial message configuration schema. */
|
|
29
38
|
export declare const InitialMessageSchema: z.ZodObject<{
|
|
@@ -96,8 +105,12 @@ export declare const AgentDefinitionSchema: z.ZodObject<{
|
|
|
96
105
|
threshold: z.ZodNumber;
|
|
97
106
|
}, z.core.$strip>, z.ZodObject<{
|
|
98
107
|
maxTokensSize: z.ZodOptional<z.ZodNumber>;
|
|
99
|
-
}, z.core.$strip>]>>;
|
|
100
|
-
callback: z.ZodString
|
|
108
|
+
}, z.core.$strip>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
109
|
+
callback: z.ZodOptional<z.ZodString>;
|
|
110
|
+
callbacks: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
111
|
+
name: z.ZodString;
|
|
112
|
+
setting: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
113
|
+
}, z.core.$strip>>>;
|
|
101
114
|
}, z.core.$strip>>>;
|
|
102
115
|
initialMessage: z.ZodOptional<z.ZodObject<{
|
|
103
116
|
enabled: z.ZodBoolean;
|
package/dist/definition/index.js
CHANGED
|
@@ -53,9 +53,18 @@ const ToolSchema = z.union([
|
|
|
53
53
|
FilesystemToolSchema,
|
|
54
54
|
DirectToolSchema,
|
|
55
55
|
]);
|
|
56
|
+
/** Individual callback configuration schema. */
|
|
57
|
+
export const CallbackConfigSchema = z.object({
|
|
58
|
+
/** Callback reference - predefined hook name or file path */
|
|
59
|
+
name: z.string(),
|
|
60
|
+
/** Callback-specific settings */
|
|
61
|
+
setting: z.record(z.string(), z.unknown()).optional(),
|
|
62
|
+
});
|
|
56
63
|
/** Hook configuration schema. */
|
|
57
|
-
export const HookConfigSchema = z
|
|
64
|
+
export const HookConfigSchema = z
|
|
65
|
+
.object({
|
|
58
66
|
type: z.enum(["context_size", "tool_response"]),
|
|
67
|
+
/** @deprecated Use callbacks array instead */
|
|
59
68
|
setting: z
|
|
60
69
|
.union([
|
|
61
70
|
// For context_size hooks
|
|
@@ -66,9 +75,17 @@ export const HookConfigSchema = z.object({
|
|
|
66
75
|
z.object({
|
|
67
76
|
maxTokensSize: z.number().min(0).optional(),
|
|
68
77
|
}),
|
|
78
|
+
// Generic settings for callbacks
|
|
79
|
+
z.record(z.string(), z.unknown()),
|
|
69
80
|
])
|
|
70
81
|
.optional(),
|
|
71
|
-
|
|
82
|
+
/** @deprecated Use callbacks array instead */
|
|
83
|
+
callback: z.string().optional(),
|
|
84
|
+
/** Array of callback configurations to execute in order */
|
|
85
|
+
callbacks: z.array(CallbackConfigSchema).optional(),
|
|
86
|
+
})
|
|
87
|
+
.refine((data) => data.callback || data.callbacks, {
|
|
88
|
+
message: "Either 'callback' or 'callbacks' must be provided",
|
|
72
89
|
});
|
|
73
90
|
/** Initial message configuration schema. */
|
|
74
91
|
export const InitialMessageSchema = z.object({
|
|
@@ -49,8 +49,12 @@ export declare const zAgentRunnerParams: z.ZodObject<{
|
|
|
49
49
|
threshold: z.ZodNumber;
|
|
50
50
|
}, z.core.$strip>, z.ZodObject<{
|
|
51
51
|
maxTokensSize: z.ZodOptional<z.ZodNumber>;
|
|
52
|
-
}, z.core.$strip>]>>;
|
|
53
|
-
callback: z.ZodString
|
|
52
|
+
}, z.core.$strip>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
53
|
+
callback: z.ZodOptional<z.ZodString>;
|
|
54
|
+
callbacks: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
55
|
+
name: z.ZodString;
|
|
56
|
+
setting: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
57
|
+
}, z.core.$strip>>>;
|
|
54
58
|
}, z.core.$strip>>>;
|
|
55
59
|
initialMessage: z.ZodOptional<z.ZodObject<{
|
|
56
60
|
enabled: z.ZodBoolean;
|
|
@@ -16,7 +16,8 @@ export declare class HookExecutor {
|
|
|
16
16
|
private agentDefinition;
|
|
17
17
|
private storage;
|
|
18
18
|
private sessionId;
|
|
19
|
-
|
|
19
|
+
private agentDir;
|
|
20
|
+
constructor(hooks: HookConfig[], model: string, loadCallback: (callbackRef: string) => Promise<HookCallback>, onNotification?: OnHookNotification, agentDefinition?: Readonly<AgentDefinition>, storage?: HookStorageInterface, sessionId?: string, agentDir?: string);
|
|
20
21
|
/**
|
|
21
22
|
* Emit a notification - sends immediately if callback provided, otherwise collects for batch return
|
|
22
23
|
*/
|
|
@@ -31,7 +32,8 @@ export declare class HookExecutor {
|
|
|
31
32
|
}>;
|
|
32
33
|
private executeContextSizeHook;
|
|
33
34
|
/**
|
|
34
|
-
* Execute tool_response hooks when a tool returns output
|
|
35
|
+
* Execute tool_response hooks when a tool returns output.
|
|
36
|
+
* Chains callbacks in order, passing modified output between them.
|
|
35
37
|
*/
|
|
36
38
|
executeToolResponseHooks(session: ReadonlySession, currentContextTokens: number, toolResponse: {
|
|
37
39
|
toolCallId: string;
|
|
@@ -43,7 +45,7 @@ export declare class HookExecutor {
|
|
|
43
45
|
modifiedOutput?: Record<string, unknown>;
|
|
44
46
|
truncationWarning?: string;
|
|
45
47
|
metadata?: Record<string, unknown>;
|
|
48
|
+
newContextEntry?: ContextEntry;
|
|
46
49
|
notifications: HookNotification[];
|
|
47
50
|
}>;
|
|
48
|
-
private executeToolResponseHook;
|
|
49
51
|
}
|