@tyvm/knowhow 0.0.56 → 0.0.59

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 (190) hide show
  1. package/package.json +3 -3
  2. package/src/agents/base/base.ts +87 -43
  3. package/src/agents/tools/execCommand.ts +17 -14
  4. package/src/agents/tools/googleSearch.ts +1 -0
  5. package/src/agents/tools/index.ts +1 -0
  6. package/src/agents/tools/lazy/definitions.ts +63 -0
  7. package/src/agents/tools/lazy/disableTools.ts +16 -0
  8. package/src/agents/tools/lazy/enableTools.ts +16 -0
  9. package/src/agents/tools/lazy/index.ts +3 -0
  10. package/src/agents/tools/lazy/listAvailableTools.ts +14 -0
  11. package/src/agents/tools/list.ts +2 -0
  12. package/src/agents/tools/mcp/connectMcpServer.ts +40 -0
  13. package/src/agents/tools/mcp/definitions.ts +67 -0
  14. package/src/agents/tools/mcp/disconnectMcpServer.ts +40 -0
  15. package/src/agents/tools/mcp/index.ts +3 -0
  16. package/src/agents/tools/mcp/listAvailableMcpServers.ts +28 -0
  17. package/src/agents/tools/writeFile.ts +4 -1
  18. package/src/chat/CliChatService.ts +8 -3
  19. package/src/chat/modules/AgentModule.ts +74 -296
  20. package/src/cli.ts +33 -10
  21. package/src/plugins/GitPlugin.ts +30 -24
  22. package/src/plugins/language.ts +95 -18
  23. package/src/processors/ToolResponseCache.ts +98 -79
  24. package/src/processors/tools/grepToolResponse.ts +99 -0
  25. package/src/processors/tools/index.ts +21 -0
  26. package/src/processors/tools/jqToolResponse.ts +124 -0
  27. package/src/processors/tools/listStoredToolResponses.ts +83 -0
  28. package/src/processors/tools/tailToolResponse.ts +75 -0
  29. package/src/services/AgentService.ts +1 -1
  30. package/src/services/AgentSynchronization.ts +291 -0
  31. package/src/services/DockerService.ts +37 -1
  32. package/src/services/EventService.ts +8 -2
  33. package/src/services/KnowhowClient.ts +141 -1
  34. package/src/services/LazyToolsService.ts +146 -0
  35. package/src/services/Mcp.ts +171 -4
  36. package/src/services/SessionManager.ts +287 -0
  37. package/src/services/TaskRegistry.ts +108 -0
  38. package/src/services/Tools.ts +2 -0
  39. package/src/services/index.ts +7 -0
  40. package/src/services/script-execution/ScriptExecutor.ts +7 -5
  41. package/src/types.ts +1 -0
  42. package/src/utils/InputQueueManager.ts +91 -57
  43. package/src/utils/errors.ts +0 -0
  44. package/src/utils/index.ts +11 -0
  45. package/src/worker.ts +12 -0
  46. package/tests/compressor/bigstring.test.ts +100 -0
  47. package/tests/compressor/bigstring.txt +1 -0
  48. package/tests/plugins/language/languagePlugin-content-triggers.test.ts +13 -5
  49. package/tests/plugins/language/languagePlugin-integration.test.ts +22 -7
  50. package/tests/plugins/language/languagePlugin.test.ts +11 -4
  51. package/tests/processors/ToolResponseCache.test.ts +128 -0
  52. package/tests/unit/InputQueueManager.test.ts +174 -0
  53. package/ts_build/package.json +3 -3
  54. package/ts_build/src/agents/base/base.d.ts +10 -0
  55. package/ts_build/src/agents/base/base.js +66 -34
  56. package/ts_build/src/agents/base/base.js.map +1 -1
  57. package/ts_build/src/agents/tools/execCommand.js +1 -9
  58. package/ts_build/src/agents/tools/execCommand.js.map +1 -1
  59. package/ts_build/src/agents/tools/github/index.d.ts +1 -1
  60. package/ts_build/src/agents/tools/googleSearch.d.ts +1 -0
  61. package/ts_build/src/agents/tools/googleSearch.js +1 -0
  62. package/ts_build/src/agents/tools/googleSearch.js.map +1 -1
  63. package/ts_build/src/agents/tools/index.d.ts +1 -0
  64. package/ts_build/src/agents/tools/index.js +1 -0
  65. package/ts_build/src/agents/tools/index.js.map +1 -1
  66. package/ts_build/src/agents/tools/lazy/definitions.d.ts +5 -0
  67. package/ts_build/src/agents/tools/lazy/definitions.js +58 -0
  68. package/ts_build/src/agents/tools/lazy/definitions.js.map +1 -0
  69. package/ts_build/src/agents/tools/lazy/disableTools.d.ts +9 -0
  70. package/ts_build/src/agents/tools/lazy/disableTools.js +15 -0
  71. package/ts_build/src/agents/tools/lazy/disableTools.js.map +1 -0
  72. package/ts_build/src/agents/tools/lazy/enableTools.d.ts +9 -0
  73. package/ts_build/src/agents/tools/lazy/enableTools.js +15 -0
  74. package/ts_build/src/agents/tools/lazy/enableTools.js.map +1 -0
  75. package/ts_build/src/agents/tools/lazy/index.d.ts +3 -0
  76. package/ts_build/src/agents/tools/lazy/index.js +20 -0
  77. package/ts_build/src/agents/tools/lazy/index.js.map +1 -0
  78. package/ts_build/src/agents/tools/lazy/listAvailableTools.d.ts +11 -0
  79. package/ts_build/src/agents/tools/lazy/listAvailableTools.js +15 -0
  80. package/ts_build/src/agents/tools/lazy/listAvailableTools.js.map +1 -0
  81. package/ts_build/src/agents/tools/list.js +2 -0
  82. package/ts_build/src/agents/tools/list.js.map +1 -1
  83. package/ts_build/src/agents/tools/mcp/connectMcpServer.d.ts +5 -0
  84. package/ts_build/src/agents/tools/mcp/connectMcpServer.js +31 -0
  85. package/ts_build/src/agents/tools/mcp/connectMcpServer.js.map +1 -0
  86. package/ts_build/src/agents/tools/mcp/definitions.d.ts +2 -0
  87. package/ts_build/src/agents/tools/mcp/definitions.js +62 -0
  88. package/ts_build/src/agents/tools/mcp/definitions.js.map +1 -0
  89. package/ts_build/src/agents/tools/mcp/disconnectMcpServer.d.ts +5 -0
  90. package/ts_build/src/agents/tools/mcp/disconnectMcpServer.js +31 -0
  91. package/ts_build/src/agents/tools/mcp/disconnectMcpServer.js.map +1 -0
  92. package/ts_build/src/agents/tools/mcp/index.d.ts +3 -0
  93. package/ts_build/src/agents/tools/mcp/index.js +10 -0
  94. package/ts_build/src/agents/tools/mcp/index.js.map +1 -0
  95. package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.d.ts +14 -0
  96. package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.js +23 -0
  97. package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.js.map +1 -0
  98. package/ts_build/src/agents/tools/writeFile.js +4 -1
  99. package/ts_build/src/agents/tools/writeFile.js.map +1 -1
  100. package/ts_build/src/chat/CliChatService.js +3 -1
  101. package/ts_build/src/chat/CliChatService.js.map +1 -1
  102. package/ts_build/src/chat/modules/AgentModule.d.ts +4 -3
  103. package/ts_build/src/chat/modules/AgentModule.js +71 -265
  104. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  105. package/ts_build/src/cli.d.ts +1 -1
  106. package/ts_build/src/cli.js +17 -4
  107. package/ts_build/src/cli.js.map +1 -1
  108. package/ts_build/src/plugins/GitPlugin.d.ts +1 -0
  109. package/ts_build/src/plugins/GitPlugin.js +26 -19
  110. package/ts_build/src/plugins/GitPlugin.js.map +1 -1
  111. package/ts_build/src/plugins/language.d.ts +3 -0
  112. package/ts_build/src/plugins/language.js +55 -13
  113. package/ts_build/src/plugins/language.js.map +1 -1
  114. package/ts_build/src/processors/ToolResponseCache.d.ts +7 -4
  115. package/ts_build/src/processors/ToolResponseCache.js +47 -88
  116. package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
  117. package/ts_build/src/processors/tools/grepToolResponse.d.ts +10 -0
  118. package/ts_build/src/processors/tools/grepToolResponse.js +71 -0
  119. package/ts_build/src/processors/tools/grepToolResponse.js.map +1 -0
  120. package/ts_build/src/processors/tools/index.d.ts +4 -0
  121. package/ts_build/src/processors/tools/index.js +16 -0
  122. package/ts_build/src/processors/tools/index.js.map +1 -0
  123. package/ts_build/src/processors/tools/jqToolResponse.d.ts +3 -0
  124. package/ts_build/src/processors/tools/jqToolResponse.js +115 -0
  125. package/ts_build/src/processors/tools/jqToolResponse.js.map +1 -0
  126. package/ts_build/src/processors/tools/listStoredToolResponses.d.ts +21 -0
  127. package/ts_build/src/processors/tools/listStoredToolResponses.js +51 -0
  128. package/ts_build/src/processors/tools/listStoredToolResponses.js.map +1 -0
  129. package/ts_build/src/processors/tools/tailToolResponse.d.ts +6 -0
  130. package/ts_build/src/processors/tools/tailToolResponse.js +55 -0
  131. package/ts_build/src/processors/tools/tailToolResponse.js.map +1 -0
  132. package/ts_build/src/services/AgentService.d.ts +1 -1
  133. package/ts_build/src/services/AgentSynchronization.d.ts +27 -0
  134. package/ts_build/src/services/AgentSynchronization.js +168 -0
  135. package/ts_build/src/services/AgentSynchronization.js.map +1 -0
  136. package/ts_build/src/services/DockerService.d.ts +2 -0
  137. package/ts_build/src/services/DockerService.js +21 -1
  138. package/ts_build/src/services/DockerService.js.map +1 -1
  139. package/ts_build/src/services/EventService.d.ts +5 -0
  140. package/ts_build/src/services/EventService.js +7 -2
  141. package/ts_build/src/services/EventService.js.map +1 -1
  142. package/ts_build/src/services/KnowhowClient.d.ts +41 -1
  143. package/ts_build/src/services/KnowhowClient.js +42 -0
  144. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  145. package/ts_build/src/services/LazyToolsService.d.ts +29 -0
  146. package/ts_build/src/services/LazyToolsService.js +96 -0
  147. package/ts_build/src/services/LazyToolsService.js.map +1 -0
  148. package/ts_build/src/services/Mcp.d.ts +18 -1
  149. package/ts_build/src/services/Mcp.js +119 -4
  150. package/ts_build/src/services/Mcp.js.map +1 -1
  151. package/ts_build/src/services/SessionManager.d.ts +15 -0
  152. package/ts_build/src/services/SessionManager.js +220 -0
  153. package/ts_build/src/services/SessionManager.js.map +1 -0
  154. package/ts_build/src/services/TaskRegistry.d.ts +15 -0
  155. package/ts_build/src/services/TaskRegistry.js +58 -0
  156. package/ts_build/src/services/TaskRegistry.js.map +1 -0
  157. package/ts_build/src/services/Tools.d.ts +2 -0
  158. package/ts_build/src/services/Tools.js.map +1 -1
  159. package/ts_build/src/services/index.d.ts +4 -0
  160. package/ts_build/src/services/index.js +4 -0
  161. package/ts_build/src/services/index.js.map +1 -1
  162. package/ts_build/src/services/script-execution/ScriptExecutor.js +7 -5
  163. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -1
  164. package/ts_build/src/types.d.ts +1 -0
  165. package/ts_build/src/types.js.map +1 -1
  166. package/ts_build/src/utils/InputQueueManager.d.ts +9 -2
  167. package/ts_build/src/utils/InputQueueManager.js +54 -40
  168. package/ts_build/src/utils/InputQueueManager.js.map +1 -1
  169. package/ts_build/src/utils/errors.d.ts +0 -0
  170. package/ts_build/src/utils/errors.js +1 -0
  171. package/ts_build/src/utils/errors.js.map +1 -0
  172. package/ts_build/src/utils/index.d.ts +1 -0
  173. package/ts_build/src/utils/index.js +5 -1
  174. package/ts_build/src/utils/index.js.map +1 -1
  175. package/ts_build/src/worker.js +8 -0
  176. package/ts_build/src/worker.js.map +1 -1
  177. package/ts_build/tests/compressor/bigstring.test.d.ts +1 -0
  178. package/ts_build/tests/compressor/bigstring.test.js +66 -0
  179. package/ts_build/tests/compressor/bigstring.test.js.map +1 -0
  180. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +6 -5
  181. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
  182. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +9 -7
  183. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -1
  184. package/ts_build/tests/plugins/language/languagePlugin.test.js +7 -4
  185. package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -1
  186. package/ts_build/tests/processors/ToolResponseCache.test.js +107 -0
  187. package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -1
  188. package/ts_build/tests/unit/InputQueueManager.test.d.ts +1 -0
  189. package/ts_build/tests/unit/InputQueueManager.test.js +104 -0
  190. package/ts_build/tests/unit/InputQueueManager.test.js.map +1 -0
@@ -0,0 +1,99 @@
1
+ import { Tool } from "../../clients";
2
+
3
+ export interface GrepOptions {
4
+ ignoreCase?: boolean;
5
+ invertMatch?: boolean;
6
+ contextBefore?: number;
7
+ contextAfter?: number;
8
+ maxResults?: number;
9
+ }
10
+
11
+ /**
12
+ * Grep through tool response data to find matching lines
13
+ */
14
+ export async function executeGrep(
15
+ data: string,
16
+ toolCallId: string,
17
+ pattern: string,
18
+ availableIds: string[],
19
+ options?: GrepOptions
20
+ ): Promise<string> {
21
+ if (!data) {
22
+ return `Error: No tool response found for toolCallId "${toolCallId}". Available IDs: ${availableIds.join(
23
+ ", "
24
+ )}`;
25
+ }
26
+
27
+ try {
28
+ const lines = data.split("\n");
29
+ const matchedResults: string[] = [];
30
+ const ignoreCase = options?.ignoreCase || false;
31
+ const invertMatch = options?.invertMatch || false;
32
+ const contextBefore = options?.contextBefore || 0;
33
+ const contextAfter = options?.contextAfter || 0;
34
+ const maxResults = options?.maxResults || 1000;
35
+
36
+ // Create regex from pattern
37
+ const flags = ignoreCase ? "i" : "";
38
+ const regex = new RegExp(pattern, flags);
39
+
40
+ for (let i = 0; i < lines.length && matchedResults.length < maxResults; i++) {
41
+ const line = lines[i];
42
+ const matches = regex.test(line);
43
+ const shouldInclude = invertMatch ? !matches : matches;
44
+
45
+ if (shouldInclude) {
46
+ const startIdx = Math.max(0, i - contextBefore);
47
+ const endIdx = Math.min(lines.length - 1, i + contextAfter);
48
+
49
+ const contextLines = [];
50
+ for (let j = startIdx; j <= endIdx; j++) {
51
+ const prefix = j === i ? "> " : " ";
52
+ contextLines.push(`${prefix}${j + 1}: ${lines[j]}`);
53
+ }
54
+
55
+ matchedResults.push(contextLines.join("\n"));
56
+
57
+ // Skip ahead to avoid overlapping context
58
+ i += contextAfter;
59
+ }
60
+ }
61
+
62
+ if (matchedResults.length === 0) {
63
+ return `No matches found for pattern "${pattern}" in toolCallId "${toolCallId}"`;
64
+ }
65
+
66
+ return matchedResults.join("\n---\n");
67
+ } catch (error: any) {
68
+ return `Grep Error: ${error.message}`;
69
+ }
70
+ }
71
+
72
+ export const grepToolResponseDefinition: Tool = {
73
+ type: "function",
74
+ function: {
75
+ name: "grepToolResponse",
76
+ description:
77
+ "Search through a stored tool response using grep-like pattern matching. Useful when a tool response is too large and you need to find specific lines or content without re-running the tool. Returns matching lines with optional context.",
78
+ parameters: {
79
+ type: "object",
80
+ positional: true,
81
+ properties: {
82
+ toolCallId: {
83
+ type: "string",
84
+ description: "The toolCallId of the stored tool response",
85
+ },
86
+ pattern: {
87
+ type: "string",
88
+ description: "Regular expression pattern to search for in the tool response",
89
+ },
90
+ options: {
91
+ type: "object",
92
+ description: "Optional grep settings: ignoreCase (boolean), invertMatch (boolean), contextBefore (number), contextAfter (number), maxResults (number, default: 1000)",
93
+ properties: {},
94
+ },
95
+ },
96
+ required: ["toolCallId", "pattern"],
97
+ },
98
+ },
99
+ };
@@ -0,0 +1,21 @@
1
+ export {
2
+ jqToolResponseDefinition,
3
+ executeJqQuery,
4
+ } from "./jqToolResponse";
5
+
6
+ export {
7
+ grepToolResponseDefinition,
8
+ executeGrep,
9
+ GrepOptions,
10
+ } from "./grepToolResponse";
11
+
12
+ export {
13
+ tailToolResponseDefinition,
14
+ executeTail,
15
+ TailOptions,
16
+ } from "./tailToolResponse";
17
+
18
+ export {
19
+ listStoredToolResponsesDefinition,
20
+ executeListStoredToolResponses,
21
+ } from "./listStoredToolResponses";
@@ -0,0 +1,124 @@
1
+ import { Tool } from "../../clients";
2
+ import * as jq from "node-jq";
3
+
4
+ /**
5
+ * Attempts to parse content as JSON and returns parsed object if successful
6
+ */
7
+ function tryParseJson(content: string): any | null {
8
+ try {
9
+ return JSON.parse(content);
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Recursively searches for JSON strings within an object and parses them
17
+ */
18
+ function parseNestedJsonStrings(obj: any): any {
19
+ if (typeof obj === "string") {
20
+ const parsed = tryParseJson(obj);
21
+ if (parsed) {
22
+ return parseNestedJsonStrings(parsed);
23
+ }
24
+ return obj;
25
+ }
26
+
27
+ if (Array.isArray(obj)) {
28
+ return obj.map((item) => parseNestedJsonStrings(item));
29
+ }
30
+
31
+ if (obj && typeof obj === "object") {
32
+ const result: any = {};
33
+ for (const [key, value] of Object.entries(obj)) {
34
+ result[key] = parseNestedJsonStrings(value);
35
+ }
36
+ return result;
37
+ }
38
+
39
+ return obj;
40
+ }
41
+
42
+ /**
43
+ * Retrieves and processes tool response data with JQ query
44
+ */
45
+ export async function executeJqQuery(
46
+ data: string,
47
+ toolCallId: string,
48
+ jqQuery: string,
49
+ availableIds: string[]
50
+ ): Promise<string> {
51
+ if (!data) {
52
+ return `Error: No tool response found for toolCallId "${toolCallId}". Available IDs: ${availableIds.join(
53
+ ", "
54
+ )}`;
55
+ }
56
+
57
+ try {
58
+ // First parse the stored string as JSON, then handle nested JSON strings
59
+ const jsonData = tryParseJson(data);
60
+ if (!jsonData) {
61
+ return `Error: Tool response data is not valid JSON for toolCallId "${toolCallId}"`;
62
+ }
63
+ const parsedData = parseNestedJsonStrings(jsonData);
64
+
65
+ // Execute JQ query
66
+ const result = await jq.run(jqQuery, parsedData, { input: "json" });
67
+
68
+ // Handle the result based on its type
69
+ if (typeof result === "string") {
70
+ return result;
71
+ } else if (typeof result === "number" || typeof result === "boolean") {
72
+ return String(result);
73
+ } else if (result === null) {
74
+ return "null";
75
+ } else {
76
+ return JSON.stringify(result);
77
+ }
78
+ } catch (error: any) {
79
+ // If JQ fails, try to provide helpful error message
80
+ let errorMessage = `JQ Query Error: ${error.message}`;
81
+
82
+ // Try to parse as JSON to see if it's valid
83
+ const jsonObj = tryParseJson(data);
84
+ if (!jsonObj) {
85
+ errorMessage += `\nNote: The tool response data is not valid JSON. Raw data preview:\n${data.substring(
86
+ 0,
87
+ 300
88
+ )}...`;
89
+ } else {
90
+ errorMessage += `\nData structure preview:\n${JSON.stringify(
91
+ jsonObj,
92
+ null,
93
+ 2
94
+ ).substring(0, 500)}...`;
95
+ }
96
+
97
+ return errorMessage;
98
+ }
99
+ }
100
+
101
+ export const jqToolResponseDefinition: Tool = {
102
+ type: "function",
103
+ function: {
104
+ name: "jqToolResponse",
105
+ description:
106
+ "Execute a JQ query on a stored tool response to extract specific data. Use this when you need to extract specific information from any tool response that has been stored. Many MCP tool responses store data in nested structures like .content[0].text where the actual data array is located.",
107
+ parameters: {
108
+ type: "object",
109
+ positional: true,
110
+ properties: {
111
+ toolCallId: {
112
+ type: "string",
113
+ description: "The toolCallId of the stored tool response",
114
+ },
115
+ jqQuery: {
116
+ type: "string",
117
+ description:
118
+ "The JQ query to execute on the tool response data. Examples: '.content[0].text | map(.title)' (extract titles from MCP array), '.content[0].text | map(select(.createdAt > \"2025-01-01\"))' (filter MCP items by date) ",
119
+ },
120
+ },
121
+ required: ["toolCallId", "jqQuery"],
122
+ },
123
+ },
124
+ };
@@ -0,0 +1,83 @@
1
+ import { Tool } from "../../clients";
2
+
3
+ export interface ToolResponseInfo {
4
+ toolCallId: string;
5
+ toolName: string;
6
+ size: number;
7
+ storedAt: number;
8
+ preview: string;
9
+ }
10
+
11
+ /**
12
+ * List all stored tool responses with metadata
13
+ */
14
+ export async function executeListStoredToolResponses(
15
+ storage: { [toolCallId: string]: string },
16
+ metadataStorage: {
17
+ [toolCallId: string]: {
18
+ toolCallId: string;
19
+ originalLength: number;
20
+ storedAt: number;
21
+ toolName?: string;
22
+ };
23
+ },
24
+ toolNameMap: { [toolCallId: string]: string }
25
+ ): Promise<string> {
26
+ const toolCallIds = Object.keys(storage);
27
+
28
+ if (toolCallIds.length === 0) {
29
+ return "No tool responses have been stored yet.";
30
+ }
31
+
32
+ const responses: ToolResponseInfo[] = toolCallIds.map((toolCallId) => {
33
+ const data = storage[toolCallId];
34
+ const metadata = metadataStorage[toolCallId];
35
+ const toolName = toolNameMap[toolCallId] || "unknown";
36
+
37
+ // Create a preview (first 100 characters)
38
+ const preview =
39
+ data.length > 100 ? data.substring(0, 100) + "..." : data;
40
+
41
+ return {
42
+ toolCallId,
43
+ toolName,
44
+ size: metadata?.originalLength || data.length,
45
+ storedAt: metadata?.storedAt || 0,
46
+ preview,
47
+ };
48
+ });
49
+
50
+ // Sort by most recent first
51
+ responses.sort((a, b) => b.storedAt - a.storedAt);
52
+
53
+ // Format the output in a readable way
54
+ const output = responses
55
+ .map((resp) => {
56
+ const date = new Date(resp.storedAt).toISOString();
57
+ return `
58
+ Tool Call ID: ${resp.toolCallId}
59
+ Tool Name: ${resp.toolName}
60
+ Size: ${resp.size} characters
61
+ Stored At: ${date}
62
+ Preview: ${resp.preview}
63
+ ---`;
64
+ })
65
+ .join("\n");
66
+
67
+ return `Found ${responses.length} stored tool response(s):\n${output}`;
68
+ }
69
+
70
+ export const listStoredToolResponsesDefinition: Tool = {
71
+ type: "function",
72
+ function: {
73
+ name: "listStoredToolResponses",
74
+ description:
75
+ "List all stored tool responses with metadata including tool call ID, tool name, size, timestamp, and a preview of the content. Use this to discover which tool responses are available for querying with jqToolResponse or grepToolResponse.",
76
+ parameters: {
77
+ type: "object",
78
+ positional: false,
79
+ properties: {},
80
+ required: [],
81
+ },
82
+ },
83
+ };
@@ -0,0 +1,75 @@
1
+ import { Tool } from "../../clients";
2
+
3
+ export interface TailOptions {
4
+ lines?: number;
5
+ }
6
+
7
+ /**
8
+ * Get the last n lines from a tool response
9
+ */
10
+ export async function executeTail(
11
+ data: string,
12
+ toolCallId: string,
13
+ availableIds: string[],
14
+ options?: TailOptions
15
+ ): Promise<string> {
16
+ if (data === null || data === undefined) {
17
+ return `Error: No tool response found for toolCallId "${toolCallId}". Available IDs: ${availableIds.join(
18
+ ", "
19
+ )}`;
20
+ }
21
+
22
+ try {
23
+ const lines = data.split("\n");
24
+ const numLines = options?.lines ?? 10;
25
+
26
+ // Handle edge case: 0 lines requested
27
+ if (numLines <= 0) {
28
+ return "";
29
+ }
30
+
31
+ // Get the last n lines
32
+ const startIdx = Math.max(0, lines.length - numLines);
33
+ const tailLines = lines.slice(startIdx);
34
+
35
+ // Format with line numbers
36
+ const formatted = tailLines.map((line, idx) => {
37
+ const lineNum = startIdx + idx + 1;
38
+ return `${lineNum}: ${line}`;
39
+ });
40
+
41
+ return formatted.join("\n");
42
+ } catch (error: any) {
43
+ return `Tail Error: ${error.message}`;
44
+ }
45
+ }
46
+
47
+ export const tailToolResponseDefinition: Tool = {
48
+ type: "function",
49
+ function: {
50
+ name: "tailToolResponse",
51
+ description:
52
+ "Get the last n lines from a stored tool response. Similar to the Unix 'tail' command, useful when a tool response is too large and you only need to see the end. Returns the last n lines with line numbers.",
53
+ parameters: {
54
+ type: "object",
55
+ positional: true,
56
+ properties: {
57
+ toolCallId: {
58
+ type: "string",
59
+ description: "The toolCallId of the stored tool response",
60
+ },
61
+ options: {
62
+ type: "object",
63
+ description: "Optional tail settings: lines (number, default: 10) - number of lines to return from the end",
64
+ properties: {
65
+ lines: {
66
+ type: "number",
67
+ description: "Number of lines to return from the end (default: 10)",
68
+ },
69
+ },
70
+ },
71
+ },
72
+ required: ["toolCallId"],
73
+ },
74
+ },
75
+ };
@@ -3,7 +3,7 @@ import { IAgent } from "../agents/interface";
3
3
  import { EventService } from "./EventService";
4
4
  import { ToolsService } from "./Tools";
5
5
  import { ConfigAgent } from "../agents/configurable/ConfigAgent";
6
- import { AgentContext } from "src/agents/base/base";
6
+ import { AgentContext } from "../agents/base/base";
7
7
 
8
8
  export class AgentService {
9
9
  private agents: Map<string, IAgent> = new Map();
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Agent Synchronization Service - Handles synchronization with Knowhow API
3
+ * including task creation, updates, and message processing
4
+ */
5
+ import {
6
+ KnowhowSimpleClient,
7
+ KNOWHOW_API_URL,
8
+ TaskDetailsResponse,
9
+ PendingMessage,
10
+ } from "./KnowhowClient";
11
+ import { BaseAgent } from "../agents/base/base";
12
+ import { wait } from "../utils";
13
+
14
+ export interface SyncOptions {
15
+ messageId?: string;
16
+ existingKnowhowTaskId?: string;
17
+ prompt: string;
18
+ }
19
+
20
+ export interface TaskSyncState {
21
+ knowhowTaskId?: string;
22
+ client: KnowhowSimpleClient;
23
+ messageId?: string;
24
+ }
25
+
26
+ /**
27
+ * AgentSynchronization handles all communication with the Knowhow API
28
+ * for task creation, updates, status polling, and message synchronization
29
+ */
30
+ export class AgentSynchronization {
31
+ private client: KnowhowSimpleClient;
32
+ private baseUrl: string;
33
+ private knowhowTaskId: string | undefined;
34
+ private eventHandlersSetup: boolean = false;
35
+
36
+ constructor(baseUrl: string = KNOWHOW_API_URL) {
37
+ this.baseUrl = baseUrl;
38
+ this.client = new KnowhowSimpleClient(baseUrl);
39
+ }
40
+
41
+ /**
42
+ * Create a new chat task in Knowhow
43
+ */
44
+ async createChatTask(options: SyncOptions): Promise<string | undefined> {
45
+ if (!options.messageId || !this.baseUrl) {
46
+ return undefined;
47
+ }
48
+
49
+ try {
50
+ console.log(
51
+ `Base URL for Knowhow API: ${this.baseUrl}, Message ID: ${options.messageId}`
52
+ );
53
+ const response = await this.client.createChatTask({
54
+ messageId: options.messageId,
55
+ prompt: options.prompt,
56
+ });
57
+ const knowhowTaskId = response.data.id;
58
+ console.log(`✅ Created Knowhow chat task: ${knowhowTaskId}`);
59
+ return knowhowTaskId;
60
+ } catch (error) {
61
+ console.error(`❌ Failed to create Knowhow chat task:`, error);
62
+ return undefined;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Update a chat task with current agent state
68
+ */
69
+ async updateChatTask(
70
+ knowhowTaskId: string,
71
+ agent: BaseAgent,
72
+ inProgress: boolean,
73
+ result?: string
74
+ ): Promise<void> {
75
+ if (!knowhowTaskId || !this.baseUrl) {
76
+ return;
77
+ }
78
+
79
+ try {
80
+ await this.client.updateChatTask(knowhowTaskId, {
81
+ threads: agent.getThreads(),
82
+ totalCostUsd: agent.getTotalCostUsd(),
83
+ inProgress,
84
+ ...(result ? { result } : {}),
85
+ });
86
+ console.log(`✅ Updated Knowhow chat task: ${knowhowTaskId}`);
87
+ } catch (error) {
88
+ console.error(`❌ Failed to update Knowhow chat task:`, error);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Check for pending messages and process them, also handle pause/kill status
94
+ */
95
+ async checkAndProcessPendingMessages(
96
+ agent: BaseAgent,
97
+ knowhowTaskId: string
98
+ ): Promise<void> {
99
+ if (!knowhowTaskId || !this.baseUrl) {
100
+ return;
101
+ }
102
+
103
+ try {
104
+ // Fetch task details to check status
105
+ const taskDetailsResponse = await this.client.getTaskDetails(
106
+ knowhowTaskId
107
+ );
108
+ const taskDetails: TaskDetailsResponse = taskDetailsResponse.data;
109
+
110
+ // Handle killed status
111
+ if (taskDetails.status === "killed") {
112
+ console.log(`🛑 Agent task ${knowhowTaskId} was killed via API`);
113
+ await agent.kill();
114
+ return;
115
+ }
116
+
117
+ // Handle paused status
118
+ if (taskDetails.status === "paused") {
119
+ console.log(
120
+ `⏸️ Agent task ${knowhowTaskId} is paused, waiting for resume...`
121
+ );
122
+ await agent.pause();
123
+ await this.waitForResume(agent, knowhowTaskId);
124
+ return; // After resume, we'll process messages on the next threadUpdate
125
+ }
126
+
127
+ // Fetch pending messages
128
+ const pendingResponse = await this.client.getPendingMessages(
129
+ knowhowTaskId
130
+ );
131
+ const pendingMessages: PendingMessage[] = pendingResponse.data || [];
132
+
133
+ if (pendingMessages.length === 0) {
134
+ return; // No pending messages to process
135
+ }
136
+
137
+ console.log(
138
+ `📬 Processing ${pendingMessages.length} pending message(s) for task ${knowhowTaskId}`
139
+ );
140
+
141
+ // Inject pending messages into the agent
142
+ const messageIds: string[] = [];
143
+ for (const msg of pendingMessages) {
144
+ agent.addPendingUserMessage({
145
+ role: msg.role as "user" | "assistant",
146
+ content: msg.message,
147
+ });
148
+ messageIds.push(msg.id);
149
+ }
150
+
151
+ // Mark messages as processed
152
+ await this.client.markMessagesAsProcessed(knowhowTaskId, messageIds);
153
+ console.log(`✅ Marked ${messageIds.length} message(s) as processed`);
154
+ } catch (error) {
155
+ console.error(`❌ Error checking/processing pending messages:`, error);
156
+ // Continue execution even if synchronization fails
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Wait for the agent to be resumed or killed via API
162
+ * Polls the API every 2 seconds, with a 1 hour timeout
163
+ */
164
+ private async waitForResume(
165
+ agent: BaseAgent,
166
+ knowhowTaskId: string
167
+ ): Promise<void> {
168
+ const POLL_INTERVAL_MS = 2000;
169
+ const MAX_WAIT_MS = 60 * 60 * 1000; // 1 hour
170
+ const startTime = Date.now();
171
+
172
+ while (Date.now() - startTime < MAX_WAIT_MS) {
173
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
174
+
175
+ try {
176
+ const taskDetailsResponse = await this.client.getTaskDetails(
177
+ knowhowTaskId
178
+ );
179
+ const taskDetails: TaskDetailsResponse = taskDetailsResponse.data;
180
+
181
+ if (taskDetails.status === "killed") {
182
+ console.log(`🛑 Agent task ${knowhowTaskId} was killed while paused`);
183
+ await agent.kill();
184
+ return;
185
+ }
186
+
187
+ if (
188
+ taskDetails.status === "running" ||
189
+ taskDetails.status === "completed"
190
+ ) {
191
+ console.log(`▶️ Agent task ${knowhowTaskId} resumed`);
192
+ await agent.unpause();
193
+ return;
194
+ }
195
+ // Still paused, continue waiting
196
+ } catch (error) {
197
+ console.error(`❌ Error polling task status:`, error);
198
+ // Continue polling even on errors
199
+ }
200
+ }
201
+
202
+ console.warn(`⚠️ Timeout waiting for resume on task ${knowhowTaskId}`);
203
+ }
204
+
205
+ /**
206
+ * Set up event-based synchronization for an agent task
207
+ * This sets up event listeners that automatically sync on threadUpdate and completion
208
+ */
209
+ async setupAgentSync(
210
+ agent: BaseAgent,
211
+ knowhowTaskId?: string
212
+ ): Promise<void> {
213
+ if (!knowhowTaskId || !this.baseUrl) {
214
+ return;
215
+ }
216
+
217
+ this.knowhowTaskId = knowhowTaskId;
218
+
219
+ // Set up event listeners for automatic synchronization
220
+ if (!this.eventHandlersSetup) {
221
+ this.setupEventHandlers(agent);
222
+ this.eventHandlersSetup = true;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Set up event handlers for automatic synchronization
228
+ */
229
+ private setupEventHandlers(agent: BaseAgent): void {
230
+ // Listen to thread updates to sync state and check for pending messages
231
+ agent.agentEvents.on(agent.eventTypes.threadUpdate, async () => {
232
+ if (!this.knowhowTaskId || !this.baseUrl) {
233
+ return;
234
+ }
235
+
236
+ try {
237
+ // Update task with current state
238
+ await this.updateChatTask(this.knowhowTaskId, agent, true);
239
+
240
+ // Check for pending messages, pause, or kill status
241
+ await this.checkAndProcessPendingMessages(agent, this.knowhowTaskId);
242
+ } catch (error) {
243
+ console.error(`❌ Error during threadUpdate sync:`, error);
244
+ // Continue execution even if synchronization fails
245
+ }
246
+ });
247
+
248
+ // Listen to completion event to finalize task
249
+ agent.agentEvents.on(agent.eventTypes.done, async (result: string) => {
250
+ if (!this.knowhowTaskId || !this.baseUrl) {
251
+ return;
252
+ }
253
+
254
+ try {
255
+ await wait(200);
256
+ console.log(
257
+ `Updating Knowhow chat task on completion..., ${this.knowhowTaskId}`
258
+ );
259
+ await this.updateChatTask(this.knowhowTaskId, agent, false, result);
260
+ console.log(`✅ Completed Knowhow chat task: ${this.knowhowTaskId}`);
261
+ } catch (error) {
262
+ console.error(`❌ Error finalizing task:`, error);
263
+ }
264
+ });
265
+ }
266
+
267
+ /**
268
+ * Reset synchronization state (useful for reusing the service)
269
+ */
270
+ reset(): void {
271
+ this.knowhowTaskId = undefined;
272
+ this.eventHandlersSetup = false;
273
+ }
274
+
275
+ /**
276
+ * Finalize an agent task on completion
277
+ */
278
+ async finalizeTask(
279
+ agent: BaseAgent,
280
+ knowhowTaskId: string | undefined,
281
+ result: string
282
+ ): Promise<void> {
283
+ if (knowhowTaskId && this.baseUrl) {
284
+ console.log(
285
+ `Updating Knowhow chat task on completion..., ${knowhowTaskId}`
286
+ );
287
+ await this.updateChatTask(knowhowTaskId, agent, false, result);
288
+ console.log(`✅ Completed Knowhow chat task: ${knowhowTaskId}`);
289
+ }
290
+ }
291
+ }