@townco/agent 0.1.50 → 0.1.52

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 (60) hide show
  1. package/dist/acp-server/adapter.d.ts +10 -0
  2. package/dist/acp-server/adapter.js +287 -80
  3. package/dist/acp-server/cli.d.ts +1 -3
  4. package/dist/acp-server/http.js +8 -1
  5. package/dist/acp-server/index.js +5 -0
  6. package/dist/acp-server/session-storage.d.ts +17 -3
  7. package/dist/acp-server/session-storage.js +9 -0
  8. package/dist/bin.js +0 -0
  9. package/dist/check-jaeger.d.ts +5 -0
  10. package/dist/check-jaeger.js +82 -0
  11. package/dist/definition/index.d.ts +16 -4
  12. package/dist/definition/index.js +17 -4
  13. package/dist/index.js +1 -1
  14. package/dist/run-subagents.d.ts +9 -0
  15. package/dist/run-subagents.js +110 -0
  16. package/dist/runner/agent-runner.d.ts +10 -2
  17. package/dist/runner/agent-runner.js +4 -0
  18. package/dist/runner/hooks/executor.d.ts +17 -0
  19. package/dist/runner/hooks/executor.js +66 -0
  20. package/dist/runner/hooks/predefined/compaction-tool.js +9 -1
  21. package/dist/runner/hooks/predefined/tool-response-compactor.d.ts +6 -0
  22. package/dist/runner/hooks/predefined/tool-response-compactor.js +461 -0
  23. package/dist/runner/hooks/registry.js +2 -0
  24. package/dist/runner/hooks/types.d.ts +39 -3
  25. package/dist/runner/hooks/types.js +9 -4
  26. package/dist/runner/index.d.ts +1 -3
  27. package/dist/runner/langchain/custom-stream-types.d.ts +36 -0
  28. package/dist/runner/langchain/custom-stream-types.js +23 -0
  29. package/dist/runner/langchain/index.js +102 -76
  30. package/dist/runner/langchain/otel-callbacks.js +67 -1
  31. package/dist/runner/langchain/tools/bash.d.ts +14 -0
  32. package/dist/runner/langchain/tools/bash.js +135 -0
  33. package/dist/scaffold/link-local.d.ts +1 -0
  34. package/dist/scaffold/link-local.js +54 -0
  35. package/dist/scaffold/project-scaffold.js +1 -0
  36. package/dist/telemetry/setup.d.ts +3 -1
  37. package/dist/telemetry/setup.js +33 -3
  38. package/dist/templates/index.d.ts +7 -0
  39. package/dist/test-telemetry.d.ts +5 -0
  40. package/dist/test-telemetry.js +88 -0
  41. package/dist/tsconfig.tsbuildinfo +1 -1
  42. package/dist/utils/context-size-calculator.d.ts +29 -0
  43. package/dist/utils/context-size-calculator.js +78 -0
  44. package/dist/utils/index.d.ts +2 -0
  45. package/dist/utils/index.js +2 -0
  46. package/dist/utils/token-counter.d.ts +19 -0
  47. package/dist/utils/token-counter.js +44 -0
  48. package/index.ts +1 -1
  49. package/package.json +7 -6
  50. package/templates/index.ts +18 -6
  51. package/dist/definition/mcp.d.ts +0 -0
  52. package/dist/definition/mcp.js +0 -0
  53. package/dist/definition/tools/todo.d.ts +0 -49
  54. package/dist/definition/tools/todo.js +0 -80
  55. package/dist/definition/tools/web_search.d.ts +0 -4
  56. package/dist/definition/tools/web_search.js +0 -26
  57. package/dist/dev-agent/index.d.ts +0 -2
  58. package/dist/dev-agent/index.js +0 -18
  59. package/dist/example.d.ts +0 -2
  60. package/dist/example.js +0 -19
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Quick script to verify Jaeger connectivity
3
+ * Run with: bun check-jaeger.ts
4
+ */
5
+ export {}; // Make this a module for top-level await
6
+ console.log("šŸ” Checking Jaeger connectivity...\n");
7
+ // Check if Jaeger collector is accepting OTLP on gRPC port
8
+ const checkPort = async (port, protocol) => {
9
+ try {
10
+ const net = await import("node:net");
11
+ const socket = new net.Socket();
12
+ return new Promise((resolve) => {
13
+ socket.setTimeout(2000);
14
+ socket.on("connect", () => {
15
+ console.log(`āœ… ${protocol} port ${port} is accepting connections`);
16
+ socket.destroy();
17
+ resolve(true);
18
+ });
19
+ socket.on("timeout", () => {
20
+ console.log(`āŒ ${protocol} port ${port} timed out`);
21
+ socket.destroy();
22
+ resolve(false);
23
+ });
24
+ socket.on("error", (error) => {
25
+ console.log(`āŒ ${protocol} port ${port} not reachable: ${error.message}`);
26
+ resolve(false);
27
+ });
28
+ socket.connect(port, "localhost");
29
+ });
30
+ }
31
+ catch (error) {
32
+ console.log(`āŒ Error checking ${protocol} port ${port}:`, error);
33
+ return false;
34
+ }
35
+ };
36
+ // Check Jaeger ports
37
+ console.log("Checking Jaeger ports:");
38
+ console.log("─────────────────────────────");
39
+ await checkPort(4317, "OTLP gRPC"); // Collector OTLP gRPC
40
+ await checkPort(4318, "OTLP HTTP"); // Collector OTLP HTTP
41
+ await checkPort(16686, "UI "); // Query UI
42
+ console.log("\nšŸ” Checking Docker container...\n");
43
+ // Check if Jaeger container is running
44
+ try {
45
+ const { execSync } = await import("node:child_process");
46
+ const output = execSync("docker ps --filter name=jaeger --format '{{.Status}}'", {
47
+ encoding: "utf-8",
48
+ }).trim();
49
+ if (output) {
50
+ console.log(`āœ… Jaeger container is running: ${output}`);
51
+ // Check container logs for OTLP
52
+ try {
53
+ const logs = execSync("docker logs jaeger 2>&1 | tail -20", {
54
+ encoding: "utf-8",
55
+ });
56
+ console.log("\nšŸ“‹ Recent container logs:");
57
+ console.log("─────────────────────────────");
58
+ console.log(logs);
59
+ }
60
+ catch (e) {
61
+ console.log("āš ļø Could not fetch container logs");
62
+ }
63
+ }
64
+ else {
65
+ console.log("āŒ No Jaeger container running with name 'jaeger'");
66
+ console.log("\nšŸ’” Start Jaeger with:");
67
+ console.log("docker run -d --name jaeger \\");
68
+ console.log(" -e COLLECTOR_OTLP_ENABLED=true \\");
69
+ console.log(" -p 16686:16686 \\");
70
+ console.log(" -p 4317:4317 \\");
71
+ console.log(" -p 4318:4318 \\");
72
+ console.log(" jaegertracing/all-in-one:latest");
73
+ }
74
+ }
75
+ catch (error) {
76
+ console.log("āš ļø Could not check Docker container:", error);
77
+ }
78
+ console.log("\nšŸŽÆ Next steps:");
79
+ console.log("─────────────────────────────");
80
+ console.log("1. Make sure ports above show āœ…");
81
+ console.log("2. Run the test: ENABLE_TELEMETRY=true bun test-telemetry.ts");
82
+ console.log("3. Check UI: http://localhost:16686");
@@ -16,14 +16,22 @@ export declare const McpConfigSchema: z.ZodUnion<readonly [z.ZodObject<{
16
16
  export declare const HookConfigSchema: z.ZodObject<{
17
17
  type: z.ZodEnum<{
18
18
  context_size: "context_size";
19
+ tool_response: "tool_response";
19
20
  }>;
20
- setting: z.ZodOptional<z.ZodObject<{
21
+ setting: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
21
22
  threshold: z.ZodNumber;
22
- }, z.core.$strip>>;
23
+ }, z.core.$strip>, z.ZodObject<{
24
+ maxContextThreshold: z.ZodOptional<z.ZodNumber>;
25
+ responseTruncationThreshold: z.ZodOptional<z.ZodNumber>;
26
+ }, z.core.$strip>]>>;
23
27
  callback: z.ZodString;
24
28
  }, z.core.$strip>;
25
29
  /** Agent definition schema. */
26
30
  export declare const AgentDefinitionSchema: z.ZodObject<{
31
+ displayName: z.ZodOptional<z.ZodString>;
32
+ version: z.ZodOptional<z.ZodString>;
33
+ description: z.ZodOptional<z.ZodString>;
34
+ suggestedPrompts: z.ZodOptional<z.ZodArray<z.ZodString>>;
27
35
  systemPrompt: z.ZodNullable<z.ZodString>;
28
36
  model: z.ZodString;
29
37
  tools: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
@@ -56,10 +64,14 @@ export declare const AgentDefinitionSchema: z.ZodObject<{
56
64
  hooks: z.ZodOptional<z.ZodArray<z.ZodObject<{
57
65
  type: z.ZodEnum<{
58
66
  context_size: "context_size";
67
+ tool_response: "tool_response";
59
68
  }>;
60
- setting: z.ZodOptional<z.ZodObject<{
69
+ setting: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
61
70
  threshold: z.ZodNumber;
62
- }, z.core.$strip>>;
71
+ }, z.core.$strip>, z.ZodObject<{
72
+ maxContextThreshold: z.ZodOptional<z.ZodNumber>;
73
+ responseTruncationThreshold: z.ZodOptional<z.ZodNumber>;
74
+ }, z.core.$strip>]>>;
63
75
  callback: z.ZodString;
64
76
  }, z.core.$strip>>>;
65
77
  }, z.core.$strip>;
@@ -54,16 +54,29 @@ const ToolSchema = z.union([
54
54
  ]);
55
55
  /** Hook configuration schema. */
56
56
  export const HookConfigSchema = z.object({
57
- type: z.enum(["context_size"]),
57
+ type: z.enum(["context_size", "tool_response"]),
58
58
  setting: z
59
- .object({
60
- threshold: z.number().min(0).max(100),
61
- })
59
+ .union([
60
+ // For context_size hooks
61
+ z.object({
62
+ threshold: z.number().min(0).max(100),
63
+ }),
64
+ // For tool_response hooks
65
+ z.object({
66
+ maxContextThreshold: z.number().min(0).max(100).optional(),
67
+ responseTruncationThreshold: z.number().min(0).max(100).optional(),
68
+ }),
69
+ ])
62
70
  .optional(),
63
71
  callback: z.string(),
64
72
  });
65
73
  /** Agent definition schema. */
66
74
  export const AgentDefinitionSchema = z.object({
75
+ /** Human-readable display name for the agent (shown in UI). */
76
+ displayName: z.string().optional(),
77
+ version: z.string().optional(),
78
+ description: z.string().optional(),
79
+ suggestedPrompts: z.array(z.string()).optional(),
67
80
  systemPrompt: z.string().nullable(),
68
81
  model: z.string(),
69
82
  tools: z.array(ToolSchema).optional(),
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ const exampleAgent = {
36
36
  {
37
37
  type: "context_size",
38
38
  setting: {
39
- threshold: 95,
39
+ threshold: 80,
40
40
  },
41
41
  callback: "compaction_tool",
42
42
  },
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Helper script to test makeSubagentsTool
4
+ *
5
+ * Usage:
6
+ * bun packages/agent/run-subagents.ts
7
+ * bun packages/agent/run-subagents.ts "your custom query here"
8
+ */
9
+ export {};
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Helper script to test makeSubagentsTool
4
+ *
5
+ * Usage:
6
+ * bun packages/agent/run-subagents.ts
7
+ * bun packages/agent/run-subagents.ts "your custom query here"
8
+ */
9
+ import { makeSubagentsTool } from "./utils";
10
+ import { makeRunnerFromDefinition } from "./runner";
11
+ // Default query if none provided
12
+ const defaultQuery = "Can you search for news about artificial intelligence?";
13
+ const userQuery = process.argv[2] || defaultQuery;
14
+ console.log("🧪 Testing makeSubagentsTool\n");
15
+ console.log("Query:", userQuery);
16
+ console.log("─".repeat(60));
17
+ // Create a simple subagent definition
18
+ const simpleSubagent = {
19
+ model: "claude-sonnet-4-5-20250929",
20
+ systemPrompt: "You are a helpful research assistant. Provide concise, informative responses.",
21
+ tools: [
22
+ "web_search",
23
+ "todo_write",
24
+ ],
25
+ mcps: [],
26
+ };
27
+ // Create the coordinator agent with subagent tool
28
+ const coordinatorAgent = {
29
+ model: "claude-sonnet-4-5-20250929",
30
+ systemPrompt: `You are a coordinator agent that delegates tasks to specialized subagents.
31
+ When you receive a query, evaluate if it needs to be delegated to a subagent.
32
+ If so, use the Task tool with the appropriate subagent.`,
33
+ tools: [
34
+ makeSubagentsTool([
35
+ {
36
+ agentName: "researcher",
37
+ description: "Use this agent to research topics, search the web, and gather information",
38
+ path: import.meta.filename, // Points to itself as a simple test
39
+ },
40
+ {
41
+ agentName: "simple",
42
+ description: "Use this for general tasks that don't require specialized tools",
43
+ path: import.meta.filename,
44
+ },
45
+ ]),
46
+ ],
47
+ mcps: [],
48
+ };
49
+ // Run the test
50
+ async function runTest() {
51
+ console.log("\nšŸ“ Creating runner...");
52
+ const runner = makeRunnerFromDefinition(coordinatorAgent);
53
+ console.log("šŸš€ Running query...\n");
54
+ try {
55
+ const stream = runner.invoke({
56
+ prompt: [
57
+ {
58
+ type: "text",
59
+ text: userQuery,
60
+ },
61
+ ],
62
+ sessionId: "test-session",
63
+ messageId: "msg-1",
64
+ });
65
+ console.log("šŸ“Š Stream output:");
66
+ console.log("─".repeat(60));
67
+ let messageCount = 0;
68
+ let toolCallCount = 0;
69
+ let lastTextContent = "";
70
+ for await (const chunk of stream) {
71
+ messageCount++;
72
+ // Pretty print different types of chunks
73
+ if (chunk.type === "agent_message_chunk") {
74
+ const content = chunk.content;
75
+ if (content.type === "text" && content.text) {
76
+ process.stdout.write(content.text);
77
+ lastTextContent += content.text;
78
+ }
79
+ else if (content.type === "tool_use") {
80
+ toolCallCount++;
81
+ console.log(`\n\nšŸ”§ Tool Call #${toolCallCount}: ${content.name}`);
82
+ console.log(" Input:", JSON.stringify(content.input, null, 2).split('\n').join('\n '));
83
+ }
84
+ }
85
+ else if (chunk.type === "tool_result") {
86
+ console.log("\n\nāœ… Tool Result:");
87
+ console.log(" ", JSON.stringify(chunk, null, 2).split('\n').join('\n '));
88
+ }
89
+ else {
90
+ // Print other chunk types for debugging
91
+ console.log("\n\nšŸ“¦ Chunk:", JSON.stringify(chunk, null, 2));
92
+ }
93
+ }
94
+ console.log("\n\n" + "─".repeat(60));
95
+ console.log("✨ Test completed!");
96
+ console.log(` Total chunks: ${messageCount}`);
97
+ console.log(` Tool calls: ${toolCallCount}`);
98
+ console.log("─".repeat(60));
99
+ }
100
+ catch (error) {
101
+ console.error("\nāŒ Error during test:");
102
+ console.error(error);
103
+ process.exit(1);
104
+ }
105
+ }
106
+ // Run the test
107
+ runTest().catch((error) => {
108
+ console.error("Fatal error:", error);
109
+ process.exit(1);
110
+ });
@@ -2,6 +2,10 @@ import type { PromptRequest, PromptResponse, SessionNotification } from "@agentc
2
2
  import { z } from "zod";
3
3
  import type { ContentBlock } from "../acp-server/session-storage.js";
4
4
  export declare const zAgentRunnerParams: z.ZodObject<{
5
+ displayName: z.ZodOptional<z.ZodString>;
6
+ version: z.ZodOptional<z.ZodString>;
7
+ description: z.ZodOptional<z.ZodString>;
8
+ suggestedPrompts: z.ZodOptional<z.ZodArray<z.ZodString>>;
5
9
  systemPrompt: z.ZodNullable<z.ZodString>;
6
10
  model: z.ZodString;
7
11
  tools: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">, z.ZodLiteral<"filesystem">]>, z.ZodObject<{
@@ -33,10 +37,14 @@ export declare const zAgentRunnerParams: z.ZodObject<{
33
37
  hooks: z.ZodOptional<z.ZodArray<z.ZodObject<{
34
38
  type: z.ZodEnum<{
35
39
  context_size: "context_size";
40
+ tool_response: "tool_response";
36
41
  }>;
37
- setting: z.ZodOptional<z.ZodObject<{
42
+ setting: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
38
43
  threshold: z.ZodNumber;
39
- }, z.core.$strip>>;
44
+ }, z.core.$strip>, z.ZodObject<{
45
+ maxContextThreshold: z.ZodOptional<z.ZodNumber>;
46
+ responseTruncationThreshold: z.ZodOptional<z.ZodNumber>;
47
+ }, z.core.$strip>]>>;
40
48
  callback: z.ZodString;
41
49
  }, z.core.$strip>>>;
42
50
  }, z.core.$strip>;
@@ -2,6 +2,10 @@ import { z } from "zod";
2
2
  import { HookConfigSchema, McpConfigSchema } from "../definition";
3
3
  import { zToolType } from "./tools";
4
4
  export const zAgentRunnerParams = z.object({
5
+ displayName: z.string().optional(),
6
+ version: z.string().optional(),
7
+ description: z.string().optional(),
8
+ suggestedPrompts: z.array(z.string()).optional(),
5
9
  systemPrompt: z.string().nullable(),
6
10
  model: z.string(),
7
11
  tools: z.array(zToolType).optional(),
@@ -20,4 +20,21 @@ export declare class HookExecutor {
20
20
  * Execute a context_size hook
21
21
  */
22
22
  private executeContextSizeHook;
23
+ /**
24
+ * Execute tool_response hooks when a tool returns output
25
+ */
26
+ executeToolResponseHooks(session: ReadonlySession, currentContextTokens: number, toolResponse: {
27
+ toolCallId: string;
28
+ toolName: string;
29
+ toolInput: Record<string, unknown>;
30
+ rawOutput: Record<string, unknown>;
31
+ outputTokens: number;
32
+ }): Promise<{
33
+ modifiedOutput?: Record<string, unknown>;
34
+ truncationWarning?: string;
35
+ }>;
36
+ /**
37
+ * Execute a single tool_response hook
38
+ */
39
+ private executeToolResponseHook;
23
40
  }
@@ -106,4 +106,70 @@ export class HookExecutor {
106
106
  };
107
107
  }
108
108
  }
109
+ /**
110
+ * Execute tool_response hooks when a tool returns output
111
+ */
112
+ async executeToolResponseHooks(session, currentContextTokens, toolResponse) {
113
+ logger.info(`Executing tool_response hooks - found ${this.hooks.length} hook(s)`, {
114
+ toolCallId: toolResponse.toolCallId,
115
+ toolName: toolResponse.toolName,
116
+ outputTokens: toolResponse.outputTokens,
117
+ });
118
+ for (const hook of this.hooks) {
119
+ if (hook.type === "tool_response") {
120
+ const result = await this.executeToolResponseHook(hook, session, currentContextTokens, toolResponse);
121
+ if (result) {
122
+ return result;
123
+ }
124
+ }
125
+ }
126
+ return {}; // No modifications
127
+ }
128
+ /**
129
+ * Execute a single tool_response hook
130
+ */
131
+ async executeToolResponseHook(hook, session, currentContextTokens, toolResponse) {
132
+ const maxTokens = getModelMaxTokens(this.model);
133
+ try {
134
+ // Load and execute callback
135
+ const callback = await this.loadCallback(hook.callback);
136
+ // Pass hook settings through requestParams
137
+ const sessionWithSettings = {
138
+ ...session,
139
+ requestParams: {
140
+ ...session.requestParams,
141
+ hookSettings: hook.setting,
142
+ },
143
+ };
144
+ const hookContext = {
145
+ session: sessionWithSettings,
146
+ currentTokens: currentContextTokens,
147
+ maxTokens,
148
+ percentage: (currentContextTokens / maxTokens) * 100,
149
+ model: this.model,
150
+ toolResponse,
151
+ };
152
+ const result = await callback(hookContext);
153
+ // Extract modified output and warnings from metadata
154
+ if (result.metadata) {
155
+ const response = {};
156
+ if (result.metadata.modifiedOutput) {
157
+ response.modifiedOutput = result.metadata.modifiedOutput;
158
+ }
159
+ if (result.metadata.truncationWarning) {
160
+ response.truncationWarning = result.metadata
161
+ .truncationWarning;
162
+ }
163
+ return response;
164
+ }
165
+ return null;
166
+ }
167
+ catch (error) {
168
+ logger.error("Tool response hook execution failed", {
169
+ callback: hook.callback,
170
+ error: error instanceof Error ? error.message : String(error),
171
+ });
172
+ return null; // Return original output on error
173
+ }
174
+ }
109
175
  }
@@ -99,7 +99,15 @@ Please provide your summary based on the conversation above, following this stru
99
99
  const summaryEntry = createFullMessageEntry("user", `This session is being continued from a previous conversation that ran out of context. The conversation is summarized below:\n${summaryText}`);
100
100
  // Set compactedUpTo to indicate all messages have been compacted into the summary
101
101
  const lastMessageIndex = messagesToCompact.length - 1;
102
- const newContextEntry = createContextEntry([summaryEntry], undefined, lastMessageIndex, summaryTokens);
102
+ const newContextEntry = createContextEntry([summaryEntry], undefined, lastMessageIndex, {
103
+ // Store summary tokens in userMessagesTokens since the summary is a user message
104
+ systemPromptTokens: 0,
105
+ userMessagesTokens: summaryTokens,
106
+ assistantMessagesTokens: 0,
107
+ toolInputTokens: 0,
108
+ toolResultsTokens: 0,
109
+ totalEstimated: summaryTokens,
110
+ });
103
111
  return {
104
112
  newContextEntry,
105
113
  metadata: {
@@ -0,0 +1,6 @@
1
+ import type { HookCallback } from "../types.js";
2
+ /**
3
+ * Tool response compaction hook - compacts or truncates large tool responses
4
+ * to prevent context overflow
5
+ */
6
+ export declare const toolResponseCompactor: HookCallback;