@tyvm/knowhow 0.0.145 → 0.0.146
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 +1 -1
- package/src/chat/CliChatService.ts +16 -0
- package/src/chat/modules/AgentModule.ts +30 -23
- package/src/chat/modules/SessionsModule.ts +7 -5
- package/src/chat/types.ts +3 -2
- package/src/clients/anthropic.ts +24 -0
- package/src/clients/fireworks.ts +3 -0
- package/src/clients/gemini.ts +8 -0
- package/src/clients/openai.ts +7 -4
- package/src/clients/types.ts +30 -7
- package/src/clients/xai.ts +23 -4
- package/src/commands/agent.ts +10 -1
- package/src/commands/services.ts +49 -3
- package/src/plugins/plugins.ts +16 -0
- package/src/processors/TokenCompressor.ts +40 -8
- package/src/processors/ToolResponseCache.ts +17 -8
- package/src/processors/tools/boundedToolResponse.ts +49 -0
- package/src/processors/tools/grepToolResponse.ts +164 -38
- package/src/processors/tools/index.ts +2 -0
- package/src/processors/tools/jqToolResponse.ts +40 -10
- package/src/processors/tools/listStoredToolResponses.ts +51 -10
- package/src/processors/tools/tailToolResponse.ts +85 -17
- package/src/services/LazyToolsService.ts +7 -0
- package/src/services/Mcp.ts +9 -2
- package/src/services/RuntimeReloadService.ts +31 -0
- package/src/services/Tools.ts +11 -0
- package/src/services/TracingService.ts +116 -0
- package/src/services/index.ts +8 -0
- package/src/services/modules/index.ts +45 -2
- package/src/services/modules/types.ts +8 -0
- package/src/util/Trace.ts +236 -0
- package/tests/clients/fireworks.test.ts +65 -0
- package/tests/clients/reasoningEffort.test.ts +79 -0
- package/tests/compressor/toolResponseCache.test.ts +2 -1
- package/tests/manual/clients/completions.test.ts +27 -10
- package/tests/processors/TokenCompressor.test.ts +17 -3
- package/tests/processors/tools/boundedRetrievalTools.test.ts +68 -0
- package/tests/processors/tools/grepToolResponse.test.ts +88 -1
- package/tests/unit/plugins/pluginLoading.test.ts +31 -0
- package/tests/unit/services/RuntimeReloadService.test.ts +29 -0
- package/ts_build/package.json +1 -1
- package/ts_build/src/chat/CliChatService.js +15 -0
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +13 -19
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/SessionsModule.js +1 -6
- package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +3 -2
- package/ts_build/src/clients/anthropic.d.ts +1 -0
- package/ts_build/src/clients/anthropic.js +12 -0
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/fireworks.js +1 -0
- package/ts_build/src/clients/fireworks.js.map +1 -1
- package/ts_build/src/clients/gemini.js +8 -0
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/openai.js +4 -2
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +4 -1
- package/ts_build/src/clients/types.js +14 -0
- package/ts_build/src/clients/types.js.map +1 -1
- package/ts_build/src/clients/xai.d.ts +1 -0
- package/ts_build/src/clients/xai.js +23 -4
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/commands/agent.js +6 -1
- package/ts_build/src/commands/agent.js.map +1 -1
- package/ts_build/src/commands/services.d.ts +2 -0
- package/ts_build/src/commands/services.js +45 -3
- package/ts_build/src/commands/services.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +1 -0
- package/ts_build/src/plugins/plugins.js +14 -0
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/processors/TokenCompressor.js +15 -6
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/processors/ToolResponseCache.d.ts +3 -3
- package/ts_build/src/processors/ToolResponseCache.js +8 -8
- package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
- package/ts_build/src/processors/tools/boundedToolResponse.d.ts +7 -0
- package/ts_build/src/processors/tools/boundedToolResponse.js +27 -0
- package/ts_build/src/processors/tools/boundedToolResponse.js.map +1 -0
- package/ts_build/src/processors/tools/grepToolResponse.d.ts +4 -0
- package/ts_build/src/processors/tools/grepToolResponse.js +101 -25
- package/ts_build/src/processors/tools/grepToolResponse.js.map +1 -1
- package/ts_build/src/processors/tools/index.d.ts +2 -2
- package/ts_build/src/processors/tools/index.js.map +1 -1
- package/ts_build/src/processors/tools/jqToolResponse.d.ts +5 -1
- package/ts_build/src/processors/tools/jqToolResponse.js +26 -9
- package/ts_build/src/processors/tools/jqToolResponse.js.map +1 -1
- package/ts_build/src/processors/tools/listStoredToolResponses.d.ts +7 -1
- package/ts_build/src/processors/tools/listStoredToolResponses.js +39 -9
- package/ts_build/src/processors/tools/listStoredToolResponses.js.map +1 -1
- package/ts_build/src/processors/tools/tailToolResponse.d.ts +5 -0
- package/ts_build/src/processors/tools/tailToolResponse.js +56 -13
- package/ts_build/src/processors/tools/tailToolResponse.js.map +1 -1
- package/ts_build/src/services/LazyToolsService.d.ts +1 -0
- package/ts_build/src/services/LazyToolsService.js +5 -0
- package/ts_build/src/services/LazyToolsService.js.map +1 -1
- package/ts_build/src/services/Mcp.js +8 -2
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/services/RuntimeReloadService.d.ts +12 -0
- package/ts_build/src/services/RuntimeReloadService.js +26 -0
- package/ts_build/src/services/RuntimeReloadService.js.map +1 -0
- package/ts_build/src/services/Tools.d.ts +1 -0
- package/ts_build/src/services/Tools.js +5 -0
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/TracingService.d.ts +16 -0
- package/ts_build/src/services/TracingService.js +35 -0
- package/ts_build/src/services/TracingService.js.map +1 -0
- package/ts_build/src/services/index.d.ts +6 -0
- package/ts_build/src/services/index.js +7 -1
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/modules/index.d.ts +5 -0
- package/ts_build/src/services/modules/index.js +28 -2
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/services/modules/types.d.ts +3 -0
- package/ts_build/src/util/Trace.d.ts +13 -0
- package/ts_build/src/util/Trace.js +107 -0
- package/ts_build/src/util/Trace.js.map +1 -0
- package/ts_build/tests/clients/fireworks.test.d.ts +1 -0
- package/ts_build/tests/clients/fireworks.test.js +65 -0
- package/ts_build/tests/clients/fireworks.test.js.map +1 -0
- package/ts_build/tests/clients/reasoningEffort.test.d.ts +1 -0
- package/ts_build/tests/clients/reasoningEffort.test.js +68 -0
- package/ts_build/tests/clients/reasoningEffort.test.js.map +1 -0
- package/ts_build/tests/compressor/toolResponseCache.test.js +1 -1
- package/ts_build/tests/compressor/toolResponseCache.test.js.map +1 -1
- package/ts_build/tests/manual/clients/completions.test.js +19 -6
- package/ts_build/tests/manual/clients/completions.test.js.map +1 -1
- package/ts_build/tests/processors/TokenCompressor.test.js +11 -2
- package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.d.ts +1 -0
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js +57 -0
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js.map +1 -0
- package/ts_build/tests/processors/tools/grepToolResponse.test.js +37 -0
- package/ts_build/tests/processors/tools/grepToolResponse.test.js.map +1 -1
- package/ts_build/tests/unit/plugins/pluginLoading.test.js +59 -0
- package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
- package/ts_build/tests/unit/services/RuntimeReloadService.test.d.ts +1 -0
- package/ts_build/tests/unit/services/RuntimeReloadService.test.js +26 -0
- package/ts_build/tests/unit/services/RuntimeReloadService.test.js.map +1 -0
|
@@ -5,6 +5,7 @@ import { JsonCompressor } from "./JsonCompressor";
|
|
|
5
5
|
import {
|
|
6
6
|
jqToolResponseDefinition,
|
|
7
7
|
executeJqQuery,
|
|
8
|
+
JqOptions,
|
|
8
9
|
grepToolResponseDefinition,
|
|
9
10
|
executeGrep,
|
|
10
11
|
GrepOptions,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
TailOptions,
|
|
14
15
|
listStoredToolResponsesDefinition,
|
|
15
16
|
executeListStoredToolResponses,
|
|
17
|
+
ListStoredToolResponsesOptions,
|
|
16
18
|
} from "./tools";
|
|
17
19
|
|
|
18
20
|
interface ToolResponseStorage {
|
|
@@ -196,11 +198,12 @@ export class ToolResponseCache {
|
|
|
196
198
|
*/
|
|
197
199
|
async queryToolResponse(
|
|
198
200
|
toolCallId: string,
|
|
199
|
-
jqQuery: string
|
|
201
|
+
jqQuery: string,
|
|
202
|
+
options?: JqOptions
|
|
200
203
|
): Promise<string> {
|
|
201
204
|
const data = this.storage[toolCallId];
|
|
202
205
|
const availableIds = Object.keys(this.storage);
|
|
203
|
-
return executeJqQuery(data, toolCallId, jqQuery, availableIds, this.toolNameMap);
|
|
206
|
+
return executeJqQuery(data, toolCallId, jqQuery, availableIds, this.toolNameMap, options);
|
|
204
207
|
}
|
|
205
208
|
|
|
206
209
|
/**
|
|
@@ -231,11 +234,14 @@ export class ToolResponseCache {
|
|
|
231
234
|
/**
|
|
232
235
|
* List all stored tool responses with metadata
|
|
233
236
|
*/
|
|
234
|
-
async listStoredToolResponses(
|
|
237
|
+
async listStoredToolResponses(
|
|
238
|
+
options?: ListStoredToolResponsesOptions
|
|
239
|
+
): Promise<string> {
|
|
235
240
|
return executeListStoredToolResponses(
|
|
236
241
|
this.storage,
|
|
237
242
|
this.metadataStorage,
|
|
238
|
-
this.toolNameMap
|
|
243
|
+
this.toolNameMap,
|
|
244
|
+
options
|
|
239
245
|
);
|
|
240
246
|
}
|
|
241
247
|
|
|
@@ -281,9 +287,10 @@ export class ToolResponseCache {
|
|
|
281
287
|
toolsService.addFunctions({
|
|
282
288
|
[jqToolResponseDefinition.function.name]: async (
|
|
283
289
|
toolCallId: string,
|
|
284
|
-
jqQuery: string
|
|
290
|
+
jqQuery: string,
|
|
291
|
+
options?: JqOptions
|
|
285
292
|
) => {
|
|
286
|
-
return await this.queryToolResponse(toolCallId, jqQuery);
|
|
293
|
+
return await this.queryToolResponse(toolCallId, jqQuery, options);
|
|
287
294
|
},
|
|
288
295
|
[grepToolResponseDefinition.function.name]: async (
|
|
289
296
|
toolCallId: string,
|
|
@@ -298,8 +305,10 @@ export class ToolResponseCache {
|
|
|
298
305
|
) => {
|
|
299
306
|
return await this.tailToolResponse(toolCallId, options);
|
|
300
307
|
},
|
|
301
|
-
[listStoredToolResponsesDefinition.function.name]: async (
|
|
302
|
-
|
|
308
|
+
[listStoredToolResponsesDefinition.function.name]: async (
|
|
309
|
+
options?: ListStoredToolResponsesOptions
|
|
310
|
+
) => {
|
|
311
|
+
return await this.listStoredToolResponses(options);
|
|
303
312
|
},
|
|
304
313
|
});
|
|
305
314
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const DEFAULT_TOOL_RESPONSE_CHARACTERS = 20_000;
|
|
2
|
+
export const MAX_TOOL_RESPONSE_CHARACTERS = 50_000;
|
|
3
|
+
|
|
4
|
+
export function boundedInteger(
|
|
5
|
+
value: number | undefined,
|
|
6
|
+
fallback: number,
|
|
7
|
+
min: number,
|
|
8
|
+
max: number
|
|
9
|
+
): number {
|
|
10
|
+
return Math.min(
|
|
11
|
+
max,
|
|
12
|
+
Math.max(min, Number.isFinite(value) ? Math.floor(value as number) : fallback)
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Return a bounded character page with instructions for retrieving the next page. */
|
|
17
|
+
export function paginateToolResponse(
|
|
18
|
+
value: string,
|
|
19
|
+
options?: { characterOffset?: number; maxCharacters?: number },
|
|
20
|
+
label = "response"
|
|
21
|
+
): string {
|
|
22
|
+
const characterOffset = boundedInteger(
|
|
23
|
+
options?.characterOffset,
|
|
24
|
+
0,
|
|
25
|
+
0,
|
|
26
|
+
Number.MAX_SAFE_INTEGER
|
|
27
|
+
);
|
|
28
|
+
const maxCharacters = boundedInteger(
|
|
29
|
+
options?.maxCharacters,
|
|
30
|
+
DEFAULT_TOOL_RESPONSE_CHARACTERS,
|
|
31
|
+
1_000,
|
|
32
|
+
MAX_TOOL_RESPONSE_CHARACTERS
|
|
33
|
+
);
|
|
34
|
+
const footerReserve = 300;
|
|
35
|
+
const contentLimit = Math.max(1, maxCharacters - footerReserve);
|
|
36
|
+
const page = value.slice(characterOffset, characterOffset + contentLimit);
|
|
37
|
+
const end = characterOffset + page.length;
|
|
38
|
+
if (characterOffset === 0 && end >= value.length) {
|
|
39
|
+
return page;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const safeLabel = label.slice(0, 200);
|
|
43
|
+
const footer =
|
|
44
|
+
end < value.length
|
|
45
|
+
? `\n\n[${safeLabel} characters ${characterOffset}-${Math.max(characterOffset, end - 1)} of ${value.length}. Repeat with characterOffset=${end} to continue; maxCharacters=${maxCharacters}.]`
|
|
46
|
+
: `\n\n[End of ${safeLabel}; characters ${characterOffset}-${Math.max(characterOffset, end - 1)} of ${value.length}.]`;
|
|
47
|
+
|
|
48
|
+
return (page + footer).slice(0, maxCharacters);
|
|
49
|
+
}
|
|
@@ -6,11 +6,33 @@ export interface GrepOptions {
|
|
|
6
6
|
contextBefore?: number;
|
|
7
7
|
contextAfter?: number;
|
|
8
8
|
maxResults?: number;
|
|
9
|
+
resultOffset?: number;
|
|
10
|
+
maxCharacters?: number;
|
|
11
|
+
lineCharacterOffset?: number;
|
|
12
|
+
maxLineCharacters?: number;
|
|
9
13
|
}
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
const DEFAULT_MAX_RESULTS = 100;
|
|
16
|
+
const MAX_RESULTS = 1000;
|
|
17
|
+
const DEFAULT_MAX_CHARACTERS = 20_000;
|
|
18
|
+
const MAX_CHARACTERS = 50_000;
|
|
19
|
+
const DEFAULT_MAX_LINE_CHARACTERS = 4_000;
|
|
20
|
+
const MAX_LINE_CHARACTERS = 10_000;
|
|
21
|
+
const FOOTER_RESERVE = 700;
|
|
22
|
+
|
|
23
|
+
function boundedInteger(
|
|
24
|
+
value: number | undefined,
|
|
25
|
+
fallback: number,
|
|
26
|
+
min: number,
|
|
27
|
+
max: number
|
|
28
|
+
): number {
|
|
29
|
+
return Math.min(
|
|
30
|
+
max,
|
|
31
|
+
Math.max(min, Number.isFinite(value) ? Math.floor(value as number) : fallback)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Search stored tool-response text without ever returning an unbounded result. */
|
|
14
36
|
export async function executeGrep(
|
|
15
37
|
data: string,
|
|
16
38
|
toolCallId: string,
|
|
@@ -26,51 +48,145 @@ export async function executeGrep(
|
|
|
26
48
|
return name ? `${id} (${name})` : id;
|
|
27
49
|
})
|
|
28
50
|
.join("\n - ");
|
|
29
|
-
|
|
51
|
+
const maxCharacters = boundedInteger(
|
|
52
|
+
options?.maxCharacters,
|
|
53
|
+
DEFAULT_MAX_CHARACTERS,
|
|
54
|
+
1000,
|
|
55
|
+
MAX_CHARACTERS
|
|
56
|
+
);
|
|
57
|
+
return `Error: No tool response found for toolCallId "${toolCallId}". Call listStoredToolResponses to see all available responses with their tool names.\n\nAvailable toolCallIds:\n - ${idList || "(none)"}`.slice(
|
|
58
|
+
0,
|
|
59
|
+
maxCharacters
|
|
60
|
+
);
|
|
30
61
|
}
|
|
31
62
|
|
|
32
63
|
try {
|
|
33
64
|
const lines = data.split("\n");
|
|
34
65
|
const matchedResults: string[] = [];
|
|
35
|
-
const ignoreCase = options?.ignoreCase
|
|
36
|
-
const invertMatch = options?.invertMatch
|
|
37
|
-
const contextBefore = options?.contextBefore
|
|
38
|
-
const contextAfter = options?.contextAfter
|
|
39
|
-
const maxResults = options?.maxResults
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
66
|
+
const ignoreCase = options?.ignoreCase ?? false;
|
|
67
|
+
const invertMatch = options?.invertMatch ?? false;
|
|
68
|
+
const contextBefore = boundedInteger(options?.contextBefore, 0, 0, 1000);
|
|
69
|
+
const contextAfter = boundedInteger(options?.contextAfter, 0, 0, 1000);
|
|
70
|
+
const maxResults = boundedInteger(options?.maxResults, DEFAULT_MAX_RESULTS, 1, MAX_RESULTS);
|
|
71
|
+
const resultOffset = boundedInteger(options?.resultOffset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
72
|
+
const maxCharacters = boundedInteger(
|
|
73
|
+
options?.maxCharacters,
|
|
74
|
+
DEFAULT_MAX_CHARACTERS,
|
|
75
|
+
1000,
|
|
76
|
+
MAX_CHARACTERS
|
|
77
|
+
);
|
|
78
|
+
const lineCharacterOffset = boundedInteger(
|
|
79
|
+
options?.lineCharacterOffset,
|
|
80
|
+
0,
|
|
81
|
+
0,
|
|
82
|
+
Number.MAX_SAFE_INTEGER
|
|
83
|
+
);
|
|
84
|
+
const maxLineCharacters = boundedInteger(
|
|
85
|
+
options?.maxLineCharacters,
|
|
86
|
+
DEFAULT_MAX_LINE_CHARACTERS,
|
|
87
|
+
100,
|
|
88
|
+
MAX_LINE_CHARACTERS
|
|
89
|
+
);
|
|
90
|
+
const contentBudget = Math.max(1, maxCharacters - FOOTER_RESERVE);
|
|
91
|
+
const regex = new RegExp(pattern, ignoreCase ? "i" : "");
|
|
92
|
+
|
|
93
|
+
let totalMatches = 0;
|
|
94
|
+
let outputCharacters = 0;
|
|
95
|
+
let outputTruncated = false;
|
|
96
|
+
const pagedLineNumbers = new Set<number>();
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < lines.length; i++) {
|
|
99
|
+
const matches = regex.test(lines[i]);
|
|
100
|
+
if (invertMatch ? matches : !matches) continue;
|
|
101
|
+
|
|
102
|
+
const resultIndex = totalMatches++;
|
|
103
|
+
const startIdx = Math.max(0, i - contextBefore);
|
|
104
|
+
const endIdx = Math.min(lines.length - 1, i + contextAfter);
|
|
105
|
+
|
|
106
|
+
if (
|
|
107
|
+
resultIndex >= resultOffset &&
|
|
108
|
+
matchedResults.length < maxResults &&
|
|
109
|
+
!outputTruncated
|
|
110
|
+
) {
|
|
111
|
+
const contextLines: string[] = [];
|
|
55
112
|
for (let j = startIdx; j <= endIdx; j++) {
|
|
113
|
+
const sourceLine = lines[j];
|
|
114
|
+
const visibleLine = sourceLine.slice(
|
|
115
|
+
lineCharacterOffset,
|
|
116
|
+
lineCharacterOffset + maxLineCharacters
|
|
117
|
+
);
|
|
56
118
|
const prefix = j === i ? "> " : " ";
|
|
57
|
-
|
|
119
|
+
let rendered = `${prefix}${j + 1}: ${visibleLine}`;
|
|
120
|
+
|
|
121
|
+
if (lineCharacterOffset >= sourceLine.length && sourceLine.length > 0) {
|
|
122
|
+
rendered += ` [no characters at offset ${lineCharacterOffset}; line length=${sourceLine.length}]`;
|
|
123
|
+
} else if (lineCharacterOffset + maxLineCharacters < sourceLine.length) {
|
|
124
|
+
pagedLineNumbers.add(j + 1);
|
|
125
|
+
rendered += `\n [line ${j + 1} truncated; characters ${lineCharacterOffset}-${lineCharacterOffset + visibleLine.length - 1} of ${sourceLine.length}]`;
|
|
126
|
+
}
|
|
127
|
+
contextLines.push(rendered);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const separatorLength = matchedResults.length > 0 ? 5 : 0;
|
|
131
|
+
const result = contextLines.join("\n");
|
|
132
|
+
const remaining = contentBudget - outputCharacters - separatorLength;
|
|
133
|
+
if (result.length > remaining) {
|
|
134
|
+
if (matchedResults.length === 0 && remaining > 80) {
|
|
135
|
+
matchedResults.push(
|
|
136
|
+
`${result.slice(0, remaining - 48)}\n[result page truncated by maxCharacters]`
|
|
137
|
+
);
|
|
138
|
+
outputCharacters = contentBudget;
|
|
139
|
+
}
|
|
140
|
+
outputTruncated = true;
|
|
141
|
+
} else {
|
|
142
|
+
matchedResults.push(result);
|
|
143
|
+
outputCharacters += separatorLength + result.length;
|
|
58
144
|
}
|
|
59
|
-
|
|
60
|
-
matchedResults.push(contextLines.join("\n"));
|
|
61
|
-
|
|
62
|
-
// Skip ahead to avoid overlapping context
|
|
63
|
-
i += contextAfter;
|
|
64
145
|
}
|
|
146
|
+
|
|
147
|
+
// Avoid duplicate result blocks when their after-context overlaps a match.
|
|
148
|
+
i += contextAfter;
|
|
65
149
|
}
|
|
66
150
|
|
|
67
|
-
if (
|
|
68
|
-
|
|
151
|
+
if (totalMatches === 0) {
|
|
152
|
+
const preview = pattern.length > 200 ? `${pattern.slice(0, 200)}…` : pattern;
|
|
153
|
+
return `No matches found for pattern "${preview}" in toolCallId "${toolCallId}"`.slice(
|
|
154
|
+
0,
|
|
155
|
+
maxCharacters
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const consumedResults = matchedResults.length;
|
|
160
|
+
const nextResultOffset = resultOffset + consumedResults;
|
|
161
|
+
const hasMoreResults = nextResultOffset < totalMatches;
|
|
162
|
+
const range = consumedResults
|
|
163
|
+
? `${resultOffset + 1}-${nextResultOffset}`
|
|
164
|
+
: `none (offset ${resultOffset} is past the final result)`;
|
|
165
|
+
const footerLines = [
|
|
166
|
+
"",
|
|
167
|
+
`[grep page: results ${range} of ${totalMatches}; ${consumedResults} returned; maxCharacters=${maxCharacters}]`,
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
if (pagedLineNumbers.size > 0) {
|
|
171
|
+
footerLines.push(
|
|
172
|
+
`[Long source line(s) ${Array.from(pagedLineNumbers).join(", ")} were sliced. Repeat with options.lineCharacterOffset=${lineCharacterOffset + maxLineCharacters} to read their next characters.]`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (hasMoreResults) {
|
|
176
|
+
footerLines.push(
|
|
177
|
+
`[More matches available. Repeat with options.resultOffset=${nextResultOffset}.]`
|
|
178
|
+
);
|
|
179
|
+
} else {
|
|
180
|
+
footerLines.push("[End of matches.]");
|
|
69
181
|
}
|
|
70
182
|
|
|
71
|
-
return matchedResults.join("\n---\n")
|
|
72
|
-
|
|
73
|
-
|
|
183
|
+
return (matchedResults.join("\n---\n") + footerLines.join("\n")).slice(
|
|
184
|
+
0,
|
|
185
|
+
maxCharacters
|
|
186
|
+
);
|
|
187
|
+
} catch (error: unknown) {
|
|
188
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
189
|
+
return `Grep Error: ${message.slice(0, 500)}`;
|
|
74
190
|
}
|
|
75
191
|
}
|
|
76
192
|
|
|
@@ -79,7 +195,7 @@ export const grepToolResponseDefinition: Tool = {
|
|
|
79
195
|
function: {
|
|
80
196
|
name: "grepToolResponse",
|
|
81
197
|
description:
|
|
82
|
-
"Search
|
|
198
|
+
"Search a stored tool response using a regular expression. Results and individual source lines are bounded and paginated. Follow the returned resultOffset or lineCharacterOffset instruction to retrieve more.",
|
|
83
199
|
parameters: {
|
|
84
200
|
type: "object",
|
|
85
201
|
positional: true,
|
|
@@ -94,8 +210,18 @@ export const grepToolResponseDefinition: Tool = {
|
|
|
94
210
|
},
|
|
95
211
|
options: {
|
|
96
212
|
type: "object",
|
|
97
|
-
description: "Optional grep
|
|
98
|
-
properties: {
|
|
213
|
+
description: "Optional grep, output-bound, and pagination settings.",
|
|
214
|
+
properties: {
|
|
215
|
+
ignoreCase: { type: "boolean", description: "Case-insensitive matching." },
|
|
216
|
+
invertMatch: { type: "boolean", description: "Return non-matching lines." },
|
|
217
|
+
contextBefore: { type: "number", description: "Context lines before a match (maximum 1000)." },
|
|
218
|
+
contextAfter: { type: "number", description: "Context lines after a match (maximum 1000)." },
|
|
219
|
+
maxResults: { type: "number", description: "Matches per page (default 100, maximum 1000)." },
|
|
220
|
+
resultOffset: { type: "number", description: "Zero-based match offset for the next result page." },
|
|
221
|
+
maxCharacters: { type: "number", description: "Response size (default 20000, hard maximum 50000)." },
|
|
222
|
+
lineCharacterOffset: { type: "number", description: "Character offset within every source line, for paging very long lines." },
|
|
223
|
+
maxLineCharacters: { type: "number", description: "Characters shown per source line (default 4000, maximum 10000)." },
|
|
224
|
+
},
|
|
99
225
|
},
|
|
100
226
|
},
|
|
101
227
|
required: ["toolCallId", "pattern"],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export {
|
|
2
2
|
jqToolResponseDefinition,
|
|
3
3
|
executeJqQuery,
|
|
4
|
+
JqOptions,
|
|
4
5
|
} from "./jqToolResponse";
|
|
5
6
|
|
|
6
7
|
export {
|
|
@@ -18,4 +19,5 @@ export {
|
|
|
18
19
|
export {
|
|
19
20
|
listStoredToolResponsesDefinition,
|
|
20
21
|
executeListStoredToolResponses,
|
|
22
|
+
ListStoredToolResponsesOptions,
|
|
21
23
|
} from "./listStoredToolResponses";
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Tool } from "../../clients";
|
|
2
2
|
import * as jq from "node-jq";
|
|
3
|
+
import { paginateToolResponse } from "./boundedToolResponse";
|
|
4
|
+
|
|
5
|
+
export interface JqOptions {
|
|
6
|
+
characterOffset?: number;
|
|
7
|
+
maxCharacters?: number;
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
/**
|
|
5
11
|
* Attempts to parse content as JSON and returns parsed object if successful
|
|
@@ -47,7 +53,8 @@ export async function executeJqQuery(
|
|
|
47
53
|
toolCallId: string,
|
|
48
54
|
jqQuery: string,
|
|
49
55
|
availableIds: string[],
|
|
50
|
-
toolNameMap?: { [toolCallId: string]: string }
|
|
56
|
+
toolNameMap?: { [toolCallId: string]: string },
|
|
57
|
+
options?: JqOptions
|
|
51
58
|
): Promise<string> {
|
|
52
59
|
if (!data) {
|
|
53
60
|
const idList = availableIds
|
|
@@ -56,30 +63,39 @@ export async function executeJqQuery(
|
|
|
56
63
|
return name ? `${id} (${name})` : id;
|
|
57
64
|
})
|
|
58
65
|
.join("\n - ");
|
|
59
|
-
return
|
|
66
|
+
return paginateToolResponse(
|
|
67
|
+
`Error: No tool response found for toolCallId "${toolCallId}". Call listStoredToolResponses to see all available responses with their tool names.\n\nAvailable toolCallIds:\n - ${idList || "(none)"}`,
|
|
68
|
+
options,
|
|
69
|
+
"available toolCallId list"
|
|
70
|
+
);
|
|
60
71
|
}
|
|
61
72
|
|
|
62
73
|
try {
|
|
63
74
|
// First parse the stored string as JSON, then handle nested JSON strings
|
|
64
75
|
const jsonData = tryParseJson(data);
|
|
65
76
|
if (!jsonData) {
|
|
66
|
-
return
|
|
77
|
+
return paginateToolResponse(
|
|
78
|
+
`Error: Tool response data is not valid JSON for toolCallId "${toolCallId}"`,
|
|
79
|
+
options,
|
|
80
|
+
"jq input error"
|
|
81
|
+
);
|
|
67
82
|
}
|
|
68
83
|
const parsedData = parseNestedJsonStrings(jsonData);
|
|
69
84
|
|
|
70
85
|
// Execute JQ query
|
|
71
86
|
const result = await jq.run(jqQuery, parsedData, { input: "json" });
|
|
72
87
|
|
|
73
|
-
|
|
88
|
+
let rendered: string;
|
|
74
89
|
if (typeof result === "string") {
|
|
75
|
-
|
|
90
|
+
rendered = result;
|
|
76
91
|
} else if (typeof result === "number" || typeof result === "boolean") {
|
|
77
|
-
|
|
92
|
+
rendered = String(result);
|
|
78
93
|
} else if (result === null) {
|
|
79
|
-
|
|
94
|
+
rendered = "null";
|
|
80
95
|
} else {
|
|
81
|
-
|
|
96
|
+
rendered = JSON.stringify(result);
|
|
82
97
|
}
|
|
98
|
+
return paginateToolResponse(rendered, options, "jq result");
|
|
83
99
|
} catch (error: any) {
|
|
84
100
|
// If JQ fails, try to provide helpful error message
|
|
85
101
|
let errorMessage = `JQ Query Error: ${error.message}`;
|
|
@@ -99,7 +115,7 @@ export async function executeJqQuery(
|
|
|
99
115
|
).substring(0, 500)}...`;
|
|
100
116
|
}
|
|
101
117
|
|
|
102
|
-
return errorMessage;
|
|
118
|
+
return paginateToolResponse(errorMessage, options, "jq error");
|
|
103
119
|
}
|
|
104
120
|
}
|
|
105
121
|
|
|
@@ -108,7 +124,7 @@ export const jqToolResponseDefinition: Tool = {
|
|
|
108
124
|
function: {
|
|
109
125
|
name: "jqToolResponse",
|
|
110
126
|
description:
|
|
111
|
-
"Execute a JQ query on a stored tool response
|
|
127
|
+
"Execute a JQ query on a stored tool response. Output is bounded to 20,000 characters by default; use options.characterOffset to page through a large result. Prefer a selective JQ query over paging a broad query. Call listStoredToolResponses first to discover the toolCallId. MCP responses are generally rooted at '.' (or '._data' when _mcp_format is true); standard built-in responses may be nested under '.content[0].text | fromjson'.",
|
|
112
128
|
parameters: {
|
|
113
129
|
type: "object",
|
|
114
130
|
positional: true,
|
|
@@ -122,6 +138,20 @@ export const jqToolResponseDefinition: Tool = {
|
|
|
122
138
|
description:
|
|
123
139
|
"The JQ query to execute on the tool response data. For mcp_* tool responses (raw JSON object): '.children | map({id: .id, name: .name})' (extract fields from children array), '.children | map(select(.state == \"PENDING\")) | length' (count pending children), '.name' (get a top-level field). For compressed responses (._mcp_format true): '._data.children | map(.name)' or '._data | map(select(.state == \"PENDING\")) | length'. For standard built-in tool responses: '.content[0].text | fromjson | map(.title)' (extract titles from standard MCP array), '.content[0].text | fromjson | map(select(.createdAt > \"2025-01-01\"))' (filter by date).",
|
|
124
140
|
},
|
|
141
|
+
options: {
|
|
142
|
+
type: "object",
|
|
143
|
+
description: "Optional output pagination settings.",
|
|
144
|
+
properties: {
|
|
145
|
+
characterOffset: {
|
|
146
|
+
type: "number",
|
|
147
|
+
description: "Zero-based character offset into the rendered JQ result.",
|
|
148
|
+
},
|
|
149
|
+
maxCharacters: {
|
|
150
|
+
type: "number",
|
|
151
|
+
description: "Response size (default 20000, hard maximum 50000).",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
125
155
|
},
|
|
126
156
|
required: ["toolCallId", "jqQuery"],
|
|
127
157
|
},
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { Tool } from "../../clients";
|
|
2
|
+
import { boundedInteger, paginateToolResponse } from "./boundedToolResponse";
|
|
3
|
+
|
|
4
|
+
export interface ListStoredToolResponsesOptions {
|
|
5
|
+
resultOffset?: number;
|
|
6
|
+
maxResults?: number;
|
|
7
|
+
characterOffset?: number;
|
|
8
|
+
maxCharacters?: number;
|
|
9
|
+
}
|
|
2
10
|
|
|
3
11
|
export interface ToolResponseInfo {
|
|
4
12
|
toolCallId: string;
|
|
@@ -21,7 +29,8 @@ export async function executeListStoredToolResponses(
|
|
|
21
29
|
toolName?: string;
|
|
22
30
|
};
|
|
23
31
|
},
|
|
24
|
-
toolNameMap: { [toolCallId: string]: string }
|
|
32
|
+
toolNameMap: { [toolCallId: string]: string },
|
|
33
|
+
options?: ListStoredToolResponsesOptions
|
|
25
34
|
): Promise<string> {
|
|
26
35
|
const toolCallIds = Object.keys(storage);
|
|
27
36
|
|
|
@@ -50,13 +59,15 @@ export async function executeListStoredToolResponses(
|
|
|
50
59
|
// Sort by most recent first
|
|
51
60
|
responses.sort((a, b) => b.storedAt - a.storedAt);
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
const
|
|
62
|
+
const resultOffset = boundedInteger(options?.resultOffset, 0, 0, responses.length);
|
|
63
|
+
const maxResults = boundedInteger(options?.maxResults, 100, 1, 1000);
|
|
64
|
+
const page = responses.slice(resultOffset, resultOffset + maxResults);
|
|
65
|
+
const output = page
|
|
55
66
|
.map((resp) => {
|
|
56
67
|
const date = new Date(resp.storedAt).toISOString();
|
|
57
68
|
return `
|
|
58
|
-
Tool Call ID: ${resp.toolCallId}
|
|
59
|
-
Tool Name: ${resp.toolName}
|
|
69
|
+
Tool Call ID: ${resp.toolCallId.slice(0, 500)}
|
|
70
|
+
Tool Name: ${resp.toolName.slice(0, 500)}
|
|
60
71
|
Size: ${resp.size} characters
|
|
61
72
|
Stored At: ${date}
|
|
62
73
|
Preview: ${resp.preview}
|
|
@@ -64,7 +75,15 @@ Preview: ${resp.preview}
|
|
|
64
75
|
})
|
|
65
76
|
.join("\n");
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
const nextOffset = resultOffset + page.length;
|
|
79
|
+
const footer = nextOffset < responses.length
|
|
80
|
+
? `\n[More stored responses available. Repeat with options.resultOffset=${nextOffset}.]`
|
|
81
|
+
: "\n[End of stored responses.]";
|
|
82
|
+
return paginateToolResponse(
|
|
83
|
+
`Found ${responses.length} stored tool response(s); showing ${resultOffset + 1}-${nextOffset}:\n${output}${footer}`,
|
|
84
|
+
options,
|
|
85
|
+
"stored-response list"
|
|
86
|
+
);
|
|
68
87
|
}
|
|
69
88
|
|
|
70
89
|
export const listStoredToolResponsesDefinition: Tool = {
|
|
@@ -72,12 +91,34 @@ export const listStoredToolResponsesDefinition: Tool = {
|
|
|
72
91
|
function: {
|
|
73
92
|
name: "listStoredToolResponses",
|
|
74
93
|
description:
|
|
75
|
-
"List
|
|
94
|
+
"List a bounded page of stored tool responses with IDs, tool names, sizes, timestamps, and previews. Call this before jqToolResponse, grepToolResponse, or tailToolResponse to discover a toolCallId.",
|
|
76
95
|
parameters: {
|
|
77
96
|
type: "object",
|
|
78
|
-
positional:
|
|
79
|
-
properties: {
|
|
80
|
-
|
|
97
|
+
positional: true,
|
|
98
|
+
properties: {
|
|
99
|
+
options: {
|
|
100
|
+
type: "object",
|
|
101
|
+
description: "Optional result and output pagination settings.",
|
|
102
|
+
properties: {
|
|
103
|
+
resultOffset: {
|
|
104
|
+
type: "number",
|
|
105
|
+
description: "Zero-based response offset (default 0).",
|
|
106
|
+
},
|
|
107
|
+
maxResults: {
|
|
108
|
+
type: "number",
|
|
109
|
+
description: "Responses per page (default 100, maximum 1000).",
|
|
110
|
+
},
|
|
111
|
+
characterOffset: {
|
|
112
|
+
type: "number",
|
|
113
|
+
description: "Character offset within the rendered page.",
|
|
114
|
+
},
|
|
115
|
+
maxCharacters: {
|
|
116
|
+
type: "number",
|
|
117
|
+
description: "Response size (default 20000, hard maximum 50000).",
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
81
122
|
},
|
|
82
123
|
},
|
|
83
124
|
};
|