@vfarcic/dot-ai 0.159.0 → 0.161.0
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/README.md +1 -1
- package/dist/core/ai-provider-factory.d.ts.map +1 -1
- package/dist/core/ai-provider-factory.js +7 -0
- package/dist/core/model-config.d.ts +1 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +1 -0
- package/dist/core/providers/host-provider.d.ts +43 -0
- package/dist/core/providers/host-provider.d.ts.map +1 -0
- package/dist/core/providers/host-provider.js +314 -0
- package/dist/core/providers/tool-utils.d.ts +21 -0
- package/dist/core/providers/tool-utils.d.ts.map +1 -0
- package/dist/core/providers/tool-utils.js +57 -0
- package/dist/interfaces/mcp.d.ts +2 -0
- package/dist/interfaces/mcp.d.ts.map +1 -1
- package/dist/interfaces/mcp.js +39 -1
- package/package.json +2 -1
- package/prompts/host-tools.md +15 -0
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ Access curated prompts as native slash commands (`/dot-ai:prompt-name`) in your
|
|
|
87
87
|
📖 [Learn more →](./docs/guides/mcp-prompts-guide.md)
|
|
88
88
|
|
|
89
89
|
### ⚡ AI Integration
|
|
90
|
-
Works with Claude Code, Cursor, VS Code via Model Context Protocol. Supports multiple AI providers (Claude, GPT, Gemini) for flexibility and cost optimization.
|
|
90
|
+
Works with Claude Code, Cursor, VS Code via Model Context Protocol. Supports multiple AI providers (Claude, GPT, Gemini, Host LLM) for flexibility and cost optimization.
|
|
91
91
|
📖 [AI Model Configuration](./docs/setup/mcp-setup.md#ai-model-configuration)
|
|
92
92
|
|
|
93
93
|
## Quick Start
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../src/core/ai-provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EACV,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../src/core/ai-provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EACV,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AA4BjC;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAiB;IAC5B;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IA2BnD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,aAAa,IAAI,UAAU;IAgFlC;;;;;OAKG;IACH,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAMrD;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,IAAI,MAAM,EAAE;IAMxC;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C"}
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.AIProviderFactory = void 0;
|
|
13
13
|
exports.createAIProvider = createAIProvider;
|
|
14
14
|
const vercel_provider_1 = require("./providers/vercel-provider");
|
|
15
|
+
const host_provider_1 = require("./providers/host-provider");
|
|
15
16
|
const noop_provider_1 = require("./providers/noop-provider");
|
|
16
17
|
const model_config_1 = require("./model-config");
|
|
17
18
|
/**
|
|
@@ -53,6 +54,9 @@ class AIProviderFactory {
|
|
|
53
54
|
* @throws Error if provider type is unsupported or configuration is invalid
|
|
54
55
|
*/
|
|
55
56
|
static create(config) {
|
|
57
|
+
if (config.provider === model_config_1.CURRENT_MODELS.host) {
|
|
58
|
+
return new host_provider_1.HostProvider();
|
|
59
|
+
}
|
|
56
60
|
// Validate configuration
|
|
57
61
|
if (!config.apiKey) {
|
|
58
62
|
throw new Error(`API key is required for ${config.provider} provider`);
|
|
@@ -101,6 +105,9 @@ class AIProviderFactory {
|
|
|
101
105
|
// AWS credentials checked at runtime by AWS SDK (env vars, ~/.aws/credentials, IAM roles)
|
|
102
106
|
apiKey = 'bedrock-uses-aws-credentials';
|
|
103
107
|
}
|
|
108
|
+
else if (providerType === model_config_1.CURRENT_MODELS.host) {
|
|
109
|
+
return new host_provider_1.HostProvider();
|
|
110
|
+
}
|
|
104
111
|
else {
|
|
105
112
|
const apiKeyEnvVar = PROVIDER_ENV_KEYS[providerType];
|
|
106
113
|
if (!apiKeyEnvVar) {
|
|
@@ -13,6 +13,7 @@ export declare const CURRENT_MODELS: {
|
|
|
13
13
|
readonly kimi: "kimi-k2-0905-preview";
|
|
14
14
|
readonly kimi_thinking: "kimi-k2-thinking";
|
|
15
15
|
readonly xai: "grok-4";
|
|
16
|
+
readonly host: "host";
|
|
16
17
|
readonly openrouter: "anthropic/claude-haiku-4.5";
|
|
17
18
|
readonly custom: "gpt-5.1-codex";
|
|
18
19
|
readonly amazon_bedrock: "global.anthropic.claude-sonnet-4-20250514-v1:0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-config.d.ts","sourceRoot":"","sources":["../../src/core/model-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"model-config.d.ts","sourceRoot":"","sources":["../../src/core/model-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;CAajB,CAAC;AAEX;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,OAAO,cAAc,GAAG,MAAM,CAE7E"}
|
|
@@ -17,6 +17,7 @@ exports.CURRENT_MODELS = {
|
|
|
17
17
|
kimi: 'kimi-k2-0905-preview', // PRD #237: Moonshot AI Kimi K2 - standard model with 256K context
|
|
18
18
|
kimi_thinking: 'kimi-k2-thinking', // PRD #237: Moonshot AI Kimi K2 - extended thinking variant
|
|
19
19
|
xai: 'grok-4',
|
|
20
|
+
host: 'host', // Delegates generation to the client via MCP Sampling
|
|
20
21
|
openrouter: 'anthropic/claude-haiku-4.5', // PRD #194: OpenRouter default model (overridden by AI_MODEL env var)
|
|
21
22
|
custom: 'gpt-5.1-codex', // PRD #194: Custom endpoint default model (overridden by AI_MODEL env var)
|
|
22
23
|
amazon_bedrock: 'global.anthropic.claude-sonnet-4-20250514-v1:0' // PRD #175: Amazon Bedrock default model (overridden by AI_MODEL env var)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AIProvider, AIResponse, AgenticResult, ToolLoopConfig } from '../ai-provider.interface';
|
|
2
|
+
export interface SamplingMessage {
|
|
3
|
+
role: 'user' | 'assistant';
|
|
4
|
+
content: {
|
|
5
|
+
type: 'text';
|
|
6
|
+
text: string;
|
|
7
|
+
} | string;
|
|
8
|
+
}
|
|
9
|
+
export interface SamplingResult {
|
|
10
|
+
content: {
|
|
11
|
+
type: 'text';
|
|
12
|
+
text: string;
|
|
13
|
+
} | string;
|
|
14
|
+
}
|
|
15
|
+
export type SamplingHandler = (messages: SamplingMessage[], systemPrompt?: string, options?: any) => Promise<SamplingResult>;
|
|
16
|
+
export declare class HostProvider implements AIProvider {
|
|
17
|
+
private static samplingHandler?;
|
|
18
|
+
private debugMode;
|
|
19
|
+
constructor();
|
|
20
|
+
setSamplingHandler(handler: SamplingHandler): void;
|
|
21
|
+
isInitialized(): boolean;
|
|
22
|
+
getDefaultModel(): string;
|
|
23
|
+
getProviderType(): string;
|
|
24
|
+
getModelName(): string;
|
|
25
|
+
private logDebugIfEnabled;
|
|
26
|
+
sendMessage(message: string, operation?: string, evaluationContext?: {
|
|
27
|
+
user_intent?: string;
|
|
28
|
+
interaction_id?: string;
|
|
29
|
+
}): Promise<AIResponse>;
|
|
30
|
+
/**
|
|
31
|
+
* Execute a tool loop with the host model
|
|
32
|
+
*
|
|
33
|
+
* The tool loop relies on a specific JSON format embedded in markdown code blocks:
|
|
34
|
+
* ```json
|
|
35
|
+
* {
|
|
36
|
+
* "tool": "toolName",
|
|
37
|
+
* "arguments": { ... }
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
toolLoop(config: ToolLoopConfig): Promise<AgenticResult>;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=host-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-provider.d.ts","sourceRoot":"","sources":["../../../src/core/providers/host-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACf,MAAM,0BAA0B,CAAC;AAkBlC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,MAAM,eAAe,GAAG,CAC5B,QAAQ,EAAE,eAAe,EAAE,EAC3B,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,GAAG,KACV,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,qBAAa,YAAa,YAAW,UAAU;IAC7C,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAkB;IACjD,OAAO,CAAC,SAAS,CAAU;;IAM3B,kBAAkB,CAAC,OAAO,EAAE,eAAe;IAI3C,aAAa,IAAI,OAAO;IAIxB,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM;IAIzB,YAAY,IAAI,MAAM;IAItB,OAAO,CAAC,iBAAiB;IAwBnB,WAAW,CACf,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAkB,EAC7B,iBAAiB,CAAC,EAAE;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GACA,OAAO,CAAC,UAAU,CAAC;IA4HtB;;;;;;;;;;OAUG;IACG,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA0K/D"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.HostProvider = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const prompts_1 = require("../../tools/prompts");
|
|
39
|
+
const model_config_1 = require("../model-config");
|
|
40
|
+
const provider_debug_utils_1 = require("./provider-debug-utils");
|
|
41
|
+
const ai_tracing_1 = require("../tracing/ai-tracing");
|
|
42
|
+
const tool_utils_1 = require("./tool-utils");
|
|
43
|
+
class HostProvider {
|
|
44
|
+
static samplingHandler;
|
|
45
|
+
debugMode;
|
|
46
|
+
constructor() {
|
|
47
|
+
this.debugMode = process.env.DEBUG_DOT_AI === 'true';
|
|
48
|
+
}
|
|
49
|
+
setSamplingHandler(handler) {
|
|
50
|
+
HostProvider.samplingHandler = handler;
|
|
51
|
+
}
|
|
52
|
+
isInitialized() {
|
|
53
|
+
return !!HostProvider.samplingHandler;
|
|
54
|
+
}
|
|
55
|
+
getDefaultModel() {
|
|
56
|
+
return model_config_1.CURRENT_MODELS.host;
|
|
57
|
+
}
|
|
58
|
+
getProviderType() {
|
|
59
|
+
return model_config_1.CURRENT_MODELS.host;
|
|
60
|
+
}
|
|
61
|
+
getModelName() {
|
|
62
|
+
return model_config_1.CURRENT_MODELS.host;
|
|
63
|
+
}
|
|
64
|
+
logDebugIfEnabled(operation, prompt, response) {
|
|
65
|
+
if (!this.debugMode)
|
|
66
|
+
return null;
|
|
67
|
+
const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
|
|
68
|
+
(0, provider_debug_utils_1.debugLogInteraction)(debugId, prompt, response, operation, this.getProviderType(), this.getModelName(), this.debugMode);
|
|
69
|
+
return {
|
|
70
|
+
promptFile: `${debugId}_prompt.md`,
|
|
71
|
+
responseFile: `${debugId}_response.md`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async sendMessage(message, operation = 'generic', evaluationContext) {
|
|
75
|
+
if (!HostProvider.samplingHandler) {
|
|
76
|
+
throw new Error('Host provider is not connected to MCP server');
|
|
77
|
+
}
|
|
78
|
+
return await (0, ai_tracing_1.withAITracing)({
|
|
79
|
+
provider: this.getProviderType(),
|
|
80
|
+
model: this.getModelName(),
|
|
81
|
+
operation: 'chat',
|
|
82
|
+
}, async () => {
|
|
83
|
+
const startTime = Date.now();
|
|
84
|
+
const messages = [
|
|
85
|
+
{ role: 'user', content: { type: 'text', text: message } },
|
|
86
|
+
];
|
|
87
|
+
try {
|
|
88
|
+
const result = await HostProvider.samplingHandler(messages, undefined, {
|
|
89
|
+
operation,
|
|
90
|
+
evaluationContext,
|
|
91
|
+
});
|
|
92
|
+
let content = '';
|
|
93
|
+
if (typeof result.content === 'object' && result.content.type === 'text') {
|
|
94
|
+
content = result.content.text;
|
|
95
|
+
}
|
|
96
|
+
else if (typeof result.content === 'string') {
|
|
97
|
+
content = result.content;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
content = JSON.stringify(result.content);
|
|
101
|
+
}
|
|
102
|
+
const response = {
|
|
103
|
+
content,
|
|
104
|
+
usage: {
|
|
105
|
+
input_tokens: 0,
|
|
106
|
+
output_tokens: 0,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const durationMs = Date.now() - startTime;
|
|
110
|
+
// Debug log the interaction if enabled
|
|
111
|
+
if (this.debugMode) {
|
|
112
|
+
this.logDebugIfEnabled(operation, message, response);
|
|
113
|
+
const evaluationMetrics = {
|
|
114
|
+
operation,
|
|
115
|
+
sdk: this.getProviderType(),
|
|
116
|
+
inputTokens: response.usage.input_tokens,
|
|
117
|
+
outputTokens: response.usage.output_tokens,
|
|
118
|
+
durationMs,
|
|
119
|
+
iterationCount: 1,
|
|
120
|
+
toolCallCount: 0,
|
|
121
|
+
status: 'completed',
|
|
122
|
+
completionReason: 'stop',
|
|
123
|
+
modelVersion: this.getModelName(),
|
|
124
|
+
test_scenario: operation,
|
|
125
|
+
ai_response_summary: response.content,
|
|
126
|
+
user_intent: evaluationContext?.user_intent || '',
|
|
127
|
+
interaction_id: evaluationContext?.interaction_id || '',
|
|
128
|
+
};
|
|
129
|
+
(0, provider_debug_utils_1.logEvaluationDataset)(evaluationMetrics, this.debugMode);
|
|
130
|
+
}
|
|
131
|
+
return response;
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
135
|
+
if (this.debugMode) {
|
|
136
|
+
const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
|
|
137
|
+
(0, provider_debug_utils_1.debugLogPromptOnly)(debugId, message, operation, this.getProviderType(), this.getModelName(), this.debugMode);
|
|
138
|
+
if (evaluationContext) {
|
|
139
|
+
const failureMetrics = {
|
|
140
|
+
operation,
|
|
141
|
+
user_intent: evaluationContext.user_intent || '',
|
|
142
|
+
ai_response_summary: `Error: ${errorMessage}`,
|
|
143
|
+
durationMs: Date.now() - startTime,
|
|
144
|
+
inputTokens: 0,
|
|
145
|
+
outputTokens: 0,
|
|
146
|
+
iterationCount: 0,
|
|
147
|
+
toolCallCount: 0,
|
|
148
|
+
status: 'failed',
|
|
149
|
+
completionReason: 'error',
|
|
150
|
+
sdk: this.getProviderType(),
|
|
151
|
+
modelVersion: this.getModelName(),
|
|
152
|
+
test_scenario: operation,
|
|
153
|
+
interaction_id: evaluationContext.interaction_id ||
|
|
154
|
+
(0, provider_debug_utils_1.generateDebugId)(operation),
|
|
155
|
+
failure_analysis: {
|
|
156
|
+
failure_type: 'error',
|
|
157
|
+
failure_reason: `Host API error: ${errorMessage}`,
|
|
158
|
+
time_to_failure: Date.now() - startTime,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
(0, provider_debug_utils_1.logEvaluationDataset)(failureMetrics, this.debugMode);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
throw new Error(`Host sampling error: ${errorMessage}`);
|
|
165
|
+
}
|
|
166
|
+
}, (response) => ({
|
|
167
|
+
inputTokens: response.usage.input_tokens,
|
|
168
|
+
outputTokens: response.usage.output_tokens,
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Execute a tool loop with the host model
|
|
173
|
+
*
|
|
174
|
+
* The tool loop relies on a specific JSON format embedded in markdown code blocks:
|
|
175
|
+
* ```json
|
|
176
|
+
* {
|
|
177
|
+
* "tool": "toolName",
|
|
178
|
+
* "arguments": { ... }
|
|
179
|
+
* }
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
async toolLoop(config) {
|
|
183
|
+
if (!HostProvider.samplingHandler) {
|
|
184
|
+
throw new Error('Host provider is not connected to MCP server');
|
|
185
|
+
}
|
|
186
|
+
return await (0, ai_tracing_1.withAITracing)({
|
|
187
|
+
provider: this.getProviderType(),
|
|
188
|
+
model: this.getModelName(),
|
|
189
|
+
operation: 'tool_loop',
|
|
190
|
+
}, async () => {
|
|
191
|
+
const maxIterations = config.maxIterations || 20;
|
|
192
|
+
const messages = [
|
|
193
|
+
{ role: 'user', content: { type: 'text', text: config.userMessage } },
|
|
194
|
+
];
|
|
195
|
+
// Construct system prompt with tool definitions
|
|
196
|
+
const promptPath = path.join(__dirname, '..', '..', '..', 'prompts', 'host-tools.md');
|
|
197
|
+
const promptTemplate = (0, prompts_1.loadPromptFile)(promptPath).content;
|
|
198
|
+
const toolDefinitions = (0, tool_utils_1.formatToolDefinitions)(config.tools);
|
|
199
|
+
const systemPrompt = config.systemPrompt +
|
|
200
|
+
'\n\n' +
|
|
201
|
+
promptTemplate.replace('{{TOOL_DEFINITIONS}}', toolDefinitions);
|
|
202
|
+
const toolCallsExecuted = [];
|
|
203
|
+
let iterations = 0;
|
|
204
|
+
while (iterations < maxIterations) {
|
|
205
|
+
iterations++;
|
|
206
|
+
try {
|
|
207
|
+
const result = await HostProvider.samplingHandler(messages, systemPrompt, {
|
|
208
|
+
operation: config.operation,
|
|
209
|
+
evaluationContext: config.evaluationContext,
|
|
210
|
+
interaction_id: config.interaction_id,
|
|
211
|
+
});
|
|
212
|
+
let content = '';
|
|
213
|
+
if (typeof result.content === 'object' && result.content.type === 'text') {
|
|
214
|
+
content = result.content.text;
|
|
215
|
+
}
|
|
216
|
+
else if (typeof result.content === 'string') {
|
|
217
|
+
content = result.content;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
content = JSON.stringify(result.content);
|
|
221
|
+
}
|
|
222
|
+
// Add assistant response to history
|
|
223
|
+
messages.push({
|
|
224
|
+
role: 'assistant',
|
|
225
|
+
content: { type: 'text', text: content },
|
|
226
|
+
});
|
|
227
|
+
// Check for tool calls
|
|
228
|
+
const toolCalls = (0, tool_utils_1.extractToolCalls)(content);
|
|
229
|
+
if (toolCalls.length > 0) {
|
|
230
|
+
for (const toolCall of toolCalls) {
|
|
231
|
+
try {
|
|
232
|
+
const toolName = toolCall.tool;
|
|
233
|
+
const toolArgs = toolCall.arguments || {};
|
|
234
|
+
// Validate tool exists
|
|
235
|
+
const toolExists = config.tools.some(t => t.name === toolName);
|
|
236
|
+
if (!toolExists) {
|
|
237
|
+
messages.push({
|
|
238
|
+
role: 'user',
|
|
239
|
+
content: {
|
|
240
|
+
type: 'text',
|
|
241
|
+
text: `Unknown tool '${toolName}'. Available tools: ${config.tools.map(t => t.name).join(', ')}`,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
// Execute tool
|
|
247
|
+
const toolOutput = await config.toolExecutor(toolName, toolArgs);
|
|
248
|
+
toolCallsExecuted.push({
|
|
249
|
+
tool: toolName,
|
|
250
|
+
input: toolArgs,
|
|
251
|
+
output: toolOutput,
|
|
252
|
+
});
|
|
253
|
+
// Add tool result to history
|
|
254
|
+
messages.push({
|
|
255
|
+
role: 'user',
|
|
256
|
+
content: {
|
|
257
|
+
type: 'text',
|
|
258
|
+
text: (0, tool_utils_1.formatToolOutput)(toolName, toolOutput),
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
catch (executionError) {
|
|
263
|
+
messages.push({
|
|
264
|
+
role: 'user',
|
|
265
|
+
content: {
|
|
266
|
+
type: 'text',
|
|
267
|
+
text: `Error executing tool '${toolCall.tool}': ${executionError instanceof Error ? executionError.message : String(executionError)}`,
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (config.onIteration) {
|
|
273
|
+
try {
|
|
274
|
+
config.onIteration(iterations, toolCallsExecuted);
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
// Ignore errors in callback
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
// No tool call, assume final response
|
|
283
|
+
return {
|
|
284
|
+
finalMessage: content,
|
|
285
|
+
iterations,
|
|
286
|
+
toolCallsExecuted,
|
|
287
|
+
totalTokens: { input: 0, output: 0 },
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
293
|
+
throw new Error(`Host sampling error in tool loop: ${message}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const lastMessage = messages[messages.length - 1];
|
|
297
|
+
const lastContent = typeof lastMessage.content === 'string'
|
|
298
|
+
? lastMessage.content
|
|
299
|
+
: (lastMessage.content?.text ?? '');
|
|
300
|
+
return {
|
|
301
|
+
finalMessage: lastContent,
|
|
302
|
+
iterations,
|
|
303
|
+
toolCallsExecuted,
|
|
304
|
+
totalTokens: { input: 0, output: 0 },
|
|
305
|
+
status: 'timeout',
|
|
306
|
+
completionReason: 'max_iterations',
|
|
307
|
+
};
|
|
308
|
+
}, (result) => ({
|
|
309
|
+
inputTokens: result.totalTokens.input,
|
|
310
|
+
outputTokens: result.totalTokens.output,
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
exports.HostProvider = HostProvider;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AITool } from '../ai-provider.interface';
|
|
2
|
+
/**
|
|
3
|
+
* Formats tool definitions into a markdown string for system prompts.
|
|
4
|
+
* Used by providers that don't support native tool calling or need manual prompting.
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatToolDefinitions(tools: AITool[]): string;
|
|
7
|
+
/**
|
|
8
|
+
* Formats a tool execution result for inclusion in conversation history.
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatToolOutput(toolName: string, output: any): string;
|
|
11
|
+
/**
|
|
12
|
+
* Regex for extracting tool calls from markdown code blocks.
|
|
13
|
+
* Matches: ```json ... ``` and captures the content.
|
|
14
|
+
*/
|
|
15
|
+
export declare const TOOL_CALL_REGEX: RegExp;
|
|
16
|
+
/**
|
|
17
|
+
* Extracts tool calls from a string containing markdown code blocks.
|
|
18
|
+
* Handles nested objects and malformed JSON gracefully.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractToolCalls(content: string): any[];
|
|
21
|
+
//# sourceMappingURL=tool-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-utils.d.ts","sourceRoot":"","sources":["../../../src/core/providers/tool-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAElD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAO7D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,MAAM,CAEtE;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAgC,CAAC;AAE7D;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,EAAE,CAuBvD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TOOL_CALL_REGEX = void 0;
|
|
4
|
+
exports.formatToolDefinitions = formatToolDefinitions;
|
|
5
|
+
exports.formatToolOutput = formatToolOutput;
|
|
6
|
+
exports.extractToolCalls = extractToolCalls;
|
|
7
|
+
/**
|
|
8
|
+
* Formats tool definitions into a markdown string for system prompts.
|
|
9
|
+
* Used by providers that don't support native tool calling or need manual prompting.
|
|
10
|
+
*/
|
|
11
|
+
function formatToolDefinitions(tools) {
|
|
12
|
+
let toolDefinitions = '';
|
|
13
|
+
for (const tool of tools) {
|
|
14
|
+
toolDefinitions += `### ${tool.name}\n${tool.description}\n`;
|
|
15
|
+
toolDefinitions += `Schema: ${JSON.stringify(tool.inputSchema)}\n\n`;
|
|
16
|
+
}
|
|
17
|
+
return toolDefinitions;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Formats a tool execution result for inclusion in conversation history.
|
|
21
|
+
*/
|
|
22
|
+
function formatToolOutput(toolName, output) {
|
|
23
|
+
return `Tool '${toolName}' output:\n${JSON.stringify(output, null, 2)}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Regex for extracting tool calls from markdown code blocks.
|
|
27
|
+
* Matches: ```json ... ``` and captures the content.
|
|
28
|
+
*/
|
|
29
|
+
exports.TOOL_CALL_REGEX = /```json\s*([\s\S]*?)\s*```/g;
|
|
30
|
+
/**
|
|
31
|
+
* Extracts tool calls from a string containing markdown code blocks.
|
|
32
|
+
* Handles nested objects and malformed JSON gracefully.
|
|
33
|
+
*/
|
|
34
|
+
function extractToolCalls(content) {
|
|
35
|
+
const toolCalls = [];
|
|
36
|
+
const matches = [...content.matchAll(exports.TOOL_CALL_REGEX)];
|
|
37
|
+
for (const match of matches) {
|
|
38
|
+
try {
|
|
39
|
+
const jsonContent = match[1];
|
|
40
|
+
const parsed = JSON.parse(jsonContent);
|
|
41
|
+
if (Array.isArray(parsed)) {
|
|
42
|
+
for (const item of parsed) {
|
|
43
|
+
if (item && typeof item === 'object' && item.tool) {
|
|
44
|
+
toolCalls.push(item);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (parsed && typeof parsed === 'object' && parsed.tool) {
|
|
49
|
+
toolCalls.push(parsed);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
// Ignore parse errors
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return toolCalls;
|
|
57
|
+
}
|
package/dist/interfaces/mcp.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export declare class MCPServer {
|
|
|
39
39
|
* Register prompts capability with McpServer
|
|
40
40
|
*/
|
|
41
41
|
private registerPrompts;
|
|
42
|
+
private configureHostProvider;
|
|
43
|
+
private handleSamplingRequest;
|
|
42
44
|
private generateRequestId;
|
|
43
45
|
start(): Promise<void>;
|
|
44
46
|
private startStdioTransport;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/interfaces/mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAkDtC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;CACxC;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,UAAU,CAAC,CAAkC;IACrD,OAAO,CAAC,aAAa,CAAC,CAAgC;IACtD,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/interfaces/mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAkDtC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;CACxC;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,UAAU,CAAC,CAAkC;IACrD,OAAO,CAAC,aAAa,CAAC,CAAgC;IACtD,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe;IAyCjD;;OAEG;IACH,OAAO,CAAC,YAAY;IA8BpB;;OAEG;IACH,OAAO,CAAC,aAAa;IA6HrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAqCvB,OAAO,CAAC,qBAAqB;YAgBf,qBAAqB;IAuBnC,OAAO,CAAC,iBAAiB;IAInB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAkBd,mBAAmB;YAMnB,kBAAkB;YAmHlB,gBAAgB;IAexB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,OAAO,IAAI,OAAO;CAGnB"}
|
package/dist/interfaces/mcp.js
CHANGED
|
@@ -52,6 +52,8 @@ class MCPServer {
|
|
|
52
52
|
prompts: {},
|
|
53
53
|
},
|
|
54
54
|
});
|
|
55
|
+
// Configure HostProvider if active
|
|
56
|
+
this.configureHostProvider();
|
|
55
57
|
this.logger.info('Initializing MCP Server', {
|
|
56
58
|
name: config.name,
|
|
57
59
|
version: config.version,
|
|
@@ -73,7 +75,10 @@ class MCPServer {
|
|
|
73
75
|
return await (0, tracing_1.withToolTracing)(name, args, handler);
|
|
74
76
|
};
|
|
75
77
|
// Register traced handler with MCP server
|
|
76
|
-
this.server.
|
|
78
|
+
this.server.registerTool(name, {
|
|
79
|
+
description,
|
|
80
|
+
inputSchema
|
|
81
|
+
}, tracedHandler);
|
|
77
82
|
// Register traced handler with REST registry
|
|
78
83
|
this.restRegistry.registerTool({
|
|
79
84
|
name,
|
|
@@ -164,6 +169,39 @@ class MCPServer {
|
|
|
164
169
|
endpoints: ['prompts/list', 'prompts/get'],
|
|
165
170
|
});
|
|
166
171
|
}
|
|
172
|
+
configureHostProvider() {
|
|
173
|
+
// Configure HostProvider if active
|
|
174
|
+
// We use capability detection (duck typing) to avoid strict class dependency
|
|
175
|
+
// and handle potential class loading issues
|
|
176
|
+
const aiProvider = this.dotAI.ai;
|
|
177
|
+
if (typeof aiProvider.setSamplingHandler === 'function') {
|
|
178
|
+
this.logger.info('Configuring Host AI Provider with Sampling capability');
|
|
179
|
+
aiProvider.setSamplingHandler(this.handleSamplingRequest.bind(this));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
this.logger.info('Using configured AI Provider', {
|
|
183
|
+
type: this.dotAI.ai.getProviderType ? this.dotAI.ai.getProviderType() : 'unknown'
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async handleSamplingRequest(messages, systemPrompt, options) {
|
|
188
|
+
try {
|
|
189
|
+
if (!this.server.server.createMessage) {
|
|
190
|
+
throw new Error('Server does not support createMessage (sampling)');
|
|
191
|
+
}
|
|
192
|
+
return await this.server.server.createMessage({
|
|
193
|
+
messages,
|
|
194
|
+
systemPrompt,
|
|
195
|
+
includeContext: 'none',
|
|
196
|
+
maxTokens: options?.maxTokens || 4096,
|
|
197
|
+
...options
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
this.logger.error('Sampling request failed', error);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
167
205
|
generateRequestId() {
|
|
168
206
|
return `mcp_${Date.now()}_${++this.requestIdCounter}`;
|
|
169
207
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vfarcic/dot-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.161.0",
|
|
4
4
|
"description": "AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance",
|
|
5
5
|
"mcpName": "io.github.vfarcic/dot-ai",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"test:integration:teardown": "./tests/integration/infrastructure/teardown-cluster.sh",
|
|
18
18
|
"test:integration:server": "KUBECONFIG=./kubeconfig-test.yaml PORT=3456 DOT_AI_SESSION_DIR=./tmp/sessions TRANSPORT_TYPE=http QDRANT_URL=http://localhost:6335 QDRANT_CAPABILITIES_COLLECTION=capabilities-policies ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY OPENAI_API_KEY=$OPENAI_API_KEY node dist/mcp/server.js",
|
|
19
19
|
"test:integration": "./tests/integration/infrastructure/run-integration-tests.sh",
|
|
20
|
+
"test:unit": "vitest --config=vitest.unit.config.ts",
|
|
20
21
|
"test:integration:watch": "vitest --config=vitest.integration.config.ts --test-timeout=1200000",
|
|
21
22
|
"test:integration:sonnet": "AI_PROVIDER=anthropic DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
22
23
|
"test:integration:opus": "AI_PROVIDER=anthropic_opus DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: host-tools
|
|
3
|
+
description: System prompt for host tools execution loop
|
|
4
|
+
category: core
|
|
5
|
+
---
|
|
6
|
+
## Available Tools
|
|
7
|
+
|
|
8
|
+
You have access to the following tools. To use a tool, output a JSON block with the format:
|
|
9
|
+
```json
|
|
10
|
+
{ "tool": "tool_name", "arguments": { ... } }
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
{{TOOL_DEFINITIONS}}
|
|
14
|
+
|
|
15
|
+
When you have gathered enough information, provide your final answer without any tool calls.
|