@townco/agent 0.1.113 → 0.1.115

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.
@@ -22,7 +22,7 @@ export async function getTownE2BApiKey() {
22
22
  return _apiKeyFetchPromise;
23
23
  }
24
24
  _apiKeyFetchPromise = (async () => {
25
- const shedAuth = getShedAuth();
25
+ const shedAuth = await getShedAuth();
26
26
  if (!shedAuth) {
27
27
  throw new Error("Not logged in. Run 'town login' or set SHED_API_KEY to use the code_sandbox tools.");
28
28
  }
@@ -329,7 +329,7 @@ function makeE2BToolsInternal(getSandbox) {
329
329
  const fileBuffer = Buffer.from(result.stdout.trim(), "base64");
330
330
  await fs.writeFile(outputPath, fileBuffer);
331
331
  // Step 2: Upload to Supabase Storage
332
- const shedAuth = getShedAuth();
332
+ const shedAuth = await getShedAuth();
333
333
  if (!shedAuth) {
334
334
  // Fallback to local URL if not authenticated
335
335
  const port = process.env.PORT || "3100";
@@ -37,16 +37,6 @@ export function emitSubagentMessages(queryHash, messages, completed = false) {
37
37
  return;
38
38
  }
39
39
  if (toolCallId) {
40
- const firstMessage = messages[0];
41
- logger.info("✓ Emitting subagent messages for live streaming", {
42
- queryHash,
43
- toolCallId,
44
- messageCount: messages.length,
45
- hasContent: firstMessage ? firstMessage.content.length > 0 : false,
46
- hasToolCalls: firstMessage ? firstMessage.toolCalls.length > 0 : false,
47
- completed,
48
- invocationId: invocationCtx.invocationId,
49
- });
50
40
  // Emit to the parent's invocation-scoped EventEmitter
51
41
  invocationCtx.subagentEventEmitter.emit("messages", {
52
42
  toolCallId,
@@ -3,11 +3,13 @@ import * as fs from "node:fs/promises";
3
3
  import { mkdir } from "node:fs/promises";
4
4
  import * as path from "node:path";
5
5
  import { context, propagation, trace } from "@opentelemetry/api";
6
+ import { createLogger } from "@townco/core";
6
7
  import { z } from "zod";
7
8
  import { SUBAGENT_MODE_KEY, } from "../../../acp-server/adapter.js";
8
9
  import { makeRunnerFromDefinition } from "../../index.js";
9
10
  import { bindGeneratorToSessionContext, getAbortSignal, } from "../../session-context.js";
10
11
  import { emitSubagentMessages, hashQuery, } from "./subagent-connections.js";
12
+ const logger = createLogger("subagent-tool", "debug");
11
13
  /**
12
14
  * Name of the Task tool created by makeSubagentsTool
13
15
  */
@@ -217,6 +219,11 @@ async function querySubagent(agentName, agentPath, agentWorkingDirectory, query)
217
219
  };
218
220
  const toolCallMap = new Map();
219
221
  const queryHash = hashQuery(query);
222
+ logger.info("[DEBUG] Starting subagent generator loop", {
223
+ agentName,
224
+ queryHash,
225
+ sessionId: subagentSessionId,
226
+ });
220
227
  try {
221
228
  for await (const update of generator) {
222
229
  let shouldEmit = false;
@@ -291,23 +298,60 @@ async function querySubagent(agentName, agentPath, agentWorkingDirectory, query)
291
298
  }
292
299
  // Emit incremental update to parent (for live streaming)
293
300
  if (shouldEmit) {
301
+ logger.debug("[SUBAGENT-ACCUMULATION] Emitting incremental update", {
302
+ agentName,
303
+ queryHash,
304
+ contentLength: currentMessage.content.length,
305
+ contentBlocksCount: currentMessage.contentBlocks.length,
306
+ toolCallsCount: currentMessage.toolCalls.length,
307
+ });
294
308
  emitSubagentMessages(queryHash, [{ ...currentMessage }]);
295
309
  }
296
310
  }
311
+ logger.info("[DEBUG] Subagent generator loop finished", {
312
+ agentName,
313
+ queryHash,
314
+ sessionId: subagentSessionId,
315
+ contentLength: currentMessage.content.length,
316
+ toolCallCount: currentMessage.toolCalls.length,
317
+ });
297
318
  // Final emit to ensure everything is captured, with completion flag
298
319
  if (currentMessage.content || currentMessage.toolCalls.length > 0) {
320
+ logger.info("[DEBUG] Emitting final completion flag", {
321
+ agentName,
322
+ queryHash,
323
+ sessionId: subagentSessionId,
324
+ hasContent: true,
325
+ });
299
326
  emitSubagentMessages(queryHash, [currentMessage], true);
300
327
  }
301
328
  else {
302
329
  // Even if no messages, emit completion sentinel
330
+ logger.info("[DEBUG] Emitting empty completion flag", {
331
+ agentName,
332
+ queryHash,
333
+ sessionId: subagentSessionId,
334
+ hasContent: false,
335
+ });
303
336
  emitSubagentMessages(queryHash, [], true);
304
337
  }
338
+ logger.info("[DEBUG] Subagent querySubagent() returning result", {
339
+ agentName,
340
+ queryHash,
341
+ sessionId: subagentSessionId,
342
+ });
305
343
  return {
306
344
  text: responseText,
307
345
  sources: collectedSources,
308
346
  };
309
347
  }
310
348
  catch (error) {
349
+ logger.info("[DEBUG] Subagent querySubagent() caught error", {
350
+ agentName,
351
+ queryHash,
352
+ sessionId: subagentSessionId,
353
+ error: error instanceof Error ? error.message : String(error),
354
+ });
311
355
  // Emit completion sentinel even on error to prevent parent from hanging
312
356
  emitSubagentMessages(queryHash, [], true);
313
357
  if (parentAbortSignal?.aborted) {
@@ -19,11 +19,11 @@ function getDirectExaClient() {
19
19
  return _directExaClient;
20
20
  }
21
21
  /** Get Exa client using Town proxy with authenticated credentials */
22
- function getTownExaClient() {
22
+ async function getTownExaClient() {
23
23
  if (_townExaClient) {
24
24
  return _townExaClient;
25
25
  }
26
- const shedAuth = getShedAuth();
26
+ const shedAuth = await getShedAuth();
27
27
  if (!shedAuth) {
28
28
  throw new Error("Not logged in. Run 'town login' or set SHED_API_KEY to use the town_web_search tool.");
29
29
  }
@@ -43,7 +43,7 @@ export function getWebSearchCitationCounter() {
43
43
  }
44
44
  function makeWebSearchToolsInternal(getClient) {
45
45
  const webSearch = tool(async ({ query }) => {
46
- const client = getClient();
46
+ const client = await getClient();
47
47
  const result = await client.searchAndContents(query, {
48
48
  numResults: 5,
49
49
  type: "auto",
@@ -102,7 +102,7 @@ function makeWebSearchToolsInternal(getClient) {
102
102
  paramKey: "query",
103
103
  };
104
104
  const webFetch = tool(async ({ url, prompt }) => {
105
- const client = getClient();
105
+ const client = await getClient();
106
106
  try {
107
107
  const result = await client.getContents([url], {
108
108
  text: true,