@townco/agent 0.1.23 → 0.1.24
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/http.js +2 -2
- package/dist/runner/langchain/index.js +25 -23
- package/dist/scaffold/claude-scaffold.d.ts +8 -0
- package/dist/scaffold/claude-scaffold.js +58 -0
- package/dist/scaffold/index.d.ts +1 -0
- package/dist/scaffold/index.js +1 -0
- package/dist/scaffold/project-scaffold.js +3 -0
- package/dist/scaffold/templates/dot-claude/CLAUDE-append.md +71 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
package/dist/acp-server/http.js
CHANGED
|
@@ -384,8 +384,8 @@ export function makeHttpTransport(agent) {
|
|
|
384
384
|
const writer = inbound.writable.getWriter();
|
|
385
385
|
await writer.write(encoder.encode(`${JSON.stringify(body)}\n`));
|
|
386
386
|
writer.releaseLock();
|
|
387
|
-
// Wait for response with
|
|
388
|
-
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Request timeout")),
|
|
387
|
+
// Wait for response with ten minute timeout
|
|
388
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Request timeout")), 10 * 60 * 1000));
|
|
389
389
|
try {
|
|
390
390
|
const response = await Promise.race([responsePromise, timeoutPromise]);
|
|
391
391
|
logger.debug("POST /rpc - Response received", { id });
|
|
@@ -157,30 +157,32 @@ export class LangchainAgent {
|
|
|
157
157
|
if (toolCall.id == null) {
|
|
158
158
|
throw new Error(`Tool call is missing id: ${JSON.stringify(toolCall)}`);
|
|
159
159
|
}
|
|
160
|
+
// TODO: re-add this suppression of the todo_write tool call when we
|
|
161
|
+
// are rendering the agent-plan update in the UIs
|
|
160
162
|
// If this is a todo_write tool call, yield an agent-plan update
|
|
161
|
-
if (toolCall.name === "todo_write" && toolCall.args?.todos) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
163
|
+
//if (toolCall.name === "todo_write" && toolCall.args?.todos) {
|
|
164
|
+
// const entries = toolCall.args.todos.flatMap((todo: unknown) => {
|
|
165
|
+
// const validation = todoItemSchema.safeParse(todo);
|
|
166
|
+
// if (!validation.success) {
|
|
167
|
+
// // Invalid todo - filter it out
|
|
168
|
+
// return [];
|
|
169
|
+
// }
|
|
170
|
+
// return [
|
|
171
|
+
// {
|
|
172
|
+
// content: validation.data.content,
|
|
173
|
+
// status: validation.data.status,
|
|
174
|
+
// priority: "medium" as const,
|
|
175
|
+
// },
|
|
176
|
+
// ];
|
|
177
|
+
// });
|
|
178
|
+
// yield {
|
|
179
|
+
// sessionUpdate: "plan",
|
|
180
|
+
// entries: entries,
|
|
181
|
+
// };
|
|
182
|
+
// // Track this tool call ID to suppress tool_call notifications
|
|
183
|
+
// todoWriteToolCallIds.add(toolCall.id);
|
|
184
|
+
// continue;
|
|
185
|
+
//}
|
|
184
186
|
yield {
|
|
185
187
|
sessionUpdate: "tool_call",
|
|
186
188
|
toolCallId: toolCall.id,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize Claude Code workspace integration by creating AGENTS.town.md
|
|
3
|
+
* and adding references to it in CLAUDE.md and AGENTS.md.
|
|
4
|
+
*
|
|
5
|
+
* @param targetPath - The root directory of the Town project
|
|
6
|
+
* @throws Error if target path doesn't exist
|
|
7
|
+
*/
|
|
8
|
+
export declare function initForClaudeCode(targetPath: string): Promise<void>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { appendFile, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
/**
|
|
8
|
+
* Initialize Claude Code workspace integration by creating AGENTS.town.md
|
|
9
|
+
* and adding references to it in CLAUDE.md and AGENTS.md.
|
|
10
|
+
*
|
|
11
|
+
* @param targetPath - The root directory of the Town project
|
|
12
|
+
* @throws Error if target path doesn't exist
|
|
13
|
+
*/
|
|
14
|
+
export async function initForClaudeCode(targetPath) {
|
|
15
|
+
// Verify target path exists
|
|
16
|
+
if (!existsSync(targetPath)) {
|
|
17
|
+
throw new Error(`Target path does not exist: ${targetPath}`);
|
|
18
|
+
}
|
|
19
|
+
// Source template directory (dot-claude in the scaffold package)
|
|
20
|
+
const templateDir = join(__dirname, "templates", "dot-claude");
|
|
21
|
+
const appendTemplatePath = join(templateDir, "CLAUDE-append.md");
|
|
22
|
+
if (!existsSync(appendTemplatePath)) {
|
|
23
|
+
throw new Error(`Claude template not found: ${appendTemplatePath}. This is a bug in the Town CLI.`);
|
|
24
|
+
}
|
|
25
|
+
// Handle AGENTS.town.md and references
|
|
26
|
+
const claudeMdPath = join(targetPath, "CLAUDE.md");
|
|
27
|
+
const agentsMdPath = join(targetPath, "AGENTS.md");
|
|
28
|
+
const agentsTownMdPath = join(targetPath, "AGENTS.town.md");
|
|
29
|
+
const townContent = await readFile(appendTemplatePath, "utf-8");
|
|
30
|
+
// Create AGENTS.town.md with the Town-specific content
|
|
31
|
+
await writeFile(agentsTownMdPath, townContent, "utf-8");
|
|
32
|
+
console.log("✓ Created AGENTS.town.md");
|
|
33
|
+
// Add reference to AGENTS.town.md in both CLAUDE.md and AGENTS.md
|
|
34
|
+
const referenceText = "\n\nThis repo uses the Town agent SDK -- please reference @AGENTS.town.md for more information\n";
|
|
35
|
+
// Handle CLAUDE.md
|
|
36
|
+
if (existsSync(claudeMdPath)) {
|
|
37
|
+
// CLAUDE.md exists, append reference
|
|
38
|
+
await appendFile(claudeMdPath, referenceText);
|
|
39
|
+
console.log("✓ Added Town SDK reference to CLAUDE.md");
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// CLAUDE.md doesn't exist, create it with just the reference
|
|
43
|
+
await writeFile(claudeMdPath, `${referenceText.trim()}\n`, "utf-8");
|
|
44
|
+
console.log("✓ Created CLAUDE.md with Town SDK reference");
|
|
45
|
+
}
|
|
46
|
+
// Handle AGENTS.md
|
|
47
|
+
if (existsSync(agentsMdPath)) {
|
|
48
|
+
// AGENTS.md exists, append reference
|
|
49
|
+
await appendFile(agentsMdPath, referenceText);
|
|
50
|
+
console.log("✓ Added Town SDK reference to AGENTS.md");
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// AGENTS.md doesn't exist, create it with just the reference
|
|
54
|
+
await writeFile(agentsMdPath, `${referenceText.trim()}\n`, "utf-8");
|
|
55
|
+
console.log("✓ Created AGENTS.md with Town SDK reference");
|
|
56
|
+
}
|
|
57
|
+
console.log("✓ Claude Code workspace initialized");
|
|
58
|
+
}
|
package/dist/scaffold/index.d.ts
CHANGED
|
@@ -16,4 +16,5 @@ export interface ScaffoldResult {
|
|
|
16
16
|
* Scaffold a new agent package
|
|
17
17
|
*/
|
|
18
18
|
export declare function scaffoldAgent(options: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
19
|
+
export { initForClaudeCode } from "./claude-scaffold";
|
|
19
20
|
export { scaffoldProject } from "./project-scaffold";
|
package/dist/scaffold/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { initForClaudeCode } from "./claude-scaffold.js";
|
|
4
5
|
/**
|
|
5
6
|
* Generate project package.json
|
|
6
7
|
*/
|
|
@@ -299,6 +300,8 @@ export async function scaffoldProject(options) {
|
|
|
299
300
|
}
|
|
300
301
|
// Run bun install
|
|
301
302
|
await runBunInstall(projectPath);
|
|
303
|
+
// Initialize Claude Code workspace integration
|
|
304
|
+
await initForClaudeCode(projectPath);
|
|
302
305
|
return {
|
|
303
306
|
success: true,
|
|
304
307
|
path: projectPath,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Town Agent SDK
|
|
2
|
+
This project has code for agents developed using the Town agent SDK. The
|
|
3
|
+
structure of this repository is:
|
|
4
|
+
- `agents/`: code for main agent loop and configuration
|
|
5
|
+
- `tools/`: code for function-based tools that are shared by agents
|
|
6
|
+
|
|
7
|
+
## Writing custom tools
|
|
8
|
+
You may add one of the built-in tools to the agent SDK (e.g. `web_search`), or
|
|
9
|
+
you may implement your own. To do this:
|
|
10
|
+
1. add a file `tools/<tool-name>.ts` with the following structure:
|
|
11
|
+
```
|
|
12
|
+
import * as z from 'zod';
|
|
13
|
+
|
|
14
|
+
export const schema = z.object({
|
|
15
|
+
a: z.number().describe("First number"),
|
|
16
|
+
b: z.number().describe("Second number"),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const name = "add_two_numbers";
|
|
20
|
+
export const description = "Add two numbers together and return the result";
|
|
21
|
+
|
|
22
|
+
export default function addTwoNumbers(input: unknown) {
|
|
23
|
+
const { a, b } = input as { a: number; b: number };
|
|
24
|
+
return { result: a + b };
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
where
|
|
28
|
+
- `schema` is a zod schema for the tool input
|
|
29
|
+
- `name` is the name of the tool (given to the LLM)
|
|
30
|
+
- `description` describes the tool (given to the LLM)
|
|
31
|
+
- default-exported function containing the body of the function
|
|
32
|
+
|
|
33
|
+
2. Modify the agent code to import the `createTool` utility and the new tool
|
|
34
|
+
```
|
|
35
|
+
import { createTool } from '@townco/agent/utils';
|
|
36
|
+
import * as newCoolTool from '../../tools/new-cool-tool';
|
|
37
|
+
```
|
|
38
|
+
and modify the agent's tools list
|
|
39
|
+
```
|
|
40
|
+
const agent: AgentDefinition = {
|
|
41
|
+
model: "claude-sonnet-4-5-20250929",
|
|
42
|
+
systemPrompt: "You are a helpful assistant.\n",
|
|
43
|
+
tools: ["todo_write", "web_search", createTool(newCoolTool)],
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Configuring MCPs
|
|
48
|
+
You may configure stdio and streamable HTTP-based MCPs. Here is an example:
|
|
49
|
+
```
|
|
50
|
+
const agent: AgentDefinition = {
|
|
51
|
+
model: "claude-sonnet-4-5-20250929",
|
|
52
|
+
systemPrompt: "You are a helpful assistant.\n",
|
|
53
|
+
tools: ["todo_write", "web_search"],
|
|
54
|
+
mcps: [
|
|
55
|
+
{
|
|
56
|
+
transport: "http",
|
|
57
|
+
name: "foobar",
|
|
58
|
+
url: "http://town.com:8080/mcp",
|
|
59
|
+
headers: {
|
|
60
|
+
'Authorization': `Bearer ${process.env.MCP_API_KEY}`
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
transport: 'stdio',
|
|
65
|
+
name: 'local-mcp',
|
|
66
|
+
command: 'node',
|
|
67
|
+
args: ['path/to/local/mcp.js'],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
```
|