@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.
- package/package.json +3 -3
- package/src/agents/base/base.ts +87 -43
- package/src/agents/tools/execCommand.ts +17 -14
- package/src/agents/tools/googleSearch.ts +1 -0
- package/src/agents/tools/index.ts +1 -0
- package/src/agents/tools/lazy/definitions.ts +63 -0
- package/src/agents/tools/lazy/disableTools.ts +16 -0
- package/src/agents/tools/lazy/enableTools.ts +16 -0
- package/src/agents/tools/lazy/index.ts +3 -0
- package/src/agents/tools/lazy/listAvailableTools.ts +14 -0
- package/src/agents/tools/list.ts +2 -0
- package/src/agents/tools/mcp/connectMcpServer.ts +40 -0
- package/src/agents/tools/mcp/definitions.ts +67 -0
- package/src/agents/tools/mcp/disconnectMcpServer.ts +40 -0
- package/src/agents/tools/mcp/index.ts +3 -0
- package/src/agents/tools/mcp/listAvailableMcpServers.ts +28 -0
- package/src/agents/tools/writeFile.ts +4 -1
- package/src/chat/CliChatService.ts +8 -3
- package/src/chat/modules/AgentModule.ts +74 -296
- package/src/cli.ts +33 -10
- package/src/plugins/GitPlugin.ts +30 -24
- package/src/plugins/language.ts +95 -18
- package/src/processors/ToolResponseCache.ts +98 -79
- package/src/processors/tools/grepToolResponse.ts +99 -0
- package/src/processors/tools/index.ts +21 -0
- package/src/processors/tools/jqToolResponse.ts +124 -0
- package/src/processors/tools/listStoredToolResponses.ts +83 -0
- package/src/processors/tools/tailToolResponse.ts +75 -0
- package/src/services/AgentService.ts +1 -1
- package/src/services/AgentSynchronization.ts +291 -0
- package/src/services/DockerService.ts +37 -1
- package/src/services/EventService.ts +8 -2
- package/src/services/KnowhowClient.ts +141 -1
- package/src/services/LazyToolsService.ts +146 -0
- package/src/services/Mcp.ts +171 -4
- package/src/services/SessionManager.ts +287 -0
- package/src/services/TaskRegistry.ts +108 -0
- package/src/services/Tools.ts +2 -0
- package/src/services/index.ts +7 -0
- package/src/services/script-execution/ScriptExecutor.ts +7 -5
- package/src/types.ts +1 -0
- package/src/utils/InputQueueManager.ts +91 -57
- package/src/utils/errors.ts +0 -0
- package/src/utils/index.ts +11 -0
- package/src/worker.ts +12 -0
- package/tests/compressor/bigstring.test.ts +100 -0
- package/tests/compressor/bigstring.txt +1 -0
- package/tests/plugins/language/languagePlugin-content-triggers.test.ts +13 -5
- package/tests/plugins/language/languagePlugin-integration.test.ts +22 -7
- package/tests/plugins/language/languagePlugin.test.ts +11 -4
- package/tests/processors/ToolResponseCache.test.ts +128 -0
- package/tests/unit/InputQueueManager.test.ts +174 -0
- package/ts_build/package.json +3 -3
- package/ts_build/src/agents/base/base.d.ts +10 -0
- package/ts_build/src/agents/base/base.js +66 -34
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/execCommand.js +1 -9
- package/ts_build/src/agents/tools/execCommand.js.map +1 -1
- package/ts_build/src/agents/tools/github/index.d.ts +1 -1
- package/ts_build/src/agents/tools/googleSearch.d.ts +1 -0
- package/ts_build/src/agents/tools/googleSearch.js +1 -0
- package/ts_build/src/agents/tools/googleSearch.js.map +1 -1
- package/ts_build/src/agents/tools/index.d.ts +1 -0
- package/ts_build/src/agents/tools/index.js +1 -0
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/lazy/definitions.d.ts +5 -0
- package/ts_build/src/agents/tools/lazy/definitions.js +58 -0
- package/ts_build/src/agents/tools/lazy/definitions.js.map +1 -0
- package/ts_build/src/agents/tools/lazy/disableTools.d.ts +9 -0
- package/ts_build/src/agents/tools/lazy/disableTools.js +15 -0
- package/ts_build/src/agents/tools/lazy/disableTools.js.map +1 -0
- package/ts_build/src/agents/tools/lazy/enableTools.d.ts +9 -0
- package/ts_build/src/agents/tools/lazy/enableTools.js +15 -0
- package/ts_build/src/agents/tools/lazy/enableTools.js.map +1 -0
- package/ts_build/src/agents/tools/lazy/index.d.ts +3 -0
- package/ts_build/src/agents/tools/lazy/index.js +20 -0
- package/ts_build/src/agents/tools/lazy/index.js.map +1 -0
- package/ts_build/src/agents/tools/lazy/listAvailableTools.d.ts +11 -0
- package/ts_build/src/agents/tools/lazy/listAvailableTools.js +15 -0
- package/ts_build/src/agents/tools/lazy/listAvailableTools.js.map +1 -0
- package/ts_build/src/agents/tools/list.js +2 -0
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/mcp/connectMcpServer.d.ts +5 -0
- package/ts_build/src/agents/tools/mcp/connectMcpServer.js +31 -0
- package/ts_build/src/agents/tools/mcp/connectMcpServer.js.map +1 -0
- package/ts_build/src/agents/tools/mcp/definitions.d.ts +2 -0
- package/ts_build/src/agents/tools/mcp/definitions.js +62 -0
- package/ts_build/src/agents/tools/mcp/definitions.js.map +1 -0
- package/ts_build/src/agents/tools/mcp/disconnectMcpServer.d.ts +5 -0
- package/ts_build/src/agents/tools/mcp/disconnectMcpServer.js +31 -0
- package/ts_build/src/agents/tools/mcp/disconnectMcpServer.js.map +1 -0
- package/ts_build/src/agents/tools/mcp/index.d.ts +3 -0
- package/ts_build/src/agents/tools/mcp/index.js +10 -0
- package/ts_build/src/agents/tools/mcp/index.js.map +1 -0
- package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.d.ts +14 -0
- package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.js +23 -0
- package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.js.map +1 -0
- package/ts_build/src/agents/tools/writeFile.js +4 -1
- package/ts_build/src/agents/tools/writeFile.js.map +1 -1
- package/ts_build/src/chat/CliChatService.js +3 -1
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +4 -3
- package/ts_build/src/chat/modules/AgentModule.js +71 -265
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/cli.d.ts +1 -1
- package/ts_build/src/cli.js +17 -4
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/plugins/GitPlugin.d.ts +1 -0
- package/ts_build/src/plugins/GitPlugin.js +26 -19
- package/ts_build/src/plugins/GitPlugin.js.map +1 -1
- package/ts_build/src/plugins/language.d.ts +3 -0
- package/ts_build/src/plugins/language.js +55 -13
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/processors/ToolResponseCache.d.ts +7 -4
- package/ts_build/src/processors/ToolResponseCache.js +47 -88
- package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
- package/ts_build/src/processors/tools/grepToolResponse.d.ts +10 -0
- package/ts_build/src/processors/tools/grepToolResponse.js +71 -0
- package/ts_build/src/processors/tools/grepToolResponse.js.map +1 -0
- package/ts_build/src/processors/tools/index.d.ts +4 -0
- package/ts_build/src/processors/tools/index.js +16 -0
- package/ts_build/src/processors/tools/index.js.map +1 -0
- package/ts_build/src/processors/tools/jqToolResponse.d.ts +3 -0
- package/ts_build/src/processors/tools/jqToolResponse.js +115 -0
- package/ts_build/src/processors/tools/jqToolResponse.js.map +1 -0
- package/ts_build/src/processors/tools/listStoredToolResponses.d.ts +21 -0
- package/ts_build/src/processors/tools/listStoredToolResponses.js +51 -0
- package/ts_build/src/processors/tools/listStoredToolResponses.js.map +1 -0
- package/ts_build/src/processors/tools/tailToolResponse.d.ts +6 -0
- package/ts_build/src/processors/tools/tailToolResponse.js +55 -0
- package/ts_build/src/processors/tools/tailToolResponse.js.map +1 -0
- package/ts_build/src/services/AgentService.d.ts +1 -1
- package/ts_build/src/services/AgentSynchronization.d.ts +27 -0
- package/ts_build/src/services/AgentSynchronization.js +168 -0
- package/ts_build/src/services/AgentSynchronization.js.map +1 -0
- package/ts_build/src/services/DockerService.d.ts +2 -0
- package/ts_build/src/services/DockerService.js +21 -1
- package/ts_build/src/services/DockerService.js.map +1 -1
- package/ts_build/src/services/EventService.d.ts +5 -0
- package/ts_build/src/services/EventService.js +7 -2
- package/ts_build/src/services/EventService.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.d.ts +41 -1
- package/ts_build/src/services/KnowhowClient.js +42 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/LazyToolsService.d.ts +29 -0
- package/ts_build/src/services/LazyToolsService.js +96 -0
- package/ts_build/src/services/LazyToolsService.js.map +1 -0
- package/ts_build/src/services/Mcp.d.ts +18 -1
- package/ts_build/src/services/Mcp.js +119 -4
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/services/SessionManager.d.ts +15 -0
- package/ts_build/src/services/SessionManager.js +220 -0
- package/ts_build/src/services/SessionManager.js.map +1 -0
- package/ts_build/src/services/TaskRegistry.d.ts +15 -0
- package/ts_build/src/services/TaskRegistry.js +58 -0
- package/ts_build/src/services/TaskRegistry.js.map +1 -0
- package/ts_build/src/services/Tools.d.ts +2 -0
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/index.d.ts +4 -0
- package/ts_build/src/services/index.js +4 -0
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/script-execution/ScriptExecutor.js +7 -5
- package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -1
- package/ts_build/src/types.d.ts +1 -0
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/utils/InputQueueManager.d.ts +9 -2
- package/ts_build/src/utils/InputQueueManager.js +54 -40
- package/ts_build/src/utils/InputQueueManager.js.map +1 -1
- package/ts_build/src/utils/errors.d.ts +0 -0
- package/ts_build/src/utils/errors.js +1 -0
- package/ts_build/src/utils/errors.js.map +1 -0
- package/ts_build/src/utils/index.d.ts +1 -0
- package/ts_build/src/utils/index.js +5 -1
- package/ts_build/src/utils/index.js.map +1 -1
- package/ts_build/src/worker.js +8 -0
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/tests/compressor/bigstring.test.d.ts +1 -0
- package/ts_build/tests/compressor/bigstring.test.js +66 -0
- package/ts_build/tests/compressor/bigstring.test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +6 -5
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +9 -7
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -1
- package/ts_build/tests/plugins/language/languagePlugin.test.js +7 -4
- package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -1
- package/ts_build/tests/processors/ToolResponseCache.test.js +107 -0
- package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -1
- package/ts_build/tests/unit/InputQueueManager.test.d.ts +1 -0
- package/ts_build/tests/unit/InputQueueManager.test.js +104 -0
- 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 "
|
|
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
|
+
}
|