@townco/agent 0.1.9 → 0.1.12

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.
Files changed (44) hide show
  1. package/dist/acp-server/adapter.d.ts +10 -14
  2. package/dist/acp-server/adapter.js +72 -73
  3. package/dist/acp-server/cli.d.ts +1 -3
  4. package/dist/acp-server/cli.js +5 -9
  5. package/dist/acp-server/http.d.ts +1 -3
  6. package/dist/acp-server/http.js +163 -173
  7. package/dist/bin.js +0 -0
  8. package/dist/definition/index.d.ts +4 -1
  9. package/dist/definition/index.js +8 -1
  10. package/dist/index.js +13 -12
  11. package/dist/runner/agent-runner.d.ts +4 -1
  12. package/dist/runner/agent-runner.js +4 -4
  13. package/dist/runner/index.d.ts +1 -3
  14. package/dist/runner/index.js +14 -18
  15. package/dist/runner/langchain/index.d.ts +8 -19
  16. package/dist/runner/langchain/index.js +55 -6
  17. package/dist/runner/langchain/tools/todo.d.ts +32 -48
  18. package/dist/runner/langchain/tools/todo.js +13 -16
  19. package/dist/runner/langchain/tools/web_search.d.ts +1 -1
  20. package/dist/runner/langchain/tools/web_search.js +18 -21
  21. package/dist/runner/tool-loader.d.ts +14 -0
  22. package/dist/runner/tool-loader.js +42 -0
  23. package/dist/runner/tools.d.ts +8 -7
  24. package/dist/runner/tools.js +12 -4
  25. package/dist/scaffold/bundle.d.ts +1 -1
  26. package/dist/scaffold/bundle.js +1 -1
  27. package/dist/scaffold/copy-gui.js +1 -1
  28. package/dist/scaffold/index.d.ts +8 -10
  29. package/dist/scaffold/index.js +82 -90
  30. package/dist/storage/index.js +24 -23
  31. package/dist/templates/index.d.ts +4 -1
  32. package/dist/templates/index.js +7 -3
  33. package/dist/test-script.js +11 -12
  34. package/dist/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +10 -2
  36. package/templates/index.ts +11 -4
  37. package/dist/definition/mcp.d.ts +0 -0
  38. package/dist/definition/mcp.js +0 -0
  39. package/dist/definition/tools/todo.d.ts +0 -49
  40. package/dist/definition/tools/todo.js +0 -80
  41. package/dist/definition/tools/web_search.d.ts +0 -4
  42. package/dist/definition/tools/web_search.js +0 -26
  43. package/dist/example.d.ts +0 -2
  44. package/dist/example.js +0 -19
@@ -3,7 +3,10 @@ import { z } from "zod";
3
3
  export declare const zAgentRunnerParams: z.ZodObject<{
4
4
  systemPrompt: z.ZodNullable<z.ZodString>;
5
5
  model: z.ZodString;
6
- tools: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">]>>>;
6
+ tools: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">]>, z.ZodObject<{
7
+ type: z.ZodLiteral<"custom">;
8
+ modulePath: z.ZodString;
9
+ }, z.core.$strip>]>>>;
7
10
  mcps: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
8
11
  name: z.ZodString;
9
12
  transport: z.ZodLiteral<"stdio">;
@@ -2,8 +2,8 @@ import { z } from "zod";
2
2
  import { McpConfigSchema } from "../definition";
3
3
  import { zToolType } from "./tools";
4
4
  export const zAgentRunnerParams = z.object({
5
- systemPrompt: z.string().nullable(),
6
- model: z.string(),
7
- tools: z.array(zToolType).optional(),
8
- mcps: z.array(McpConfigSchema).optional(),
5
+ systemPrompt: z.string().nullable(),
6
+ model: z.string(),
7
+ tools: z.array(zToolType).optional(),
8
+ mcps: z.array(McpConfigSchema).optional(),
9
9
  });
@@ -1,6 +1,4 @@
1
1
  import type { AgentDefinition } from "../definition";
2
2
  import { type AgentRunner } from "./agent-runner";
3
3
  export type { AgentRunner };
4
- export declare const makeRunnerFromDefinition: (
5
- definition: AgentDefinition,
6
- ) => AgentRunner;
4
+ export declare const makeRunnerFromDefinition: (definition: AgentDefinition) => AgentRunner;
@@ -1,22 +1,18 @@
1
1
  import { zAgentRunnerParams } from "./agent-runner";
2
2
  import { LangchainAgent } from "./langchain";
3
3
  export const makeRunnerFromDefinition = (definition) => {
4
- const agentRunnerParams = zAgentRunnerParams.safeParse(definition);
5
- if (!agentRunnerParams.success) {
6
- throw new Error(
7
- `Invalid agent definition: ${agentRunnerParams.error.message}`,
8
- );
9
- }
10
- switch (definition.harnessImplementation) {
11
- case undefined:
12
- case "langchain": {
13
- return new LangchainAgent(agentRunnerParams.data);
14
- }
15
- default: {
16
- const _exhaustiveCheck = definition.harnessImplementation;
17
- throw new Error(
18
- `Unsupported harness implementation: ${definition.harnessImplementation}`,
19
- );
20
- }
21
- }
4
+ const agentRunnerParams = zAgentRunnerParams.safeParse(definition);
5
+ if (!agentRunnerParams.success) {
6
+ throw new Error(`Invalid agent definition: ${agentRunnerParams.error.message}`);
7
+ }
8
+ switch (definition.harnessImplementation) {
9
+ case undefined:
10
+ case "langchain": {
11
+ return new LangchainAgent(agentRunnerParams.data);
12
+ }
13
+ default: {
14
+ const _exhaustiveCheck = definition.harnessImplementation;
15
+ throw new Error(`Unsupported harness implementation: ${definition.harnessImplementation}`);
16
+ }
17
+ }
22
18
  };
@@ -1,26 +1,15 @@
1
- import type {
2
- PromptResponse,
3
- SessionNotification,
4
- } from "@agentclientprotocol/sdk";
1
+ import type { PromptResponse, SessionNotification } from "@agentclientprotocol/sdk";
5
2
  import { type DynamicStructuredTool, type Tool } from "langchain";
6
- import type {
7
- AgentRunner,
8
- CreateAgentRunnerParams,
9
- InvokeRequest,
10
- } from "../agent-runner";
11
- import type { ToolType } from "../tools";
3
+ import type { AgentRunner, CreateAgentRunnerParams, InvokeRequest } from "../agent-runner";
4
+ import type { BuiltInToolType } from "../tools";
12
5
  type LangchainTool = DynamicStructuredTool | Tool;
13
6
  /** Lazily-loaded langchain tools */
14
7
  type LazyLangchainTool = MakeLazy<LangchainTool>;
15
8
  type MakeLazy<T> = T extends LangchainTool ? () => T : never;
16
- export declare const TOOL_REGISTRY: Record<
17
- ToolType,
18
- LangchainTool | LazyLangchainTool
19
- >;
9
+ export declare const TOOL_REGISTRY: Record<BuiltInToolType, LangchainTool | LazyLangchainTool>;
20
10
  export declare class LangchainAgent implements AgentRunner {
21
- definition: CreateAgentRunnerParams;
22
- constructor(params: CreateAgentRunnerParams);
23
- invoke(
24
- req: InvokeRequest,
25
- ): AsyncGenerator<SessionNotification["update"], PromptResponse, undefined>;
11
+ definition: CreateAgentRunnerParams;
12
+ constructor(params: CreateAgentRunnerParams);
13
+ invoke(req: InvokeRequest): AsyncGenerator<SessionNotification["update"], PromptResponse, undefined>;
26
14
  }
15
+ export {};
@@ -1,6 +1,7 @@
1
1
  import { MultiServerMCPClient } from "@langchain/mcp-adapters";
2
2
  import { AIMessageChunk, createAgent, ToolMessage, tool, } from "langchain";
3
3
  import { z } from "zod";
4
+ import { loadCustomToolModule } from "../tool-loader";
4
5
  import { todoItemSchema, todoWrite } from "./tools/todo";
5
6
  import { makeWebSearchTool } from "./tools/web_search";
6
7
  const getWeather = tool(({ city }) => `It's always sunny in ${city}!`, {
@@ -15,6 +16,27 @@ export const TOOL_REGISTRY = {
15
16
  get_weather: getWeather,
16
17
  web_search: makeWebSearchTool,
17
18
  };
19
+ // ============================================================================
20
+ // Custom tool loading
21
+ // ============================================================================
22
+ function toLangchainTool(resolved) {
23
+ return tool(resolved.fn, {
24
+ name: resolved.name,
25
+ description: resolved.description,
26
+ schema: resolved.schema,
27
+ });
28
+ }
29
+ async function loadCustomTool(modulePath) {
30
+ const resolved = await loadCustomToolModule(modulePath);
31
+ return toLangchainTool(resolved);
32
+ }
33
+ async function loadCustomTools(modulePaths) {
34
+ const tools = [];
35
+ for (const modulePath of modulePaths) {
36
+ tools.push(await loadCustomTool(modulePath));
37
+ }
38
+ return tools;
39
+ }
18
40
  export class LangchainAgent {
19
41
  definition;
20
42
  constructor(params) {
@@ -23,13 +45,40 @@ export class LangchainAgent {
23
45
  async *invoke(req) {
24
46
  // Track todo_write tool call IDs to suppress their tool_call notifications
25
47
  const todoWriteToolCallIds = new Set();
26
- const enabledTools = (this.definition.tools ?? []).map((name) => {
27
- const tool = TOOL_REGISTRY[name];
28
- if (typeof tool === "function") {
29
- return tool();
48
+ // --------------------------------------------------------------------------
49
+ // Resolve tools: built-ins (string) + custom ({ type: "custom", modulePath })
50
+ // --------------------------------------------------------------------------
51
+ const enabledTools = [];
52
+ const toolDefs = this.definition.tools ?? [];
53
+ const builtInNames = [];
54
+ const customToolPaths = [];
55
+ for (const t of toolDefs) {
56
+ if (typeof t === "string") {
57
+ builtInNames.push(t);
30
58
  }
31
- return tool;
32
- });
59
+ else if (t &&
60
+ typeof t === "object" &&
61
+ "type" in t &&
62
+ t.type === "custom" &&
63
+ "modulePath" in t &&
64
+ typeof t.modulePath === "string") {
65
+ customToolPaths.push(t.modulePath);
66
+ }
67
+ }
68
+ // Built-in tools from registry
69
+ for (const name of builtInNames) {
70
+ const entry = TOOL_REGISTRY[name];
71
+ if (!entry) {
72
+ throw new Error(`Unknown built-in tool "${name}"`);
73
+ }
74
+ enabledTools.push(typeof entry === "function" ? entry() : entry);
75
+ }
76
+ // Custom tools loaded from modulePaths
77
+ if (customToolPaths.length > 0) {
78
+ const customTools = await loadCustomTools(customToolPaths);
79
+ enabledTools.push(...customTools);
80
+ }
81
+ // MCP tools
33
82
  if ((this.definition.mcps?.length ?? 0) > 0) {
34
83
  enabledTools.push(...(await makeMcpToolsClient(this.definition.mcps).getTools()));
35
84
  }
@@ -1,49 +1,33 @@
1
1
  import { z } from "zod";
2
- export declare const todoItemSchema: z.ZodObject<
3
- {
4
- content: z.ZodString;
5
- status: z.ZodEnum<{
6
- pending: "pending";
7
- in_progress: "in_progress";
8
- completed: "completed";
9
- }>;
10
- activeForm: z.ZodString;
11
- },
12
- z.core.$strip
13
- >;
14
- export declare const todoWrite: import("langchain").DynamicStructuredTool<
15
- z.ZodObject<
16
- {
17
- todos: z.ZodArray<
18
- z.ZodObject<
19
- {
20
- content: z.ZodString;
21
- status: z.ZodEnum<{
22
- pending: "pending";
23
- in_progress: "in_progress";
24
- completed: "completed";
25
- }>;
26
- activeForm: z.ZodString;
27
- },
28
- z.core.$strip
29
- >
30
- >;
31
- },
32
- z.core.$strip
33
- >,
34
- {
35
- todos: {
36
- content: string;
37
- status: "pending" | "in_progress" | "completed";
38
- activeForm: string;
39
- }[];
40
- },
41
- {
42
- todos: {
43
- content: string;
44
- status: "pending" | "in_progress" | "completed";
45
- activeForm: string;
46
- }[];
47
- },
48
- string
49
- >;
2
+ export declare const todoItemSchema: z.ZodObject<{
3
+ content: z.ZodString;
4
+ status: z.ZodEnum<{
5
+ pending: "pending";
6
+ in_progress: "in_progress";
7
+ completed: "completed";
8
+ }>;
9
+ activeForm: z.ZodString;
10
+ }, z.core.$strip>;
11
+ export declare const todoWrite: import("langchain").DynamicStructuredTool<z.ZodObject<{
12
+ todos: z.ZodArray<z.ZodObject<{
13
+ content: z.ZodString;
14
+ status: z.ZodEnum<{
15
+ pending: "pending";
16
+ in_progress: "in_progress";
17
+ completed: "completed";
18
+ }>;
19
+ activeForm: z.ZodString;
20
+ }, z.core.$strip>>;
21
+ }, z.core.$strip>, {
22
+ todos: {
23
+ content: string;
24
+ status: "pending" | "in_progress" | "completed";
25
+ activeForm: string;
26
+ }[];
27
+ }, {
28
+ todos: {
29
+ content: string;
30
+ status: "pending" | "in_progress" | "completed";
31
+ activeForm: string;
32
+ }[];
33
+ }, string>;
@@ -1,18 +1,16 @@
1
1
  import { tool } from "langchain";
2
2
  import { z } from "zod";
3
3
  export const todoItemSchema = z.object({
4
- content: z.string().min(1),
5
- status: z.enum(["pending", "in_progress", "completed"]),
6
- activeForm: z.string().min(1),
4
+ content: z.string().min(1),
5
+ status: z.enum(["pending", "in_progress", "completed"]),
6
+ activeForm: z.string().min(1),
7
7
  });
8
- export const todoWrite = tool(
9
- ({ todos }) => {
10
- // Simple implementation that confirms the todos were written
11
- return `Successfully updated todo list with ${todos.length} items`;
12
- },
13
- {
14
- name: "todo_write",
15
- description: `Use this tool to create and manage a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
8
+ export const todoWrite = tool(({ todos }) => {
9
+ // Simple implementation that confirms the todos were written
10
+ return `Successfully updated todo list with ${todos.length} items`;
11
+ }, {
12
+ name: "todo_write",
13
+ description: `Use this tool to create and manage a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
16
14
  It also helps the user understand the progress of the task and overall progress of their requests.
17
15
 
18
16
  ## When to Use This Tool
@@ -73,8 +71,7 @@ NOTE that you should not use this tool if there is only one trivial task to do.
73
71
  - activeForm: "Fixing authentication bug"
74
72
 
75
73
  When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully.`,
76
- schema: z.object({
77
- todos: z.array(todoItemSchema),
78
- }),
79
- },
80
- );
74
+ schema: z.object({
75
+ todos: z.array(todoItemSchema),
76
+ }),
77
+ });
@@ -1,4 +1,4 @@
1
1
  import { ExaSearchResults } from "@langchain/exa";
2
2
  export declare function makeWebSearchTool(): ExaSearchResults<{
3
- text: true;
3
+ text: true;
4
4
  }>;
@@ -1,26 +1,23 @@
1
1
  import { ExaSearchResults } from "@langchain/exa";
2
2
  import Exa from "exa-js";
3
-
4
3
  let _webSearchInstance = null;
5
4
  export function makeWebSearchTool() {
6
- if (_webSearchInstance) {
7
- return _webSearchInstance;
8
- }
9
- const apiKey = process.env.EXA_API_KEY;
10
- if (!apiKey) {
11
- throw new Error(
12
- "EXA_API_KEY environment variable is required to use the web_search tool. " +
13
- "Please set it to your Exa API key from https://exa.ai",
14
- );
15
- }
16
- const client = new Exa(apiKey);
17
- _webSearchInstance = new ExaSearchResults({
18
- client,
19
- searchArgs: {
20
- numResults: 5,
21
- type: "auto",
22
- text: true,
23
- },
24
- });
25
- return _webSearchInstance;
5
+ if (_webSearchInstance) {
6
+ return _webSearchInstance;
7
+ }
8
+ const apiKey = process.env.EXA_API_KEY;
9
+ if (!apiKey) {
10
+ throw new Error("EXA_API_KEY environment variable is required to use the web_search tool. " +
11
+ "Please set it to your Exa API key from https://exa.ai");
12
+ }
13
+ const client = new Exa(apiKey);
14
+ _webSearchInstance = new ExaSearchResults({
15
+ client,
16
+ searchArgs: {
17
+ numResults: 5,
18
+ type: "auto",
19
+ text: true,
20
+ },
21
+ });
22
+ return _webSearchInstance;
26
23
  }
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ export type CustomToolModule = {
3
+ default: (input: unknown) => unknown | Promise<unknown>;
4
+ schema: z.ZodTypeAny | ((z: typeof import("zod").z) => z.ZodTypeAny);
5
+ name: string;
6
+ description: string;
7
+ };
8
+ export type ResolvedCustomTool = {
9
+ fn: (input: unknown) => unknown | Promise<unknown>;
10
+ schema: z.ZodTypeAny;
11
+ name: string;
12
+ description: string;
13
+ };
14
+ export declare function loadCustomToolModule(modulePath: string): Promise<ResolvedCustomTool>;
@@ -0,0 +1,42 @@
1
+ import { pathToFileURL } from "node:url";
2
+ import { z } from "zod";
3
+ // Cache for resolved custom tools (keyed by modulePath)
4
+ const customToolCache = new Map();
5
+ export async function loadCustomToolModule(modulePath) {
6
+ // Check cache first
7
+ const cached = customToolCache.get(modulePath);
8
+ if (cached != null) {
9
+ return cached;
10
+ }
11
+ const moduleUrl = pathToFileURL(modulePath).href;
12
+ let mod;
13
+ try {
14
+ mod = (await import(moduleUrl));
15
+ }
16
+ catch (error) {
17
+ throw new Error(`Failed to import custom tool from ${modulePath}: ${error instanceof Error ? error.message : String(error)}`);
18
+ }
19
+ if (typeof mod.default !== "function") {
20
+ throw new Error(`Custom tool module "${modulePath}" must export a default function.`);
21
+ }
22
+ if (!mod.schema) {
23
+ throw new Error(`Custom tool module "${modulePath}" must export a "schema" (zod schema).`);
24
+ }
25
+ if (!mod.name) {
26
+ throw new Error(`Custom tool module "${modulePath}" must export a "name" string.`);
27
+ }
28
+ if (!mod.description) {
29
+ throw new Error(`Custom tool module "${modulePath}" must export a "description" string.`);
30
+ }
31
+ // Resolve schema: if it's a function, call it with z injected
32
+ const resolvedSchema = typeof mod.schema === "function" ? mod.schema(z) : mod.schema;
33
+ const resolved = {
34
+ fn: mod.default,
35
+ schema: resolvedSchema,
36
+ name: mod.name,
37
+ description: mod.description,
38
+ };
39
+ // Cache the resolved tool
40
+ customToolCache.set(modulePath, resolved);
41
+ return resolved;
42
+ }
@@ -1,9 +1,10 @@
1
1
  import { z } from "zod";
2
- export declare const zToolType: z.ZodUnion<
3
- readonly [
4
- z.ZodLiteral<"todo_write">,
5
- z.ZodLiteral<"get_weather">,
6
- z.ZodLiteral<"web_search">,
7
- ]
8
- >;
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">]>;
4
+ /** Tool type - can be a built-in tool string or custom tool object. */
5
+ export declare const zToolType: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">]>, z.ZodObject<{
6
+ type: z.ZodLiteral<"custom">;
7
+ modulePath: z.ZodString;
8
+ }, z.core.$strip>]>;
9
9
  export type ToolType = z.infer<typeof zToolType>;
10
+ export type BuiltInToolType = z.infer<typeof zBuiltInToolType>;
@@ -1,6 +1,14 @@
1
1
  import { z } from "zod";
2
- export const zToolType = z.union([
3
- z.literal("todo_write"),
4
- z.literal("get_weather"),
5
- z.literal("web_search"),
2
+ /** Built-in tool types. */
3
+ export const zBuiltInToolType = z.union([
4
+ z.literal("todo_write"),
5
+ z.literal("get_weather"),
6
+ z.literal("web_search"),
6
7
  ]);
8
+ /** Custom tool schema. */
9
+ const zCustomTool = z.object({
10
+ type: z.literal("custom"),
11
+ modulePath: z.string(),
12
+ });
13
+ /** Tool type - can be a built-in tool string or custom tool object. */
14
+ export const zToolType = z.union([zBuiltInToolType, zCustomTool]);
@@ -1,4 +1,4 @@
1
1
  /**
2
- * Copy necessary files from @town/agent to the agent directory
2
+ * Copy necessary files from @townco/agent to the agent directory
3
3
  */
4
4
  export declare function bundleAgentDependencies(agentPath: string): Promise<void>;
@@ -10,7 +10,7 @@ function getAgentPackageDir() {
10
10
  return join(dirname(currentFile), "..");
11
11
  }
12
12
  /**
13
- * Copy necessary files from @town/agent to the agent directory
13
+ * Copy necessary files from @townco/agent to the agent directory
14
14
  */
15
15
  export async function bundleAgentDependencies(agentPath) {
16
16
  const sourceDir = getAgentPackageDir();
@@ -83,7 +83,7 @@ export default defineConfig({
83
83
  plugins: [react()],
84
84
  resolve: {
85
85
  alias: {
86
- "@town/ui": resolve(__dirname, "./ui/src"),
86
+ "@townco/ui": resolve(__dirname, "./ui/src"),
87
87
  // Exclude Node.js-only modules from browser bundle
88
88
  "node:child_process": resolve(__dirname, "./polyfills/child_process.js"),
89
89
  "node:stream": resolve(__dirname, "./polyfills/stream.js"),
@@ -1,18 +1,16 @@
1
1
  import type { AgentDefinition } from "../definition";
2
2
  export interface ScaffoldOptions {
3
- name: string;
4
- definition: AgentDefinition;
5
- overwrite?: boolean;
6
- includeGui?: boolean;
3
+ name: string;
4
+ definition: AgentDefinition;
5
+ overwrite?: boolean;
6
+ includeGui?: boolean;
7
7
  }
8
8
  export interface ScaffoldResult {
9
- success: boolean;
10
- path: string;
11
- error?: string;
9
+ success: boolean;
10
+ path: string;
11
+ error?: string;
12
12
  }
13
13
  /**
14
14
  * Scaffold a new agent package
15
15
  */
16
- export declare function scaffoldAgent(
17
- options: ScaffoldOptions,
18
- ): Promise<ScaffoldResult>;
16
+ export declare function scaffoldAgent(options: ScaffoldOptions): Promise<ScaffoldResult>;