@townco/agent 0.1.112 → 0.1.114

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.
@@ -1,22 +1,14 @@
1
1
  import { createHash } from "node:crypto";
2
- import { EventEmitter } from "node:events";
3
2
  import { createLogger } from "@townco/core";
3
+ import { getInvocationContext } from "../../session-context.js";
4
4
  const logger = createLogger("subagent-connections");
5
- /**
6
- * Event emitter for subagent connection events.
7
- * The runner listens to these events and emits tool_call_update.
8
- */
9
- export const subagentEvents = new EventEmitter();
10
5
  /**
11
6
  * Maps query hash to toolCallId.
12
7
  * Set by the runner when it sees a subagent tool_call.
8
+ * This is still global because the registration happens in the parent runner
9
+ * before subagent execution begins.
13
10
  */
14
11
  export const queryToToolCallId = new Map();
15
- /**
16
- * Maps query hash to resolved toolCallId (preserved after connection event).
17
- * Used to correlate messages when the tool completes.
18
- */
19
- const queryToResolvedToolCallId = new Map();
20
12
  /**
21
13
  * Generate a hash from the query string for correlation.
22
14
  */
@@ -29,61 +21,45 @@ export function hashQuery(query) {
29
21
  return hash;
30
22
  }
31
23
  /**
32
- * Called by the subagent tool when connection is established.
33
- * Emits an event that the runner can listen to.
24
+ * Called by the subagent tool during execution to emit incremental messages.
25
+ * Emits an event with the messages for live streaming to the UI.
26
+ * Uses the invocation context's EventEmitter to ensure messages go to the correct parent.
27
+ * @param completed - If true, signals that the subagent stream has finished
34
28
  */
35
- export function emitSubagentConnection(queryHash, connectionInfo) {
36
- logger.info("emitSubagentConnection called", {
37
- queryHash,
38
- port: connectionInfo.port,
39
- sessionId: connectionInfo.sessionId,
40
- registeredHashes: Array.from(queryToToolCallId.keys()),
41
- });
29
+ export function emitSubagentMessages(queryHash, messages, completed = false) {
42
30
  const toolCallId = queryToToolCallId.get(queryHash);
43
- if (toolCallId) {
44
- logger.info("Found toolCallId for queryHash, emitting connection event", {
45
- queryHash,
46
- toolCallId,
47
- port: connectionInfo.port,
48
- sessionId: connectionInfo.sessionId,
49
- });
50
- subagentEvents.emit("connection", {
51
- toolCallId,
52
- ...connectionInfo,
53
- });
54
- // Preserve the toolCallId for message emission, but remove from pending lookup
55
- queryToResolvedToolCallId.set(queryHash, toolCallId);
56
- queryToToolCallId.delete(queryHash);
57
- }
58
- else {
59
- logger.warn("No toolCallId found for queryHash", {
31
+ const invocationCtx = getInvocationContext();
32
+ if (!invocationCtx) {
33
+ logger.warn("✗ No invocation context found - cannot emit subagent messages", {
60
34
  queryHash,
61
- registeredHashes: Array.from(queryToToolCallId.keys()),
35
+ hasToolCallId: !!toolCallId,
62
36
  });
37
+ return;
63
38
  }
64
- }
65
- /**
66
- * Called by the subagent tool when it completes with accumulated messages.
67
- * Emits an event with the messages for session storage.
68
- */
69
- export function emitSubagentMessages(queryHash, messages) {
70
- const toolCallId = queryToResolvedToolCallId.get(queryHash);
71
39
  if (toolCallId) {
72
- logger.info("Emitting subagent messages for storage", {
40
+ const firstMessage = messages[0];
41
+ logger.info("✓ Emitting subagent messages for live streaming", {
73
42
  queryHash,
74
43
  toolCallId,
75
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,
76
49
  });
77
- subagentEvents.emit("messages", {
50
+ // Emit to the parent's invocation-scoped EventEmitter
51
+ invocationCtx.subagentEventEmitter.emit("messages", {
78
52
  toolCallId,
79
53
  messages,
54
+ completed,
80
55
  });
81
- // Clean up the resolved mapping
82
- queryToResolvedToolCallId.delete(queryHash);
83
56
  }
84
57
  else {
85
- logger.warn("No resolved toolCallId for messages emission", {
58
+ logger.warn("No toolCallId found for messages emission (RACE CONDITION)", {
86
59
  queryHash,
60
+ registeredHashes: Array.from(queryToToolCallId.keys()),
61
+ mapSize: queryToToolCallId.size,
62
+ invocationId: invocationCtx.invocationId,
87
63
  });
88
64
  }
89
65
  }