@townco/agent 0.1.72 → 0.1.74
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 +98 -5
- package/dist/acp-server/http.js +71 -14
- package/dist/acp-server/session-storage.d.ts +40 -0
- package/dist/acp-server/session-storage.js +26 -0
- package/dist/runner/agent-runner.d.ts +11 -1
- package/dist/runner/hooks/predefined/tool-response-compactor.js +73 -19
- package/dist/runner/langchain/index.js +104 -29
- package/dist/runner/langchain/model-factory.js +6 -9
- package/dist/runner/langchain/otel-callbacks.d.ts +7 -1
- package/dist/runner/langchain/otel-callbacks.js +80 -20
- package/dist/runner/langchain/tools/filesystem.js +15 -0
- package/dist/runner/langchain/tools/subagent-connections.d.ts +34 -0
- package/dist/runner/langchain/tools/subagent-connections.js +32 -1
- package/dist/runner/langchain/tools/subagent.js +66 -8
- package/dist/runner/langchain/tools/todo.js +4 -0
- package/dist/runner/langchain/tools/web_search.d.ts +24 -0
- package/dist/runner/langchain/tools/web_search.js +42 -11
- package/dist/runner/tool-loader.d.ts +10 -0
- package/dist/runner/tool-loader.js +1 -0
- package/dist/runner/tools.d.ts +2 -2
- package/dist/runner/tools.js +1 -0
- package/dist/telemetry/index.d.ts +5 -0
- package/dist/telemetry/index.js +8 -0
- package/dist/telemetry/setup.js +10 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/tool.d.ts +5 -0
- package/dist/utils/tool.js +1 -0
- package/package.json +6 -6
|
@@ -7,7 +7,7 @@ import { createLogger as coreCreateLogger } from "@townco/core";
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { SUBAGENT_MODE_KEY } from "../../../acp-server/adapter.js";
|
|
9
9
|
import { findAvailablePort } from "./port-utils.js";
|
|
10
|
-
import { emitSubagentConnection, hashQuery } from "./subagent-connections.js";
|
|
10
|
+
import { emitSubagentConnection, emitSubagentMessages, hashQuery, } from "./subagent-connections.js";
|
|
11
11
|
/**
|
|
12
12
|
* Name of the Task tool created by makeSubagentsTool
|
|
13
13
|
*/
|
|
@@ -317,6 +317,15 @@ async function querySubagent(agentName, agentPath, agentWorkingDirectory, query)
|
|
|
317
317
|
// Step 3: Connect to SSE for receiving streaming responses
|
|
318
318
|
sseAbortController = new AbortController();
|
|
319
319
|
let responseText = "";
|
|
320
|
+
// Track full message structure for session storage
|
|
321
|
+
const currentMessage = {
|
|
322
|
+
id: `subagent-${Date.now()}`,
|
|
323
|
+
content: "",
|
|
324
|
+
contentBlocks: [],
|
|
325
|
+
toolCalls: [],
|
|
326
|
+
};
|
|
327
|
+
// Map of tool call IDs to their indices in toolCalls array
|
|
328
|
+
const toolCallMap = new Map();
|
|
320
329
|
const ssePromise = (async () => {
|
|
321
330
|
const sseResponse = await fetch(`${baseUrl}/events`, {
|
|
322
331
|
headers: { "X-Session-ID": sessionId },
|
|
@@ -342,18 +351,63 @@ async function querySubagent(agentName, agentPath, agentWorkingDirectory, query)
|
|
|
342
351
|
continue;
|
|
343
352
|
try {
|
|
344
353
|
const message = JSON.parse(data);
|
|
345
|
-
|
|
346
|
-
if (message.method
|
|
347
|
-
|
|
348
|
-
|
|
354
|
+
const update = message.params?.update;
|
|
355
|
+
if (message.method !== "session/update" || !update)
|
|
356
|
+
continue;
|
|
357
|
+
// Handle agent_message_chunk - accumulate text
|
|
358
|
+
if (update.sessionUpdate === "agent_message_chunk") {
|
|
359
|
+
const content = update.content;
|
|
349
360
|
if (content?.type === "text" &&
|
|
350
361
|
typeof content.text === "string") {
|
|
351
362
|
responseText += content.text;
|
|
363
|
+
currentMessage.content += content.text;
|
|
364
|
+
// Add to contentBlocks - append to last text block or create new one
|
|
365
|
+
const lastBlock = currentMessage.contentBlocks[currentMessage.contentBlocks.length - 1];
|
|
366
|
+
if (lastBlock && lastBlock.type === "text") {
|
|
367
|
+
lastBlock.text += content.text;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
currentMessage.contentBlocks.push({
|
|
371
|
+
type: "text",
|
|
372
|
+
text: content.text,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
352
375
|
}
|
|
353
376
|
}
|
|
354
|
-
//
|
|
355
|
-
if (
|
|
356
|
-
|
|
377
|
+
// Handle tool_call - track new tool calls
|
|
378
|
+
if (update.sessionUpdate === "tool_call" && update.toolCallId) {
|
|
379
|
+
const toolCall = {
|
|
380
|
+
id: update.toolCallId,
|
|
381
|
+
title: update.title || "Tool call",
|
|
382
|
+
prettyName: update._meta?.prettyName,
|
|
383
|
+
icon: update._meta?.icon,
|
|
384
|
+
status: update.status || "pending",
|
|
385
|
+
};
|
|
386
|
+
currentMessage.toolCalls.push(toolCall);
|
|
387
|
+
toolCallMap.set(update.toolCallId, currentMessage.toolCalls.length - 1);
|
|
388
|
+
// Add to contentBlocks for interleaved display
|
|
389
|
+
currentMessage.contentBlocks.push({
|
|
390
|
+
type: "tool_call",
|
|
391
|
+
toolCall,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
// Handle tool_call_update - update existing tool call status
|
|
395
|
+
if (update.sessionUpdate === "tool_call_update" &&
|
|
396
|
+
update.toolCallId) {
|
|
397
|
+
const idx = toolCallMap.get(update.toolCallId);
|
|
398
|
+
if (idx !== undefined && currentMessage.toolCalls[idx]) {
|
|
399
|
+
if (update.status) {
|
|
400
|
+
currentMessage.toolCalls[idx].status =
|
|
401
|
+
update.status;
|
|
402
|
+
}
|
|
403
|
+
// Also update in contentBlocks
|
|
404
|
+
const block = currentMessage.contentBlocks.find((b) => b.type === "tool_call" &&
|
|
405
|
+
b.toolCall.id === update.toolCallId);
|
|
406
|
+
if (block && update.status) {
|
|
407
|
+
block.toolCall.status =
|
|
408
|
+
update.status;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
357
411
|
}
|
|
358
412
|
}
|
|
359
413
|
catch {
|
|
@@ -404,6 +458,10 @@ async function querySubagent(agentName, agentPath, agentWorkingDirectory, query)
|
|
|
404
458
|
ssePromise.catch(() => { }), // Ignore abort errors
|
|
405
459
|
new Promise((r) => setTimeout(r, 1000)),
|
|
406
460
|
]);
|
|
461
|
+
// Emit accumulated messages for session storage
|
|
462
|
+
if (currentMessage.content || currentMessage.toolCalls.length > 0) {
|
|
463
|
+
emitSubagentMessages(queryHash, [currentMessage]);
|
|
464
|
+
}
|
|
407
465
|
return responseText;
|
|
408
466
|
}
|
|
409
467
|
finally {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
/** Create web search tools using direct EXA_API_KEY */
|
|
2
3
|
export declare function makeWebSearchTools(): readonly [import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
3
4
|
query: z.ZodString;
|
|
4
5
|
}, z.core.$strip>, {
|
|
@@ -21,3 +22,26 @@ export declare function makeWebSearchTools(): readonly [import("langchain").Dyna
|
|
|
21
22
|
url: string;
|
|
22
23
|
prompt: string;
|
|
23
24
|
}, string>];
|
|
25
|
+
/** Create web search tools using Town proxy */
|
|
26
|
+
export declare function makeTownWebSearchTools(): readonly [import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
27
|
+
query: z.ZodString;
|
|
28
|
+
}, z.core.$strip>, {
|
|
29
|
+
query: string;
|
|
30
|
+
}, {
|
|
31
|
+
query: string;
|
|
32
|
+
}, string | import("exa-js").SearchResponse<{
|
|
33
|
+
numResults: number;
|
|
34
|
+
type: "auto";
|
|
35
|
+
text: {
|
|
36
|
+
maxCharacters: number;
|
|
37
|
+
};
|
|
38
|
+
}>>, import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
39
|
+
url: z.ZodString;
|
|
40
|
+
prompt: z.ZodString;
|
|
41
|
+
}, z.core.$strip>, {
|
|
42
|
+
url: string;
|
|
43
|
+
prompt: string;
|
|
44
|
+
}, {
|
|
45
|
+
url: string;
|
|
46
|
+
prompt: string;
|
|
47
|
+
}, string>];
|
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { getShedAuth } from "@townco/core/auth";
|
|
2
3
|
import Exa from "exa-js";
|
|
3
4
|
import { tool } from "langchain";
|
|
4
5
|
import { z } from "zod";
|
|
5
|
-
let
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
let _directExaClient = null;
|
|
7
|
+
let _townExaClient = null;
|
|
8
|
+
/** Get Exa client using direct EXA_API_KEY environment variable */
|
|
9
|
+
function getDirectExaClient() {
|
|
10
|
+
if (_directExaClient) {
|
|
11
|
+
return _directExaClient;
|
|
9
12
|
}
|
|
10
13
|
const apiKey = process.env.EXA_API_KEY;
|
|
11
14
|
if (!apiKey) {
|
|
12
15
|
throw new Error("EXA_API_KEY environment variable is required to use the web_search tool. " +
|
|
13
16
|
"Please set it to your Exa API key from https://exa.ai");
|
|
14
17
|
}
|
|
15
|
-
|
|
16
|
-
return
|
|
18
|
+
_directExaClient = new Exa(apiKey);
|
|
19
|
+
return _directExaClient;
|
|
17
20
|
}
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
/** Get Exa client using Town proxy with authenticated credentials */
|
|
22
|
+
function getTownExaClient() {
|
|
23
|
+
if (_townExaClient) {
|
|
24
|
+
return _townExaClient;
|
|
25
|
+
}
|
|
26
|
+
const shedAuth = getShedAuth();
|
|
27
|
+
if (!shedAuth) {
|
|
28
|
+
throw new Error("Not logged in. Run 'town login' or set SHED_API_KEY to use the town_web_search tool.");
|
|
29
|
+
}
|
|
30
|
+
_townExaClient = new Exa(shedAuth.accessToken, `${shedAuth.shedUrl}/api/exa`);
|
|
31
|
+
return _townExaClient;
|
|
32
|
+
}
|
|
33
|
+
function makeWebSearchToolsInternal(getClient) {
|
|
20
34
|
const webSearch = tool(async ({ query }) => {
|
|
21
|
-
const client =
|
|
35
|
+
const client = getClient();
|
|
22
36
|
const result = await client.searchAndContents(query, {
|
|
23
37
|
numResults: 5,
|
|
24
38
|
type: "auto",
|
|
@@ -48,9 +62,13 @@ export function makeWebSearchTools() {
|
|
|
48
62
|
});
|
|
49
63
|
webSearch.prettyName = "Web Search";
|
|
50
64
|
webSearch.icon = "Globe";
|
|
51
|
-
|
|
65
|
+
webSearch.verbiage = {
|
|
66
|
+
active: "Searching the web for {query}",
|
|
67
|
+
past: "Searched the web for {query}",
|
|
68
|
+
paramKey: "query",
|
|
69
|
+
};
|
|
52
70
|
const webFetch = tool(async ({ url, prompt }) => {
|
|
53
|
-
const client =
|
|
71
|
+
const client = getClient();
|
|
54
72
|
try {
|
|
55
73
|
const result = await client.getContents([url], {
|
|
56
74
|
text: true,
|
|
@@ -122,8 +140,21 @@ export function makeWebSearchTools() {
|
|
|
122
140
|
});
|
|
123
141
|
webFetch.prettyName = "Web Fetch";
|
|
124
142
|
webFetch.icon = "Link";
|
|
143
|
+
webFetch.verbiage = {
|
|
144
|
+
active: "Fetching {url}",
|
|
145
|
+
past: "Fetched {url}",
|
|
146
|
+
paramKey: "url",
|
|
147
|
+
};
|
|
125
148
|
return [webSearch, webFetch];
|
|
126
149
|
}
|
|
150
|
+
/** Create web search tools using direct EXA_API_KEY */
|
|
151
|
+
export function makeWebSearchTools() {
|
|
152
|
+
return makeWebSearchToolsInternal(getDirectExaClient);
|
|
153
|
+
}
|
|
154
|
+
/** Create web search tools using Town proxy */
|
|
155
|
+
export function makeTownWebSearchTools() {
|
|
156
|
+
return makeWebSearchToolsInternal(getTownExaClient);
|
|
157
|
+
}
|
|
127
158
|
function buildWebFetchUserMessage(pageContent, prompt) {
|
|
128
159
|
return `Web page content:
|
|
129
160
|
---
|
|
@@ -6,6 +6,11 @@ export type CustomToolModule = {
|
|
|
6
6
|
description: string;
|
|
7
7
|
prettyName?: string;
|
|
8
8
|
icon?: string;
|
|
9
|
+
verbiage?: {
|
|
10
|
+
active: string;
|
|
11
|
+
past: string;
|
|
12
|
+
paramKey?: string;
|
|
13
|
+
};
|
|
9
14
|
};
|
|
10
15
|
export type ResolvedCustomTool = {
|
|
11
16
|
fn: (input: unknown) => unknown | Promise<unknown>;
|
|
@@ -14,5 +19,10 @@ export type ResolvedCustomTool = {
|
|
|
14
19
|
description: string;
|
|
15
20
|
prettyName?: string;
|
|
16
21
|
icon?: string;
|
|
22
|
+
verbiage?: {
|
|
23
|
+
active: string;
|
|
24
|
+
past: string;
|
|
25
|
+
paramKey?: string;
|
|
26
|
+
};
|
|
17
27
|
};
|
|
18
28
|
export declare function loadCustomToolModule(modulePath: string): Promise<ResolvedCustomTool>;
|
|
@@ -37,6 +37,7 @@ export async function loadCustomToolModule(modulePath) {
|
|
|
37
37
|
description: mod.description,
|
|
38
38
|
...(mod.prettyName && { prettyName: mod.prettyName }),
|
|
39
39
|
...(mod.icon && { icon: mod.icon }),
|
|
40
|
+
...(mod.verbiage && { verbiage: mod.verbiage }),
|
|
40
41
|
};
|
|
41
42
|
// Cache the resolved tool
|
|
42
43
|
customToolCache.set(modulePath, resolved);
|
package/dist/runner/tools.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/** Built-in tool types. */
|
|
3
|
-
export declare const zBuiltInToolType: z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">, z.ZodLiteral<"filesystem">, z.ZodLiteral<"generate_image">, z.ZodLiteral<"browser">]>;
|
|
3
|
+
export declare const zBuiltInToolType: z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">, z.ZodLiteral<"town_web_search">, z.ZodLiteral<"filesystem">, z.ZodLiteral<"generate_image">, z.ZodLiteral<"browser">]>;
|
|
4
4
|
/** Subagent configuration schema for Task tools. */
|
|
5
5
|
export declare const zSubagentConfig: z.ZodObject<{
|
|
6
6
|
agentName: z.ZodString;
|
|
@@ -23,7 +23,7 @@ declare const zDirectTool: z.ZodObject<{
|
|
|
23
23
|
}, z.core.$strip>>>;
|
|
24
24
|
}, z.core.$strip>;
|
|
25
25
|
/** Tool type - can be a built-in tool string or custom tool object. */
|
|
26
|
-
export declare const zToolType: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">, z.ZodLiteral<"filesystem">, z.ZodLiteral<"generate_image">, z.ZodLiteral<"browser">]>, z.ZodObject<{
|
|
26
|
+
export declare const zToolType: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">, z.ZodLiteral<"town_web_search">, z.ZodLiteral<"filesystem">, z.ZodLiteral<"generate_image">, z.ZodLiteral<"browser">]>, z.ZodObject<{
|
|
27
27
|
type: z.ZodLiteral<"custom">;
|
|
28
28
|
modulePath: z.ZodString;
|
|
29
29
|
}, z.core.$strip>, z.ZodObject<{
|
package/dist/runner/tools.js
CHANGED
|
@@ -28,6 +28,11 @@ declare class AgentTelemetry {
|
|
|
28
28
|
* @param attributes - Attributes to merge with existing base attributes
|
|
29
29
|
*/
|
|
30
30
|
setBaseAttributes(attributes: Record<string, string | number>): void;
|
|
31
|
+
/**
|
|
32
|
+
* Remove a base attribute
|
|
33
|
+
* @param key - Attribute key to remove
|
|
34
|
+
*/
|
|
35
|
+
clearBaseAttribute(key: string): void;
|
|
31
36
|
/**
|
|
32
37
|
* Start a new span
|
|
33
38
|
* @param name - Span name
|
package/dist/telemetry/index.js
CHANGED
|
@@ -40,6 +40,14 @@ class AgentTelemetry {
|
|
|
40
40
|
...attributes,
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Remove a base attribute
|
|
45
|
+
* @param key - Attribute key to remove
|
|
46
|
+
*/
|
|
47
|
+
clearBaseAttribute(key) {
|
|
48
|
+
const { [key]: _, ...rest } = this.baseAttributes;
|
|
49
|
+
this.baseAttributes = rest;
|
|
50
|
+
}
|
|
43
51
|
/**
|
|
44
52
|
* Start a new span
|
|
45
53
|
* @param name - Span name
|
package/dist/telemetry/setup.js
CHANGED
|
@@ -68,10 +68,12 @@ export function initializeOpenTelemetry(options = {}) {
|
|
|
68
68
|
// false "Request timed out" errors. The export usually succeeds anyway.
|
|
69
69
|
// Only log non-timeout errors as actual errors.
|
|
70
70
|
const errorMsg = result.error?.message ?? "";
|
|
71
|
-
if (errorMsg.includes("timed out")
|
|
71
|
+
if (errorMsg.includes("timed out") ||
|
|
72
|
+
errorMsg.includes("Request timed out")) {
|
|
72
73
|
if (debug) {
|
|
73
|
-
console.log(`⚠️ Export reported timeout (Bun http bug - data
|
|
74
|
+
console.log(`⚠️ Export reported timeout (Bun http bug - data successfully sent)`);
|
|
74
75
|
}
|
|
76
|
+
// Don't log timeout errors at all - they're false positives
|
|
75
77
|
}
|
|
76
78
|
else {
|
|
77
79
|
console.error(`❌ Failed to export spans:`, result.error);
|
|
@@ -141,7 +143,12 @@ export function initializeOpenTelemetry(options = {}) {
|
|
|
141
143
|
}
|
|
142
144
|
}
|
|
143
145
|
catch (error) {
|
|
144
|
-
|
|
146
|
+
// Filter out Bun HTTP timeout errors - they're false positives
|
|
147
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
148
|
+
if (!errorMsg.includes("timed out") &&
|
|
149
|
+
!errorMsg.includes("Request timed out")) {
|
|
150
|
+
console.error("Error flushing telemetry:", error);
|
|
151
|
+
}
|
|
145
152
|
}
|
|
146
153
|
};
|
|
147
154
|
return { provider, loggerProvider, shutdown };
|