@tyvm/knowhow 0.0.97 → 0.0.99
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/agents/base/base.ts +12 -4
- package/src/agents/patcher/patcher.ts +1 -1
- package/src/agents/tools/list.ts +1 -0
- package/src/chat/modules/AgentModule.ts +24 -22
- package/src/chat/modules/SessionsModule.ts +1 -3
- package/src/chat/modules/SystemModule.ts +23 -8
- package/src/cli.ts +17 -0
- package/src/clients/anthropic.ts +10 -4
- package/src/clients/gemini.ts +23 -5
- package/src/clients/http.ts +5 -3
- package/src/clients/index.ts +261 -139
- package/src/clients/knowhow.ts +15 -4
- package/src/clients/openai.ts +213 -10
- package/src/clients/types.ts +7 -1
- package/src/clients/xai.ts +13 -6
- package/src/cloudWorker.ts +314 -0
- package/src/config.ts +9 -1
- package/src/processors/TokenCompressor.ts +8 -1
- package/src/services/KnowhowClient.ts +56 -12
- package/src/services/LazyToolsService.ts +15 -14
- package/src/services/Tools.ts +10 -1
- package/src/types.ts +11 -2
- package/src/utils/InputQueueManager.ts +131 -20
- package/test-ai-completion.ts +39 -0
- package/test-mcp-args.ts +71 -0
- package/test-tools-service.ts +45 -0
- package/ts_build/package.json +1 -1
- package/ts_build/src/agents/base/base.js +8 -2
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/patcher/patcher.js +1 -1
- package/ts_build/src/agents/patcher/patcher.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +1 -0
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +17 -19
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/SessionsModule.js +1 -3
- package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
- package/ts_build/src/chat/modules/SystemModule.js +16 -8
- package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
- package/ts_build/src/cli.js +17 -0
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +9 -7
- package/ts_build/src/clients/anthropic.js +9 -4
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/gemini.d.ts +2 -1
- package/ts_build/src/clients/gemini.js +13 -4
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/http.d.ts +1 -1
- package/ts_build/src/clients/http.js +2 -2
- package/ts_build/src/clients/http.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +23 -47
- package/ts_build/src/clients/index.js +152 -99
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/knowhow.d.ts +3 -2
- package/ts_build/src/clients/knowhow.js +6 -3
- package/ts_build/src/clients/knowhow.js.map +1 -1
- package/ts_build/src/clients/openai.d.ts +20 -18
- package/ts_build/src/clients/openai.js +166 -8
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +3 -1
- package/ts_build/src/clients/xai.d.ts +3 -2
- package/ts_build/src/clients/xai.js +10 -4
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/cloudWorker.d.ts +8 -0
- package/ts_build/src/cloudWorker.js +239 -0
- package/ts_build/src/cloudWorker.js.map +1 -0
- package/ts_build/src/config.js +8 -1
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/processors/TokenCompressor.js +7 -1
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.d.ts +24 -1
- package/ts_build/src/services/KnowhowClient.js +14 -2
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/LazyToolsService.js +5 -7
- package/ts_build/src/services/LazyToolsService.js.map +1 -1
- package/ts_build/src/services/Tools.js +9 -1
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/types.d.ts +3 -1
- package/ts_build/src/types.js +8 -1
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/utils/InputQueueManager.d.ts +2 -0
- package/ts_build/src/utils/InputQueueManager.js +76 -10
- package/ts_build/src/utils/InputQueueManager.js.map +1 -1
|
@@ -144,7 +144,6 @@ export class KnowhowSimpleClient {
|
|
|
144
144
|
const currentOrg = orgs.find((org) => {
|
|
145
145
|
return org.organizationId === orgId;
|
|
146
146
|
});
|
|
147
|
-
|
|
148
147
|
} catch (error) {
|
|
149
148
|
throw new Error("Invalid JWT. Please login again.");
|
|
150
149
|
}
|
|
@@ -203,13 +202,9 @@ export class KnowhowSimpleClient {
|
|
|
203
202
|
}
|
|
204
203
|
) {
|
|
205
204
|
await this.checkJwt();
|
|
206
|
-
return http.put(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
{
|
|
210
|
-
headers: this.headers,
|
|
211
|
-
}
|
|
212
|
-
);
|
|
205
|
+
return http.put(`${this.baseUrl}/api/org-embeddings/${id}`, data, {
|
|
206
|
+
headers: this.headers,
|
|
207
|
+
});
|
|
213
208
|
}
|
|
214
209
|
|
|
215
210
|
async createChatCompletion(options: CompletionOptions) {
|
|
@@ -234,9 +229,9 @@ export class KnowhowSimpleClient {
|
|
|
234
229
|
);
|
|
235
230
|
}
|
|
236
231
|
|
|
237
|
-
async getModels() {
|
|
232
|
+
async getModels(type = "all") {
|
|
238
233
|
await this.checkJwt();
|
|
239
|
-
return http.get(`${this.baseUrl}/api/proxy/v1/models?type
|
|
234
|
+
return http.get(`${this.baseUrl}/api/proxy/v1/models?type=${type}`, {
|
|
240
235
|
headers: this.headers,
|
|
241
236
|
});
|
|
242
237
|
}
|
|
@@ -609,7 +604,11 @@ export class KnowhowSimpleClient {
|
|
|
609
604
|
throw new Error(`File not found: ${filePath}`);
|
|
610
605
|
}
|
|
611
606
|
|
|
612
|
-
await http.post(
|
|
607
|
+
await http.post(
|
|
608
|
+
`${this.baseUrl}/api/org-files/upload/${file.id}/complete`,
|
|
609
|
+
{},
|
|
610
|
+
{ headers: this.headers }
|
|
611
|
+
);
|
|
613
612
|
}
|
|
614
613
|
|
|
615
614
|
/**
|
|
@@ -624,7 +623,8 @@ export class KnowhowSimpleClient {
|
|
|
624
623
|
|
|
625
624
|
// Extract just the filename from the path
|
|
626
625
|
const lastSlash = filePath.lastIndexOf("/");
|
|
627
|
-
const fileName =
|
|
626
|
+
const fileName =
|
|
627
|
+
lastSlash >= 0 ? filePath.substring(lastSlash + 1) : filePath;
|
|
628
628
|
|
|
629
629
|
// Get upload URL using the file ID
|
|
630
630
|
const response = await http.post<{ uploadUrl: string }>(
|
|
@@ -648,4 +648,48 @@ export class KnowhowSimpleClient {
|
|
|
648
648
|
);
|
|
649
649
|
return response.data;
|
|
650
650
|
}
|
|
651
|
+
|
|
652
|
+
// ============================================
|
|
653
|
+
// Cloud Worker Methods
|
|
654
|
+
// ============================================
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* List all cloud workers for the current user's org
|
|
658
|
+
*/
|
|
659
|
+
async listCloudWorkers() {
|
|
660
|
+
await this.checkJwt();
|
|
661
|
+
return http.get<
|
|
662
|
+
{ id: string; name: string; status: string; workerConfigJson?: Record<string, unknown> }[]
|
|
663
|
+
>(`${this.baseUrl}/api/cloud-workers`, { headers: this.headers });
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Create a new cloud worker
|
|
668
|
+
*/
|
|
669
|
+
async createCloudWorker(data: {
|
|
670
|
+
name: string;
|
|
671
|
+
workerConfigJson?: Record<string, unknown>;
|
|
672
|
+
}) {
|
|
673
|
+
await this.checkJwt();
|
|
674
|
+
return http.post<{ id: string; name: string; status: string; workerConfigJson?: Record<string, unknown> }>(
|
|
675
|
+
`${this.baseUrl}/api/cloud-workers`,
|
|
676
|
+
data,
|
|
677
|
+
{ headers: this.headers }
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Update an existing cloud worker
|
|
683
|
+
*/
|
|
684
|
+
async updateCloudWorker(
|
|
685
|
+
id: string,
|
|
686
|
+
data: { workerConfigJson?: Record<string, unknown> }
|
|
687
|
+
) {
|
|
688
|
+
await this.checkJwt();
|
|
689
|
+
return http.put<{ id: string; name: string; status: string; workerConfigJson?: Record<string, unknown> }>(
|
|
690
|
+
`${this.baseUrl}/api/cloud-workers/${id}`,
|
|
691
|
+
data,
|
|
692
|
+
{ headers: this.headers }
|
|
693
|
+
);
|
|
694
|
+
}
|
|
651
695
|
}
|
|
@@ -125,23 +125,24 @@ export class LazyToolsService extends ToolsService {
|
|
|
125
125
|
async callTool(toolCall: ToolCall, enabledTools?: string[]) {
|
|
126
126
|
const functionName = toolCall.function.name;
|
|
127
127
|
|
|
128
|
-
// If
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
128
|
+
// If the tool isn't currently visible but exists in allTools, auto-enable it.
|
|
129
|
+
// This handles the case where the agent explicitly calls a tool without first
|
|
130
|
+
// enabling it via listAvailableTools/enableTools.
|
|
131
|
+
const isCurrentlyEnabled = this.tools.some(
|
|
132
|
+
(t) => t.function.name === functionName
|
|
133
|
+
);
|
|
134
|
+
const existsInAll = this.allTools.some(
|
|
135
|
+
(t) => t.function.name === functionName
|
|
136
|
+
);
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
138
|
+
if (!isCurrentlyEnabled && existsInAll) {
|
|
139
|
+
// Auto-enable by adding the tool name as an exact pattern
|
|
140
|
+
this.enableTools([functionName]);
|
|
142
141
|
}
|
|
143
142
|
|
|
144
|
-
|
|
143
|
+
// Always use the current enabled tool names after any auto-enable above,
|
|
144
|
+
// so the base class check sees the freshly-enabled tool in the allowed list.
|
|
145
|
+
return super.callTool(toolCall, this.getToolNames());
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
// Internal: Update visible tools based on patterns
|
package/src/services/Tools.ts
CHANGED
|
@@ -73,7 +73,12 @@ export class ToolsService {
|
|
|
73
73
|
this.addTools(tools);
|
|
74
74
|
|
|
75
75
|
for (const name of toolNames) {
|
|
76
|
-
|
|
76
|
+
const fn = toolsService.getFunction(name);
|
|
77
|
+
if (!fn) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.setFunction(name, fn);
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
|
|
@@ -118,6 +123,10 @@ export class ToolsService {
|
|
|
118
123
|
|
|
119
124
|
setFunctions(names: string[], funcs: ((...args: any) => any)[]) {
|
|
120
125
|
for (let i = 0; i < names.length; i++) {
|
|
126
|
+
if (!names[i] || typeof funcs[i] !== "function") {
|
|
127
|
+
console.warn("Ignoring invalid function entry at index", i, "with name", names[i]);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
121
130
|
this.setFunction(names[i], funcs[i]);
|
|
122
131
|
}
|
|
123
132
|
}
|
package/src/types.ts
CHANGED
|
@@ -132,8 +132,9 @@ export type McpConfig = {
|
|
|
132
132
|
};
|
|
133
133
|
|
|
134
134
|
export type ModelProvider = {
|
|
135
|
-
url
|
|
135
|
+
url?: string;
|
|
136
136
|
provider: string;
|
|
137
|
+
envKey?: string;
|
|
137
138
|
headers?: { [key: string]: string };
|
|
138
139
|
jwtFile?: string;
|
|
139
140
|
};
|
|
@@ -359,7 +360,15 @@ export const OpenAiEmbeddingModels = [
|
|
|
359
360
|
EmbeddingModels.openai.EmbeddingLarge3,
|
|
360
361
|
EmbeddingModels.openai.EmbeddingSmall3,
|
|
361
362
|
];
|
|
362
|
-
|
|
363
|
+
|
|
364
|
+
// Models that ONLY support the Responses API (not Chat Completions)
|
|
365
|
+
export const OpenAiResponsesOnlyModels = [
|
|
366
|
+
Models.openai.GPT_53_Codex,
|
|
367
|
+
Models.openai.GPT_54,
|
|
368
|
+
Models.openai.GPT_54_Mini,
|
|
369
|
+
Models.openai.GPT_54_Nano,
|
|
370
|
+
Models.openai.GPT_54_Pro,
|
|
371
|
+
];
|
|
363
372
|
|
|
364
373
|
export const GoogleReasoningModels = [
|
|
365
374
|
Models.google.Gemini_31_Pro_Preview,
|
|
@@ -16,6 +16,9 @@ export class InputQueueManager {
|
|
|
16
16
|
private static rl: readline.Interface | null = null;
|
|
17
17
|
private static keypressListenerSetup = false;
|
|
18
18
|
|
|
19
|
+
// Tab completion state
|
|
20
|
+
private lastKeypressWasTab = false;
|
|
21
|
+
|
|
19
22
|
// We keep one "live" buffer shared across stacked questions
|
|
20
23
|
// (so typing is preserved when questions change)
|
|
21
24
|
private currentLine = "";
|
|
@@ -57,27 +60,11 @@ export class InputQueueManager {
|
|
|
57
60
|
historySize: 0,
|
|
58
61
|
|
|
59
62
|
/**
|
|
60
|
-
*
|
|
63
|
+
* Disable readline's built-in completion display - we handle Tab ourselves
|
|
64
|
+
* in the keypress listener to avoid readline's kRefreshLine overwriting
|
|
65
|
+
* small completion lists (lists that fit in 1-2 lines get erased).
|
|
61
66
|
*/
|
|
62
|
-
completer: (line: string) =>
|
|
63
|
-
const current = this.peek();
|
|
64
|
-
const opts = current?.options ?? [];
|
|
65
|
-
if (opts.length === 0) return [[], line];
|
|
66
|
-
|
|
67
|
-
// Identify the "word" at the end of the line that we want to complete
|
|
68
|
-
// (default readline behavior is word-based completion)
|
|
69
|
-
const lastSpace = Math.max(
|
|
70
|
-
line.lastIndexOf(" "),
|
|
71
|
-
line.lastIndexOf("\t")
|
|
72
|
-
);
|
|
73
|
-
const word = line.slice(lastSpace + 1); // the token to complete
|
|
74
|
-
|
|
75
|
-
const hits = opts.filter((c) => c.startsWith(word));
|
|
76
|
-
|
|
77
|
-
// Return [matches, wordToReplace]
|
|
78
|
-
// Readline will replace `word` with the selected match (or extend if unique)
|
|
79
|
-
return [hits, word];
|
|
80
|
-
},
|
|
67
|
+
completer: (line: string) => [[], line],
|
|
81
68
|
});
|
|
82
69
|
|
|
83
70
|
// When user presses Enter, buffer the line for paste detection
|
|
@@ -177,6 +164,16 @@ export class InputQueueManager {
|
|
|
177
164
|
instance.savedLineBeforeHistory = "";
|
|
178
165
|
}
|
|
179
166
|
|
|
167
|
+
// Handle Tab completion ourselves to avoid readline's kRefreshLine
|
|
168
|
+
// overwriting small completion lists (1-2 line lists get erased by readline).
|
|
169
|
+
if (key?.name === "tab") {
|
|
170
|
+
instance.handleTabCompletion();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Any non-tab keypress resets the "last was tab" state
|
|
175
|
+
instance.lastKeypressWasTab = false;
|
|
176
|
+
|
|
180
177
|
// Custom Up/Down history navigation using only passed-in history
|
|
181
178
|
if (key?.name === "up") {
|
|
182
179
|
const history = instance.getHistory();
|
|
@@ -311,6 +308,120 @@ export class InputQueueManager {
|
|
|
311
308
|
return out;
|
|
312
309
|
}
|
|
313
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Handle Tab key completion manually.
|
|
313
|
+
*
|
|
314
|
+
* We do this ourselves instead of using readline's built-in completer because
|
|
315
|
+
* readline's kRefreshLine (called after displaying completions) uses
|
|
316
|
+
* clearScreenDown which erases small completion lists (1-2 lines) that fit
|
|
317
|
+
* within the terminal's current scroll region. Large lists scroll past and
|
|
318
|
+
* survive, but small lists get wiped out before the user can read them.
|
|
319
|
+
*
|
|
320
|
+
* Behavior:
|
|
321
|
+
* - First Tab: extend line to longest common prefix of matches (if possible)
|
|
322
|
+
* - Second consecutive Tab (no extension possible): print the matches list
|
|
323
|
+
*/
|
|
324
|
+
private handleTabCompletion(): void {
|
|
325
|
+
const current = this.peek();
|
|
326
|
+
const opts = current?.options ?? [];
|
|
327
|
+
|
|
328
|
+
// Sync current line from readline before we do anything
|
|
329
|
+
this.syncFromReadline();
|
|
330
|
+
const line = this.currentLine;
|
|
331
|
+
|
|
332
|
+
if (opts.length === 0) {
|
|
333
|
+
this.lastKeypressWasTab = false;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Find the "word" to complete (everything after last space/tab)
|
|
338
|
+
const lastSpace = Math.max(line.lastIndexOf(" "), line.lastIndexOf("\t"));
|
|
339
|
+
const word = line.slice(lastSpace + 1);
|
|
340
|
+
|
|
341
|
+
const hits = opts.filter((c) => c.startsWith(word));
|
|
342
|
+
|
|
343
|
+
let effectiveHits: string[];
|
|
344
|
+
let completionWord: string; // the replacement for `word` in the line
|
|
345
|
+
|
|
346
|
+
if (hits.length > 0) {
|
|
347
|
+
// Exact prefix matches: use normal longest-common-prefix logic
|
|
348
|
+
effectiveHits = hits;
|
|
349
|
+
completionWord = this.longestCommonPrefix(effectiveHits);
|
|
350
|
+
} else {
|
|
351
|
+
// Contains matches: find options that contain the typed word somewhere
|
|
352
|
+
const containsHits = opts.filter((c) => c.includes(word));
|
|
353
|
+
effectiveHits = containsHits;
|
|
354
|
+
|
|
355
|
+
if (containsHits.length > 0) {
|
|
356
|
+
// If there's only one match, complete to the full option string
|
|
357
|
+
if (containsHits.length === 1) {
|
|
358
|
+
completionWord = containsHits[0];
|
|
359
|
+
} else {
|
|
360
|
+
// For each match, find the position of `word` inside it, then take
|
|
361
|
+
// the substring from that position and find the longest common prefix
|
|
362
|
+
// of all those suffixes. This gives us the longest unambiguous extension
|
|
363
|
+
// starting from the user's typed word.
|
|
364
|
+
const suffixes = containsHits.map((c) => {
|
|
365
|
+
const idx = c.indexOf(word);
|
|
366
|
+
return c.slice(idx); // e.g. "opus-4-5" from "anthropic/claude-opus-4-5"
|
|
367
|
+
});
|
|
368
|
+
completionWord = this.longestCommonPrefix(suffixes);
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
completionWord = word;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (effectiveHits.length === 0) {
|
|
376
|
+
this.lastKeypressWasTab = false;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (completionWord.length > word.length) {
|
|
381
|
+
// Can extend - replace word with the longer completion
|
|
382
|
+
const before = line.slice(0, lastSpace + 1);
|
|
383
|
+
const newLine = before + completionWord;
|
|
384
|
+
this.replaceLine(newLine);
|
|
385
|
+
this.currentLine = newLine;
|
|
386
|
+
this.lastKeypressWasTab = false;
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// No extension possible - show list on second consecutive Tab
|
|
391
|
+
if (!this.lastKeypressWasTab) {
|
|
392
|
+
// First tab with no extension: just set flag, user needs to tab again
|
|
393
|
+
this.lastKeypressWasTab = true;
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Second consecutive tab: print the completions list
|
|
398
|
+
this.lastKeypressWasTab = false;
|
|
399
|
+
const columns = process.stdout.columns || 80;
|
|
400
|
+
const maxWidth = Math.max(...effectiveHits.map((h) => h.length)) + 2;
|
|
401
|
+
const numCols = Math.max(1, Math.floor(columns / maxWidth));
|
|
402
|
+
const rows: string[] = [];
|
|
403
|
+
for (let i = 0; i < effectiveHits.length; i += numCols) {
|
|
404
|
+
const row = effectiveHits.slice(i, i + numCols);
|
|
405
|
+
rows.push(row.map((h) => h.padEnd(maxWidth)).join(""));
|
|
406
|
+
}
|
|
407
|
+
// Write completions. We intentionally do NOT call rl.prompt() here because
|
|
408
|
+
// readline tracks the cursor row internally. When prompt(true) is called,
|
|
409
|
+
// readline moves the cursor back UP to where it thinks the prompt is and
|
|
410
|
+
// redraws — overwriting any completion output that fit within the same scroll
|
|
411
|
+
// region (i.e., small lists that didn't cause the terminal to scroll).
|
|
412
|
+
//
|
|
413
|
+
// Instead, we write the completions and then a fresh prompt line manually,
|
|
414
|
+
// without involving readline's cursor tracking at all.
|
|
415
|
+
const promptText = current!.question;
|
|
416
|
+
const inputText = this.currentLine;
|
|
417
|
+
process.stdout.write("\r\n" + rows.join("\r\n") + "\r\n" + promptText + inputText);
|
|
418
|
+
// Now sync readline's internal state to match what we drew manually.
|
|
419
|
+
// We replace the line content via ctrl+u then re-type it so readline's
|
|
420
|
+
// internal `line` buffer and cursor position are correct.
|
|
421
|
+
InputQueueManager.rl?.write(null, { ctrl: true, name: "u" });
|
|
422
|
+
if (inputText) InputQueueManager.rl?.write(inputText);
|
|
423
|
+
}
|
|
424
|
+
|
|
314
425
|
private render(): void {
|
|
315
426
|
if (!InputQueueManager.rl) return;
|
|
316
427
|
const current = this.peek();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct test of createAiCompletion outside the script sandbox
|
|
3
|
+
*/
|
|
4
|
+
import { createAiCompletion } from './src/agents/tools/aiClient';
|
|
5
|
+
import { services } from './src/services';
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
// Initialize services
|
|
9
|
+
const svc = services();
|
|
10
|
+
|
|
11
|
+
// Wait for clients to initialize
|
|
12
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
13
|
+
|
|
14
|
+
console.log('Testing createAiCompletion directly...');
|
|
15
|
+
|
|
16
|
+
// Test with anthropic (empty provider - auto-detect from model)
|
|
17
|
+
try {
|
|
18
|
+
const result = await createAiCompletion.call(svc.Tools, 'anthropic', {
|
|
19
|
+
model: 'claude-3-5-haiku-20241022',
|
|
20
|
+
messages: [{ role: 'user', content: 'Say hello in 5 words' }]
|
|
21
|
+
});
|
|
22
|
+
console.log('Direct call result:', result?.choices?.[0]?.message?.content);
|
|
23
|
+
} catch(e) {
|
|
24
|
+
console.error('Direct call error:', e.message);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Test with empty provider
|
|
28
|
+
try {
|
|
29
|
+
const result2 = await createAiCompletion.call(svc.Tools, '', {
|
|
30
|
+
model: 'claude-3-5-haiku-20241022',
|
|
31
|
+
messages: [{ role: 'user', content: 'Say hello in 5 words' }]
|
|
32
|
+
});
|
|
33
|
+
console.log('Empty provider result:', result2?.choices?.[0]?.message?.content);
|
|
34
|
+
} catch(e) {
|
|
35
|
+
console.error('Empty provider error:', e.message);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
main().catch(console.error).finally(() => process.exit(0));
|
package/test-mcp-args.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test how McpServer's toZodSchema + Object.values parses createAiCompletion args
|
|
3
|
+
*/
|
|
4
|
+
import { McpServerService } from './src/services/McpServer';
|
|
5
|
+
import { ToolsService } from './src/services/Tools';
|
|
6
|
+
import { services } from './src/services';
|
|
7
|
+
|
|
8
|
+
// Simulate the properties from createAiCompletion tool definition
|
|
9
|
+
const createAiCompletionProps = {
|
|
10
|
+
provider: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "The AI provider to use",
|
|
13
|
+
},
|
|
14
|
+
options: {
|
|
15
|
+
type: "object",
|
|
16
|
+
description: "Provider-specific completion options",
|
|
17
|
+
properties: {
|
|
18
|
+
model: { type: "string", description: "The model to use" },
|
|
19
|
+
messages: {
|
|
20
|
+
type: "array",
|
|
21
|
+
description: "The chat history",
|
|
22
|
+
items: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
role: { type: "string" },
|
|
26
|
+
content: { type: "string" },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
max_tokens: { type: "number" },
|
|
31
|
+
},
|
|
32
|
+
required: ["model", "messages"],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const outerRequired = ["provider"];
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
const svc = services();
|
|
40
|
+
const mcpServer = new McpServerService(svc.Tools);
|
|
41
|
+
|
|
42
|
+
// Simulate toZodSchema
|
|
43
|
+
const zodSchema = mcpServer.toZodSchema(createAiCompletionProps as any, outerRequired);
|
|
44
|
+
|
|
45
|
+
// Simulate what MCP SDK passes as args after zod parsing
|
|
46
|
+
const incomingArgs = {
|
|
47
|
+
provider: "",
|
|
48
|
+
options: {
|
|
49
|
+
model: "gpt-5-nano",
|
|
50
|
+
messages: [{ role: "user", content: "hello" }],
|
|
51
|
+
max_tokens: 5000
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const parsed = zodSchema.parse(incomingArgs);
|
|
57
|
+
console.log('Parsed args:', JSON.stringify(parsed, null, 2));
|
|
58
|
+
console.log('Object.values(parsed):', JSON.stringify(Object.values(parsed), null, 2));
|
|
59
|
+
console.log('Keys order:', Object.keys(parsed));
|
|
60
|
+
|
|
61
|
+
// Simulate what McpServer does
|
|
62
|
+
const [providerArg, optionsArg] = Object.values(parsed) as any[];
|
|
63
|
+
console.log('provider arg:', JSON.stringify(providerArg));
|
|
64
|
+
console.log('options arg:', JSON.stringify(optionsArg));
|
|
65
|
+
console.log('options.model:', optionsArg?.model);
|
|
66
|
+
} catch(e) {
|
|
67
|
+
console.error('Zod parse error:', e.message);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
main().catch(console.error).finally(() => process.exit(0));
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test ToolsService.callTool for createAiCompletion directly
|
|
3
|
+
*/
|
|
4
|
+
import { services } from './src/services';
|
|
5
|
+
|
|
6
|
+
async function main() {
|
|
7
|
+
const svc = services();
|
|
8
|
+
|
|
9
|
+
// Wait for services to init
|
|
10
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
11
|
+
|
|
12
|
+
const toolsService = svc.Tools;
|
|
13
|
+
|
|
14
|
+
// Check tool definition
|
|
15
|
+
const toolDef = toolsService.getTool('createAiCompletion');
|
|
16
|
+
console.log('Tool found:', !!toolDef);
|
|
17
|
+
console.log('positional:', toolDef?.function?.parameters?.positional);
|
|
18
|
+
console.log('properties keys:', Object.keys(toolDef?.function?.parameters?.properties || {}));
|
|
19
|
+
|
|
20
|
+
// Simulate what SandboxContext.callTool does
|
|
21
|
+
const toolCall = {
|
|
22
|
+
id: 'test-123',
|
|
23
|
+
type: 'function' as const,
|
|
24
|
+
function: {
|
|
25
|
+
name: 'createAiCompletion',
|
|
26
|
+
arguments: JSON.stringify({
|
|
27
|
+
provider: '',
|
|
28
|
+
options: {
|
|
29
|
+
model: 'claude-3-haiku-20240307',
|
|
30
|
+
messages: [{ role: 'user', content: 'Say hello in 5 words' }]
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const result = await toolsService.callTool(toolCall);
|
|
38
|
+
console.log('Result functionResp:', JSON.stringify(result?.functionResp)?.substring(0, 200));
|
|
39
|
+
console.log('Tool messages content:', result?.toolMessages?.[0]?.content?.substring(0, 200));
|
|
40
|
+
} catch(e) {
|
|
41
|
+
console.error('Error:', e.message);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
main().catch(console.error).finally(() => process.exit(0));
|
package/ts_build/package.json
CHANGED
|
@@ -166,11 +166,17 @@ class BaseAgent {
|
|
|
166
166
|
if (!this.client) {
|
|
167
167
|
if (this.provider) {
|
|
168
168
|
this.log(`Getting client for provider ${this.provider}`);
|
|
169
|
-
|
|
169
|
+
const clientInfo = this.clientService.getClient(this.getProvider());
|
|
170
|
+
if (clientInfo) {
|
|
171
|
+
this.client = clientInfo.client;
|
|
172
|
+
}
|
|
170
173
|
}
|
|
171
174
|
if (!this.client) {
|
|
172
175
|
this.log(`Getting client for model ${this.modelName}`);
|
|
173
|
-
|
|
176
|
+
const clientInfo = this.clientService.getClient(undefined, this.getModel());
|
|
177
|
+
if (clientInfo) {
|
|
178
|
+
this.client = clientInfo.client;
|
|
179
|
+
}
|
|
174
180
|
}
|
|
175
181
|
}
|
|
176
182
|
return this.client;
|