@tyvm/knowhow 0.0.83 → 0.0.85

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 (237) hide show
  1. package/package.json +4 -2
  2. package/src/agents/base/base.ts +72 -62
  3. package/src/agents/index.ts +30 -14
  4. package/src/agents/researcher/researcher.ts +1 -2
  5. package/src/agents/tools/aiClient.ts +48 -0
  6. package/src/agents/tools/list.ts +57 -0
  7. package/src/agents/tools/startAgentTask.ts +3 -1
  8. package/src/chat/CliChatService.ts +20 -4
  9. package/src/chat/modules/AgentModule.ts +399 -357
  10. package/src/chat/modules/CustomCommandsModule.ts +0 -1
  11. package/src/chat/modules/InternalChatModule.ts +18 -2
  12. package/src/chat/modules/RendererModule.ts +109 -0
  13. package/src/chat/modules/SessionsModule.ts +854 -0
  14. package/src/chat/modules/SetupModule.ts +6 -8
  15. package/src/chat/modules/index.ts +1 -0
  16. package/src/chat/renderer/CompactRenderer.ts +209 -0
  17. package/src/chat/renderer/ConsoleRenderer.ts +141 -0
  18. package/src/chat/renderer/FancyRenderer.ts +421 -0
  19. package/src/chat/renderer/index.ts +5 -0
  20. package/src/chat/renderer/loadRenderer.ts +314 -0
  21. package/src/chat/renderer/messagesToRenderEvents.ts +96 -0
  22. package/src/chat/renderer/types.ts +88 -0
  23. package/src/chat/types.ts +5 -0
  24. package/src/chat.ts +69 -5
  25. package/src/cli.ts +24 -5
  26. package/src/clients/index.ts +91 -0
  27. package/src/clients/pricing/google.ts +81 -2
  28. package/src/clients/pricing/openai.ts +68 -0
  29. package/src/config.ts +15 -0
  30. package/src/plugins/AgentsMdPlugin.ts +1 -1
  31. package/src/plugins/GitPlugin.ts +20 -20
  32. package/src/plugins/PluginBase.ts +11 -0
  33. package/src/plugins/SkillsPlugin.ts +150 -0
  34. package/src/plugins/asana.ts +4 -4
  35. package/src/plugins/embedding.ts +3 -5
  36. package/src/plugins/exec.ts +3 -3
  37. package/src/plugins/figma.ts +3 -7
  38. package/src/plugins/github.ts +18 -29
  39. package/src/plugins/jira.ts +2 -2
  40. package/src/plugins/language.ts +4 -4
  41. package/src/plugins/linear.ts +4 -4
  42. package/src/plugins/notion.ts +6 -8
  43. package/src/plugins/plugins.ts +29 -3
  44. package/src/plugins/url.ts +2 -2
  45. package/src/plugins/vim.ts +4 -3
  46. package/src/services/AgentService.ts +17 -0
  47. package/src/services/AgentSyncFs.ts +3 -0
  48. package/src/services/EventService.ts +168 -27
  49. package/src/services/KnowhowClient.ts +1 -0
  50. package/src/services/SessionManager.ts +51 -1
  51. package/src/services/SyncedAgentWatcher.ts +397 -0
  52. package/src/services/SyncerService.ts +147 -0
  53. package/src/services/index.ts +2 -0
  54. package/src/services/modules/index.ts +14 -3
  55. package/src/types.ts +103 -5
  56. package/src/worker.ts +80 -2
  57. package/src/workers/auth/PasskeySetup.ts +185 -0
  58. package/src/workers/auth/WorkerPasskeyAuth.ts +190 -0
  59. package/src/workers/auth/types.ts +58 -0
  60. package/src/workers/tools/getChallenge.ts +33 -0
  61. package/src/workers/tools/index.ts +8 -0
  62. package/src/workers/tools/lock.ts +31 -0
  63. package/src/workers/tools/unlock.ts +116 -0
  64. package/tests/clients/pricing.test.ts +144 -0
  65. package/tests/unit/modules/moduleLoading.test.ts +226 -0
  66. package/tests/unit/plugins/pluginLoading.test.ts +151 -0
  67. package/ts_build/package.json +4 -2
  68. package/ts_build/src/agents/base/base.d.ts +4 -3
  69. package/ts_build/src/agents/base/base.js +54 -30
  70. package/ts_build/src/agents/base/base.js.map +1 -1
  71. package/ts_build/src/agents/index.d.ts +3 -0
  72. package/ts_build/src/agents/index.js +21 -11
  73. package/ts_build/src/agents/index.js.map +1 -1
  74. package/ts_build/src/agents/researcher/researcher.js +1 -1
  75. package/ts_build/src/agents/researcher/researcher.js.map +1 -1
  76. package/ts_build/src/agents/tools/aiClient.d.ts +3 -0
  77. package/ts_build/src/agents/tools/aiClient.js +31 -1
  78. package/ts_build/src/agents/tools/aiClient.js.map +1 -1
  79. package/ts_build/src/agents/tools/list.js +48 -0
  80. package/ts_build/src/agents/tools/list.js.map +1 -1
  81. package/ts_build/src/agents/tools/startAgentTask.js +2 -1
  82. package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
  83. package/ts_build/src/chat/CliChatService.js +16 -5
  84. package/ts_build/src/chat/CliChatService.js.map +1 -1
  85. package/ts_build/src/chat/modules/AgentModule.d.ts +34 -17
  86. package/ts_build/src/chat/modules/AgentModule.js +248 -258
  87. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  88. package/ts_build/src/chat/modules/CustomCommandsModule.js.map +1 -1
  89. package/ts_build/src/chat/modules/InternalChatModule.d.ts +3 -0
  90. package/ts_build/src/chat/modules/InternalChatModule.js +16 -1
  91. package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
  92. package/ts_build/src/chat/modules/RendererModule.d.ts +16 -0
  93. package/ts_build/src/chat/modules/RendererModule.js +76 -0
  94. package/ts_build/src/chat/modules/RendererModule.js.map +1 -0
  95. package/ts_build/src/chat/modules/SessionsModule.d.ts +33 -0
  96. package/ts_build/src/chat/modules/SessionsModule.js +582 -0
  97. package/ts_build/src/chat/modules/SessionsModule.js.map +1 -0
  98. package/ts_build/src/chat/modules/SetupModule.d.ts +3 -3
  99. package/ts_build/src/chat/modules/SetupModule.js +4 -6
  100. package/ts_build/src/chat/modules/SetupModule.js.map +1 -1
  101. package/ts_build/src/chat/modules/index.d.ts +1 -0
  102. package/ts_build/src/chat/modules/index.js +3 -1
  103. package/ts_build/src/chat/modules/index.js.map +1 -1
  104. package/ts_build/src/chat/renderer/CompactRenderer.d.ts +23 -0
  105. package/ts_build/src/chat/renderer/CompactRenderer.js +167 -0
  106. package/ts_build/src/chat/renderer/CompactRenderer.js.map +1 -0
  107. package/ts_build/src/chat/renderer/ConsoleRenderer.d.ts +22 -0
  108. package/ts_build/src/chat/renderer/ConsoleRenderer.js +110 -0
  109. package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -0
  110. package/ts_build/src/chat/renderer/FancyRenderer.d.ts +23 -0
  111. package/ts_build/src/chat/renderer/FancyRenderer.js +328 -0
  112. package/ts_build/src/chat/renderer/FancyRenderer.js.map +1 -0
  113. package/ts_build/src/chat/renderer/index.d.ts +5 -0
  114. package/ts_build/src/chat/renderer/index.js +29 -0
  115. package/ts_build/src/chat/renderer/index.js.map +1 -0
  116. package/ts_build/src/chat/renderer/loadRenderer.d.ts +4 -0
  117. package/ts_build/src/chat/renderer/loadRenderer.js +246 -0
  118. package/ts_build/src/chat/renderer/loadRenderer.js.map +1 -0
  119. package/ts_build/src/chat/renderer/messagesToRenderEvents.d.ts +15 -0
  120. package/ts_build/src/chat/renderer/messagesToRenderEvents.js +72 -0
  121. package/ts_build/src/chat/renderer/messagesToRenderEvents.js.map +1 -0
  122. package/ts_build/src/chat/renderer/types.d.ts +75 -0
  123. package/ts_build/src/chat/renderer/types.js +3 -0
  124. package/ts_build/src/chat/renderer/types.js.map +1 -0
  125. package/ts_build/src/chat/types.d.ts +5 -0
  126. package/ts_build/src/chat.js +46 -4
  127. package/ts_build/src/chat.js.map +1 -1
  128. package/ts_build/src/cli.js +18 -5
  129. package/ts_build/src/cli.js.map +1 -1
  130. package/ts_build/src/clients/gemini.d.ts +10 -10
  131. package/ts_build/src/clients/index.d.ts +10 -0
  132. package/ts_build/src/clients/index.js +58 -0
  133. package/ts_build/src/clients/index.js.map +1 -1
  134. package/ts_build/src/clients/pricing/google.d.ts +10 -10
  135. package/ts_build/src/clients/pricing/google.js +74 -2
  136. package/ts_build/src/clients/pricing/google.js.map +1 -1
  137. package/ts_build/src/clients/pricing/openai.js +65 -0
  138. package/ts_build/src/clients/pricing/openai.js.map +1 -1
  139. package/ts_build/src/config.d.ts +1 -0
  140. package/ts_build/src/config.js +17 -1
  141. package/ts_build/src/config.js.map +1 -1
  142. package/ts_build/src/plugins/AgentsMdPlugin.js +1 -1
  143. package/ts_build/src/plugins/AgentsMdPlugin.js.map +1 -1
  144. package/ts_build/src/plugins/GitPlugin.js +20 -20
  145. package/ts_build/src/plugins/GitPlugin.js.map +1 -1
  146. package/ts_build/src/plugins/PluginBase.d.ts +1 -0
  147. package/ts_build/src/plugins/PluginBase.js +13 -0
  148. package/ts_build/src/plugins/PluginBase.js.map +1 -1
  149. package/ts_build/src/plugins/SkillsPlugin.d.ts +13 -0
  150. package/ts_build/src/plugins/SkillsPlugin.js +149 -0
  151. package/ts_build/src/plugins/SkillsPlugin.js.map +1 -0
  152. package/ts_build/src/plugins/asana.js +4 -4
  153. package/ts_build/src/plugins/asana.js.map +1 -1
  154. package/ts_build/src/plugins/embedding.js +3 -3
  155. package/ts_build/src/plugins/embedding.js.map +1 -1
  156. package/ts_build/src/plugins/exec.js +3 -3
  157. package/ts_build/src/plugins/exec.js.map +1 -1
  158. package/ts_build/src/plugins/figma.js +3 -3
  159. package/ts_build/src/plugins/figma.js.map +1 -1
  160. package/ts_build/src/plugins/github.js +18 -18
  161. package/ts_build/src/plugins/github.js.map +1 -1
  162. package/ts_build/src/plugins/jira.js +2 -2
  163. package/ts_build/src/plugins/jira.js.map +1 -1
  164. package/ts_build/src/plugins/language.js +4 -4
  165. package/ts_build/src/plugins/language.js.map +1 -1
  166. package/ts_build/src/plugins/linear.js +4 -4
  167. package/ts_build/src/plugins/linear.js.map +1 -1
  168. package/ts_build/src/plugins/notion.js +6 -6
  169. package/ts_build/src/plugins/notion.js.map +1 -1
  170. package/ts_build/src/plugins/plugins.d.ts +3 -0
  171. package/ts_build/src/plugins/plugins.js +18 -3
  172. package/ts_build/src/plugins/plugins.js.map +1 -1
  173. package/ts_build/src/plugins/url.js +2 -2
  174. package/ts_build/src/plugins/url.js.map +1 -1
  175. package/ts_build/src/plugins/vim.js +2 -2
  176. package/ts_build/src/plugins/vim.js.map +1 -1
  177. package/ts_build/src/services/AgentService.d.ts +3 -0
  178. package/ts_build/src/services/AgentService.js +7 -0
  179. package/ts_build/src/services/AgentService.js.map +1 -1
  180. package/ts_build/src/services/AgentSyncFs.d.ts +1 -0
  181. package/ts_build/src/services/AgentSyncFs.js +2 -0
  182. package/ts_build/src/services/AgentSyncFs.js.map +1 -1
  183. package/ts_build/src/services/EventService.d.ts +25 -2
  184. package/ts_build/src/services/EventService.js +92 -14
  185. package/ts_build/src/services/EventService.js.map +1 -1
  186. package/ts_build/src/services/KnowhowClient.d.ts +1 -0
  187. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  188. package/ts_build/src/services/SessionManager.d.ts +6 -0
  189. package/ts_build/src/services/SessionManager.js +39 -1
  190. package/ts_build/src/services/SessionManager.js.map +1 -1
  191. package/ts_build/src/services/SyncedAgentWatcher.d.ts +101 -0
  192. package/ts_build/src/services/SyncedAgentWatcher.js +312 -0
  193. package/ts_build/src/services/SyncedAgentWatcher.js.map +1 -0
  194. package/ts_build/src/services/SyncerService.d.ts +30 -0
  195. package/ts_build/src/services/SyncerService.js +72 -0
  196. package/ts_build/src/services/SyncerService.js.map +1 -0
  197. package/ts_build/src/services/index.d.ts +2 -0
  198. package/ts_build/src/services/index.js +2 -0
  199. package/ts_build/src/services/index.js.map +1 -1
  200. package/ts_build/src/services/modules/index.js +10 -2
  201. package/ts_build/src/services/modules/index.js.map +1 -1
  202. package/ts_build/src/types.d.ts +51 -2
  203. package/ts_build/src/types.js +73 -5
  204. package/ts_build/src/types.js.map +1 -1
  205. package/ts_build/src/worker.d.ts +2 -0
  206. package/ts_build/src/worker.js +59 -4
  207. package/ts_build/src/worker.js.map +1 -1
  208. package/ts_build/src/workers/auth/PasskeySetup.d.ts +10 -0
  209. package/ts_build/src/workers/auth/PasskeySetup.js +131 -0
  210. package/ts_build/src/workers/auth/PasskeySetup.js.map +1 -0
  211. package/ts_build/src/workers/auth/WorkerPasskeyAuth.d.ts +35 -0
  212. package/ts_build/src/workers/auth/WorkerPasskeyAuth.js +129 -0
  213. package/ts_build/src/workers/auth/WorkerPasskeyAuth.js.map +1 -0
  214. package/ts_build/src/workers/auth/types.d.ts +36 -0
  215. package/ts_build/src/workers/auth/types.js +3 -0
  216. package/ts_build/src/workers/auth/types.js.map +1 -0
  217. package/ts_build/src/workers/tools/getChallenge.d.ts +9 -0
  218. package/ts_build/src/workers/tools/getChallenge.js +27 -0
  219. package/ts_build/src/workers/tools/getChallenge.js.map +1 -0
  220. package/ts_build/src/workers/tools/index.d.ts +6 -0
  221. package/ts_build/src/workers/tools/index.js +10 -0
  222. package/ts_build/src/workers/tools/index.js.map +1 -1
  223. package/ts_build/src/workers/tools/lock.d.ts +9 -0
  224. package/ts_build/src/workers/tools/lock.js +27 -0
  225. package/ts_build/src/workers/tools/lock.js.map +1 -0
  226. package/ts_build/src/workers/tools/unlock.d.ts +18 -0
  227. package/ts_build/src/workers/tools/unlock.js +78 -0
  228. package/ts_build/src/workers/tools/unlock.js.map +1 -0
  229. package/ts_build/tests/clients/pricing.test.d.ts +1 -0
  230. package/ts_build/tests/clients/pricing.test.js +90 -0
  231. package/ts_build/tests/clients/pricing.test.js.map +1 -0
  232. package/ts_build/tests/unit/modules/moduleLoading.test.d.ts +1 -0
  233. package/ts_build/tests/unit/modules/moduleLoading.test.js +187 -0
  234. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -0
  235. package/ts_build/tests/unit/plugins/pluginLoading.test.d.ts +1 -0
  236. package/ts_build/tests/unit/plugins/pluginLoading.test.js +123 -0
  237. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -0
@@ -7,6 +7,7 @@ import { AgentContext } from "../agents/base/base";
7
7
 
8
8
  export class AgentService {
9
9
  private agents: Map<string, IAgent> = new Map();
10
+ private agentContext: AgentContext | null = null;
10
11
 
11
12
  constructor(private tools: ToolsService, private events: EventService) {
12
13
  this.wireUp();
@@ -57,6 +58,22 @@ export class AgentService {
57
58
  this.agents.set(name, agent);
58
59
  }
59
60
 
61
+ /**
62
+ * Set the AgentContext that will be used when creating new agent instances.
63
+ * Should be called from cli.ts after all services are wired up (including LazyToolsService).
64
+ */
65
+ public setAgentContext(context: AgentContext): void {
66
+ this.agentContext = context;
67
+ }
68
+
69
+ /**
70
+ * Get the current AgentContext. Falls back to a minimal context using this service's
71
+ * own tools/events if none has been explicitly set.
72
+ */
73
+ public getAgentContext(): AgentContext {
74
+ return this.agentContext ?? { Tools: this.tools, Events: this.events };
75
+ }
76
+
60
77
  public getAgent(name: string): IAgent {
61
78
  const agent = this.agents.get(name);
62
79
  if (!agent) {
@@ -10,6 +10,7 @@ import { watch } from "fs";
10
10
  export interface FsSyncOptions {
11
11
  taskId: string;
12
12
  prompt: string;
13
+ agentName?: string;
13
14
  }
14
15
 
15
16
  /**
@@ -51,6 +52,7 @@ export class AgentSyncFs {
51
52
  await this.writeMetadata({
52
53
  taskId: this.taskId,
53
54
  prompt: options.prompt,
55
+ agentName: options.agentName || "unknown",
54
56
  startTime: new Date().toISOString(),
55
57
  status: "running",
56
58
  });
@@ -125,6 +127,7 @@ export class AgentSyncFs {
125
127
 
126
128
  metadata.threads = agent.getThreads();
127
129
  metadata.totalCostUsd = agent.getTotalCostUsd();
130
+ metadata.agentName = agent.name;
128
131
  metadata.inProgress = inProgress;
129
132
  metadata.lastUpdate = new Date().toISOString();
130
133
 
@@ -1,30 +1,57 @@
1
1
  import { EventEmitter } from "events";
2
2
  import { IAgent } from "../agents/interface";
3
3
 
4
+ export type EventHandlerFn = (...args: any[]) => any;
5
+
4
6
  export interface EventHandler {
5
- handler: (...args: any[]) => any;
7
+ handler: EventHandlerFn;
8
+ }
9
+
10
+ export interface ManagedListenerSpec {
11
+ key: string;
12
+ event: string;
13
+ once?: boolean;
14
+ blocking?: boolean;
15
+ }
16
+
17
+ export interface AgentLogEvent {
18
+ agentName: string;
19
+ message: string;
20
+ level: "info" | "warn" | "error";
21
+ timestamp: number;
22
+ taskId?: string | null;
6
23
  }
7
24
 
25
+ type ManagedListenerRecord = {
26
+ key: string;
27
+ event: string;
28
+ handler: EventHandlerFn;
29
+ wrappedHandler: EventHandlerFn;
30
+ once: boolean;
31
+ blocking: boolean;
32
+ };
33
+
8
34
  export class EventService extends EventEmitter {
9
35
  private blockingHandlers: Map<string, EventHandler[]> = new Map();
36
+ private managedListeners: Map<string, ManagedListenerRecord> = new Map();
10
37
 
11
38
  eventTypes = {
12
39
  agentMsg: "agent:msg",
13
40
  agentsRegister: "agents:register",
14
41
  agentsCall: "agents:call",
42
+ pluginLog: "plugin:log",
15
43
  };
16
44
 
17
45
  constructor() {
18
46
  super();
47
+ this.setMaxListeners(100);
19
48
  }
20
49
 
21
50
  /**
22
- * Register an event handler as blocking or non-blocking
23
- * @param event The event name
24
- * @param handler The event handler function
25
- * @param isBlocking Whether this handler should be blocking
51
+ * Register a blocking handler.
52
+ * These run during emitBlocking / emitNonBlocking before normal EventEmitter listeners.
26
53
  */
27
- onBlocking(event: string, handler: (...args: any[]) => any): void {
54
+ onBlocking(event: string, handler: EventHandlerFn): void {
28
55
  if (!this.blockingHandlers.has(event)) {
29
56
  this.blockingHandlers.set(event, []);
30
57
  }
@@ -33,10 +60,122 @@ export class EventService extends EventEmitter {
33
60
  }
34
61
 
35
62
  /**
36
- * Emit a blocking event - if any blocking handler throws, execution stops
37
- * @param event The event name
38
- * @param args Arguments to pass to handlers
39
- * @returns Promise that resolves with array of handler results when all handlers complete, or rejects if any blocking handler throws
63
+ * Remove a blocking handler.
64
+ */
65
+ offBlocking(event: string, handler: EventHandlerFn): void {
66
+ const handlers = this.blockingHandlers.get(event);
67
+ if (!handlers) return;
68
+
69
+ const filtered = handlers.filter((entry) => entry.handler !== handler);
70
+
71
+ if (filtered.length === 0) {
72
+ this.blockingHandlers.delete(event);
73
+ return;
74
+ }
75
+
76
+ this.blockingHandlers.set(event, filtered);
77
+ }
78
+
79
+ /**
80
+ * Set a managed listener.
81
+ *
82
+ * Semantics:
83
+ * - key is unique
84
+ * - if a listener already exists for this key, it is removed first
85
+ * - supports normal or blocking listeners
86
+ * - supports once semantics
87
+ */
88
+ setListener(spec: ManagedListenerSpec, handler: EventHandlerFn): void {
89
+ const { key, event, once = false, blocking = false } = spec;
90
+
91
+ this.removeManagedListener(key);
92
+
93
+ let wrappedHandler: EventHandlerFn;
94
+
95
+ if (once) {
96
+ wrappedHandler = (...args: any[]) => {
97
+ try {
98
+ handler(...args);
99
+ } finally {
100
+ this.managedListeners.delete(key);
101
+ }
102
+ };
103
+ } else {
104
+ wrappedHandler = handler;
105
+ }
106
+
107
+ const record: ManagedListenerRecord = {
108
+ key,
109
+ event,
110
+ handler,
111
+ wrappedHandler,
112
+ once,
113
+ blocking,
114
+ };
115
+
116
+ if (blocking) {
117
+ if (!this.blockingHandlers.has(event)) {
118
+ this.blockingHandlers.set(event, []);
119
+ }
120
+
121
+ this.blockingHandlers.get(event)!.push({ handler: wrappedHandler });
122
+ } else {
123
+ if (once) {
124
+ super.once(event, wrappedHandler);
125
+ } else {
126
+ super.on(event, wrappedHandler);
127
+ }
128
+ }
129
+
130
+ this.managedListeners.set(key, record);
131
+ }
132
+
133
+ /**
134
+ * Remove a managed listener by key.
135
+ */
136
+ removeManagedListener(key: string): void {
137
+ const existing = this.managedListeners.get(key);
138
+ if (!existing) return;
139
+
140
+ if (existing.blocking) {
141
+ this.offBlocking(existing.event, existing.wrappedHandler);
142
+ } else {
143
+ super.removeListener(existing.event, existing.wrappedHandler);
144
+ }
145
+
146
+ this.managedListeners.delete(key);
147
+ }
148
+
149
+ /**
150
+ * Remove all managed listeners whose key starts with the given prefix.
151
+ */
152
+ removeManagedListenersByPrefix(prefix: string): void {
153
+ for (const key of Array.from(this.managedListeners.keys())) {
154
+ if (key.startsWith(prefix)) {
155
+ this.removeManagedListener(key);
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Remove all managed listeners registered through setListener.
162
+ */
163
+ clearManagedListeners(): void {
164
+ for (const key of Array.from(this.managedListeners.keys())) {
165
+ this.removeManagedListener(key);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Check whether a managed listener exists for the given key.
171
+ */
172
+ hasManagedListener(key: string): boolean {
173
+ return this.managedListeners.has(key);
174
+ }
175
+
176
+ /**
177
+ * Emit a blocking event - if any blocking handler throws, execution stops.
178
+ * After blocking handlers succeed, normal EventEmitter listeners are emitted.
40
179
  */
41
180
  async emitBlocking(event: string, ...args: any[]): Promise<any[]> {
42
181
  const results: any[] = [];
@@ -45,14 +184,8 @@ export class EventService extends EventEmitter {
45
184
  for (const { handler } of handlers) {
46
185
  try {
47
186
  const result = handler(...args);
48
- if (result instanceof Promise) {
49
- const awaitedResult = await result;
50
- results.push(awaitedResult);
51
- } else {
52
- results.push(result);
53
- }
187
+ results.push(result instanceof Promise ? await result : result);
54
188
  } catch (error) {
55
- // If this is a blocking handler and it throws, stop execution
56
189
  throw error;
57
190
  }
58
191
  }
@@ -62,23 +195,18 @@ export class EventService extends EventEmitter {
62
195
  }
63
196
 
64
197
  /**
65
- * Emit a non-blocking event - all handlers run, errors are logged but don't stop execution
66
- * @param event The event name
67
- * @param args Arguments to pass to handlers
68
- * @returns Promise that resolves with array of handler results when all handlers complete
198
+ * Emit a non-blocking event - blocking handlers still run first,
199
+ * but their errors are logged instead of thrown.
200
+ * Then normal EventEmitter listeners are emitted.
69
201
  */
70
202
  async emitNonBlocking(event: string, ...args: any[]): Promise<any[]> {
71
203
  const handlers = this.blockingHandlers.get(event) || [];
72
204
  const results: any[] = [];
205
+
73
206
  for (const { handler } of handlers) {
74
207
  try {
75
208
  const result = handler(...args);
76
- if (result instanceof Promise) {
77
- const awaitedResult = await result;
78
- results.push(awaitedResult);
79
- } else {
80
- results.push(result);
81
- }
209
+ results.push(result instanceof Promise ? await result : result);
82
210
  } catch (error) {
83
211
  console.error(
84
212
  `Non-blocking handler error for event '${event}':`,
@@ -100,4 +228,17 @@ export class EventService extends EventEmitter {
100
228
  this.emit(this.eventTypes.agentsCall, { name, query, resolve, reject });
101
229
  });
102
230
  }
231
+
232
+ log(
233
+ source: string,
234
+ message: string,
235
+ level: "info" | "warn" | "error" = "info"
236
+ ): void {
237
+ this.emit(this.eventTypes.pluginLog, {
238
+ source,
239
+ message,
240
+ level,
241
+ timestamp: Date.now(),
242
+ });
243
+ }
103
244
  }
@@ -59,6 +59,7 @@ export interface TaskDetailsResponse {
59
59
  messageId?: string;
60
60
  hasPendingMessages: boolean;
61
61
  pendingMessagesCount: number;
62
+ result?: string;
62
63
  }
63
64
 
64
65
  export interface PendingMessage {
@@ -2,6 +2,7 @@
2
2
  * Session Manager Service - Handles agent session persistence and restoration
3
3
  */
4
4
  import * as fs from "fs";
5
+ import * as fsPromises from "fs/promises";
5
6
  import * as path from "path";
6
7
  import { TaskInfo, ChatSession } from "../chat/types";
7
8
 
@@ -38,7 +39,9 @@ export class SessionManager {
38
39
 
39
40
  const wordPart = words.join("-") || "task";
40
41
  const epochSeconds = Math.floor(Date.now() / 1000);
41
- return `${epochSeconds}-${wordPart}`;
42
+ const fullId = `${epochSeconds}-${wordPart}`;
43
+ // Truncate to 80 chars to avoid ENAMETOOLONG filesystem errors
44
+ return fullId.slice(0, 80);
42
45
  }
43
46
 
44
47
  /**
@@ -284,4 +287,51 @@ export class SessionManager {
284
287
 
285
288
  console.log("─".repeat(80));
286
289
  }
290
+
291
+ /**
292
+ * Discover agents running in other processes via the filesystem.
293
+ * By default only returns agents that are NOT completed/killed.
294
+ */
295
+ public async discoverFsAgents(
296
+ registeredIds: Set<string>,
297
+ includeCompleted: boolean = false
298
+ ): Promise<Array<{ taskId: string; agentName: string; status: string; totalCostUsd?: number }>> {
299
+ const agentsDir = path.join(".knowhow", "processes", "agents");
300
+ if (!fs.existsSync(agentsDir)) return [];
301
+
302
+ const results: Array<{ taskId: string; agentName: string; status: string; totalCostUsd?: number }> = [];
303
+
304
+ try {
305
+ const entries = await fsPromises.readdir(agentsDir, { withFileTypes: true });
306
+ for (const entry of entries) {
307
+ if (!entry.isDirectory()) continue;
308
+ const taskId = entry.name;
309
+ if (registeredIds.has(taskId)) continue; // Already in-process
310
+ const metadataPath = path.join(agentsDir, taskId, "metadata.json");
311
+ try {
312
+ const raw = await fsPromises.readFile(metadataPath, "utf-8");
313
+ const metadata = JSON.parse(raw);
314
+ const status = metadata.status || "unknown";
315
+
316
+ // Skip completed/killed tasks unless explicitly requested
317
+ if (!includeCompleted && (status === "completed" || status === "killed")) {
318
+ continue;
319
+ }
320
+
321
+ results.push({
322
+ taskId,
323
+ agentName: metadata.agentName || "unknown",
324
+ status,
325
+ totalCostUsd: metadata.totalCostUsd,
326
+ });
327
+ } catch {
328
+ // Skip dirs without metadata
329
+ }
330
+ }
331
+ } catch {
332
+ // agentsDir not readable
333
+ }
334
+
335
+ return results;
336
+ }
287
337
  }