@ftarganski/omni-ai 0.1.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/dist/chunk-3RZELMF2.js +214 -0
- package/dist/chunk-5WELBZWN.js +70 -0
- package/dist/chunk-6APAA6WD.js +495 -0
- package/dist/chunk-6OPRALDC.js +163 -0
- package/dist/chunk-6YFFZMXY.js +104 -0
- package/dist/chunk-AG6NZIJ3.js +122 -0
- package/dist/chunk-AWMSN7OB.js +451 -0
- package/dist/chunk-JTXDF5KG.js +156 -0
- package/dist/chunk-M4QJF7CV.js +57 -0
- package/dist/chunk-PPTEJ2FH.js +102 -0
- package/dist/chunk-S5MK6LBH.js +136 -0
- package/dist/chunk-TFU437SW.js +107 -0
- package/dist/chunk-Y4EYGADJ.js +216 -0
- package/dist/cli/bin.js +2723 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +42 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +86 -0
- package/dist/memory.d.ts +1 -0
- package/dist/memory.js +320 -0
- package/dist/provider-anthropic.d.ts +1 -0
- package/dist/provider-anthropic.js +120 -0
- package/dist/provider-google.d.ts +1 -0
- package/dist/provider-google.js +141 -0
- package/dist/provider-openai.d.ts +1 -0
- package/dist/provider-openai.js +214 -0
- package/dist/skills/backend.d.ts +1 -0
- package/dist/skills/backend.js +12 -0
- package/dist/skills/code.d.ts +1 -0
- package/dist/skills/code.js +6 -0
- package/dist/skills/frontend.d.ts +1 -0
- package/dist/skills/frontend.js +10 -0
- package/dist/skills/fs.d.ts +1 -0
- package/dist/skills/fs.js +10 -0
- package/dist/skills/git.d.ts +1 -0
- package/dist/skills/git.js +12 -0
- package/dist/skills/http.d.ts +1 -0
- package/dist/skills/http.js +6 -0
- package/dist/skills/index.d.ts +1 -0
- package/dist/skills/index.js +60 -0
- package/dist/skills/multimodal.d.ts +1 -0
- package/dist/skills/multimodal.js +6 -0
- package/dist/skills/qa.d.ts +1 -0
- package/dist/skills/qa.js +8 -0
- package/dist/skills/ux.d.ts +1 -0
- package/dist/skills/ux.js +6 -0
- package/dist/src-6MUVU5ML.js +8 -0
- package/dist/src-ZHTGR7T6.js +8 -0
- package/package.json +136 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// ../skills/src/http/http-request.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var BearerAuthSchema = z.object({
|
|
4
|
+
type: z.literal("bearer"),
|
|
5
|
+
token: z.string().describe("Bearer token value")
|
|
6
|
+
});
|
|
7
|
+
var BasicAuthSchema = z.object({
|
|
8
|
+
type: z.literal("basic"),
|
|
9
|
+
username: z.string(),
|
|
10
|
+
password: z.string()
|
|
11
|
+
});
|
|
12
|
+
var OAuth2ClientSchema = z.object({
|
|
13
|
+
type: z.literal("oauth2-client-credentials"),
|
|
14
|
+
tokenUrl: z.string().url().describe("Token endpoint URL"),
|
|
15
|
+
clientId: z.string(),
|
|
16
|
+
clientSecret: z.string(),
|
|
17
|
+
scope: z.string().optional().describe("Space-separated OAuth2 scopes")
|
|
18
|
+
});
|
|
19
|
+
var AuthSchema = z.discriminatedUnion("type", [BearerAuthSchema, BasicAuthSchema, OAuth2ClientSchema]);
|
|
20
|
+
var InputSchema = z.object({
|
|
21
|
+
url: z.string().url().describe("Target URL"),
|
|
22
|
+
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]).default("GET").describe("HTTP method"),
|
|
23
|
+
headers: z.record(z.string()).optional().describe("Additional request headers"),
|
|
24
|
+
body: z.union([z.string(), z.record(z.unknown())]).optional().describe("Request body \u2014 string or JSON object"),
|
|
25
|
+
auth: AuthSchema.optional().describe("Authentication configuration"),
|
|
26
|
+
timeoutMs: z.number().int().positive().default(3e4).describe("Request timeout in milliseconds")
|
|
27
|
+
});
|
|
28
|
+
async function resolveToken(auth) {
|
|
29
|
+
if (auth.type === "bearer") return `Bearer ${auth.token}`;
|
|
30
|
+
if (auth.type === "basic") {
|
|
31
|
+
const encoded = Buffer.from(`${auth.username}:${auth.password}`).toString("base64");
|
|
32
|
+
return `Basic ${encoded}`;
|
|
33
|
+
}
|
|
34
|
+
const params = new URLSearchParams({
|
|
35
|
+
grant_type: "client_credentials",
|
|
36
|
+
client_id: auth.clientId,
|
|
37
|
+
client_secret: auth.clientSecret
|
|
38
|
+
});
|
|
39
|
+
if (auth.scope) params.set("scope", auth.scope);
|
|
40
|
+
const tokenRes = await fetch(auth.tokenUrl, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
43
|
+
body: params.toString()
|
|
44
|
+
});
|
|
45
|
+
if (!tokenRes.ok) {
|
|
46
|
+
throw new Error(`OAuth2 token request failed: ${tokenRes.status} ${tokenRes.statusText}`);
|
|
47
|
+
}
|
|
48
|
+
const json = await tokenRes.json();
|
|
49
|
+
if (!json.access_token) throw new Error("OAuth2 response did not include access_token");
|
|
50
|
+
const tokenType = json.token_type ?? "Bearer";
|
|
51
|
+
return `${tokenType} ${json.access_token}`;
|
|
52
|
+
}
|
|
53
|
+
var httpRequestSkill = {
|
|
54
|
+
name: "http-request",
|
|
55
|
+
description: "Make an authenticated HTTP request to any URL. Supports Bearer token, HTTP Basic, and OAuth2 client-credentials flows. Returns status, headers and body. Use this to call external REST APIs from an agent.",
|
|
56
|
+
async execute(input) {
|
|
57
|
+
const { url, method, headers: extraHeaders, body, auth, timeoutMs } = InputSchema.parse(input);
|
|
58
|
+
const headers = {
|
|
59
|
+
Accept: "application/json",
|
|
60
|
+
...extraHeaders
|
|
61
|
+
};
|
|
62
|
+
if (auth) {
|
|
63
|
+
headers.Authorization = await resolveToken(auth);
|
|
64
|
+
}
|
|
65
|
+
let bodyStr;
|
|
66
|
+
if (body !== void 0) {
|
|
67
|
+
if (typeof body === "string") {
|
|
68
|
+
bodyStr = body;
|
|
69
|
+
} else {
|
|
70
|
+
bodyStr = JSON.stringify(body);
|
|
71
|
+
headers["Content-Type"] ??= "application/json";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const controller = new AbortController();
|
|
75
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
76
|
+
let response;
|
|
77
|
+
try {
|
|
78
|
+
response = await fetch(url, {
|
|
79
|
+
method,
|
|
80
|
+
headers,
|
|
81
|
+
body: bodyStr,
|
|
82
|
+
signal: controller.signal
|
|
83
|
+
});
|
|
84
|
+
} finally {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
}
|
|
87
|
+
const responseHeaders = {};
|
|
88
|
+
response.headers.forEach((value, key) => {
|
|
89
|
+
responseHeaders[key] = value;
|
|
90
|
+
});
|
|
91
|
+
const responseBody = await response.text();
|
|
92
|
+
return {
|
|
93
|
+
status: response.status,
|
|
94
|
+
statusText: response.statusText,
|
|
95
|
+
headers: responseHeaders,
|
|
96
|
+
body: responseBody,
|
|
97
|
+
ok: response.ok
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
httpRequestSkill
|
|
104
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
registerProvider
|
|
4
|
+
} from "./chunk-AWMSN7OB.js";
|
|
5
|
+
|
|
6
|
+
// ../provider-anthropic/src/provider.ts
|
|
7
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
8
|
+
|
|
9
|
+
// ../provider-anthropic/src/mappers.ts
|
|
10
|
+
function toAnthropicContentPart(part) {
|
|
11
|
+
if (part.type === "text") return { type: "text", text: part.text };
|
|
12
|
+
return {
|
|
13
|
+
type: "image",
|
|
14
|
+
source: { type: "base64", media_type: part.mimeType, data: part.data }
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function toAnthropicMessages(messages) {
|
|
18
|
+
return messages.filter((m) => m.role !== "system").map((m) => ({
|
|
19
|
+
role: m.role,
|
|
20
|
+
content: typeof m.content === "string" ? m.content : m.content.map(toAnthropicContentPart)
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
function toAnthropicTools(tools) {
|
|
24
|
+
return tools.map((t) => ({
|
|
25
|
+
name: t.name,
|
|
26
|
+
description: t.description,
|
|
27
|
+
input_schema: t.parameters
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
function extractSystemPrompt(request) {
|
|
31
|
+
const systemMsg = request.messages.find((m) => m.role === "system");
|
|
32
|
+
const raw = request.systemPrompt ?? systemMsg?.content;
|
|
33
|
+
if (raw === void 0) return void 0;
|
|
34
|
+
return typeof raw === "string" ? raw : raw.map((p) => p.type === "text" ? p.text : "").join("");
|
|
35
|
+
}
|
|
36
|
+
function fromAnthropicResponse(response, providerName) {
|
|
37
|
+
let content = "";
|
|
38
|
+
const toolCalls = [];
|
|
39
|
+
for (const block of response.content) {
|
|
40
|
+
if (block.type === "text") {
|
|
41
|
+
content += block.text;
|
|
42
|
+
} else if (block.type === "tool_use") {
|
|
43
|
+
toolCalls.push({
|
|
44
|
+
id: block.id,
|
|
45
|
+
name: block.name,
|
|
46
|
+
arguments: block.input
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
content,
|
|
52
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
53
|
+
usage: {
|
|
54
|
+
inputTokens: response.usage.input_tokens,
|
|
55
|
+
outputTokens: response.usage.output_tokens
|
|
56
|
+
},
|
|
57
|
+
model: response.model,
|
|
58
|
+
provider: providerName
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ../provider-anthropic/src/provider.ts
|
|
63
|
+
var AnthropicProvider = class {
|
|
64
|
+
name;
|
|
65
|
+
capabilities = {
|
|
66
|
+
chat: true,
|
|
67
|
+
embedding: false,
|
|
68
|
+
streaming: true,
|
|
69
|
+
toolUse: true,
|
|
70
|
+
vision: true
|
|
71
|
+
};
|
|
72
|
+
client;
|
|
73
|
+
defaultModel;
|
|
74
|
+
constructor(options) {
|
|
75
|
+
this.name = options.name ?? "anthropic";
|
|
76
|
+
this.defaultModel = options.defaultModel ?? "claude-sonnet-4-6";
|
|
77
|
+
this.client = new Anthropic({ apiKey: options.apiKey });
|
|
78
|
+
}
|
|
79
|
+
async complete(request) {
|
|
80
|
+
const model = request.model ?? this.defaultModel;
|
|
81
|
+
if (request.temperature !== void 0 && request.temperature > 1) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Anthropic models require temperature <= 1.0 (got ${request.temperature}). Update the agent YAML or use an OpenAI provider for higher temperature values.`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
const system = extractSystemPrompt(request);
|
|
87
|
+
const messages = toAnthropicMessages(request.messages);
|
|
88
|
+
const tools = request.tools && request.tools.length > 0 ? toAnthropicTools(request.tools) : void 0;
|
|
89
|
+
const params = {
|
|
90
|
+
model,
|
|
91
|
+
max_tokens: request.maxTokens ?? 8096,
|
|
92
|
+
temperature: request.temperature,
|
|
93
|
+
system,
|
|
94
|
+
messages,
|
|
95
|
+
tools
|
|
96
|
+
};
|
|
97
|
+
if (request.onToken) {
|
|
98
|
+
const stream = this.client.messages.stream(params);
|
|
99
|
+
stream.on("text", request.onToken);
|
|
100
|
+
const finalMessage = await stream.finalMessage();
|
|
101
|
+
return fromAnthropicResponse(finalMessage, this.name);
|
|
102
|
+
}
|
|
103
|
+
const response = await this.client.messages.create(params);
|
|
104
|
+
return fromAnthropicResponse(response, this.name);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// ../provider-anthropic/src/index.ts
|
|
109
|
+
registerProvider("anthropic", (config) => {
|
|
110
|
+
if (!config.apiKey) {
|
|
111
|
+
throw new Error(`Provider "${config.name}" (anthropic) requires ANTHROPIC_API_KEY to be set in the environment.`);
|
|
112
|
+
}
|
|
113
|
+
return new AnthropicProvider({
|
|
114
|
+
apiKey: config.apiKey,
|
|
115
|
+
defaultModel: config.defaultModel,
|
|
116
|
+
name: config.name
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
AnthropicProvider
|
|
122
|
+
};
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../core/src/providers/fallback.ts
|
|
4
|
+
var FallbackProvider = class {
|
|
5
|
+
name;
|
|
6
|
+
capabilities;
|
|
7
|
+
providers;
|
|
8
|
+
constructor(providers) {
|
|
9
|
+
if (providers.length === 0) throw new Error("FallbackProvider requires at least one provider");
|
|
10
|
+
this.providers = providers;
|
|
11
|
+
this.name = providers.map((p) => p.name).join("|");
|
|
12
|
+
this.capabilities = providers.reduce(
|
|
13
|
+
(acc, p) => ({
|
|
14
|
+
chat: acc.chat || p.capabilities.chat,
|
|
15
|
+
embedding: acc.embedding || p.capabilities.embedding,
|
|
16
|
+
streaming: acc.streaming || p.capabilities.streaming,
|
|
17
|
+
toolUse: acc.toolUse || p.capabilities.toolUse,
|
|
18
|
+
vision: acc.vision || p.capabilities.vision
|
|
19
|
+
}),
|
|
20
|
+
{ chat: false, embedding: false, streaming: false, toolUse: false, vision: false }
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
async complete(request) {
|
|
24
|
+
const errors = [];
|
|
25
|
+
for (const provider of this.providers) {
|
|
26
|
+
try {
|
|
27
|
+
return await provider.complete(request);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
throw new Error(
|
|
33
|
+
`All providers failed:
|
|
34
|
+
${errors.map((e, i) => ` [${this.providers[i].name}] ${e.message}`).join("\n")}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
async embed(request) {
|
|
38
|
+
const errors = [];
|
|
39
|
+
for (const provider of this.providers) {
|
|
40
|
+
if (!provider.embed) continue;
|
|
41
|
+
try {
|
|
42
|
+
return await provider.embed(request);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (errors.length === 0) throw new Error(`No provider in "${this.name}" supports embeddings`);
|
|
48
|
+
throw new Error(
|
|
49
|
+
`All providers failed for embed:
|
|
50
|
+
${errors.map((e, i) => ` [${this.providers[i].name}] ${e.message}`).join("\n")}`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ../core/src/providers/retry.ts
|
|
56
|
+
function isRetryable(err) {
|
|
57
|
+
if (!(err instanceof Error)) return false;
|
|
58
|
+
const msg = err.message.toLowerCase();
|
|
59
|
+
return msg.includes("429") || msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("504") || msg.includes("econnreset") || msg.includes("etimedout") || msg.includes("enotfound");
|
|
60
|
+
}
|
|
61
|
+
function delay(ms) {
|
|
62
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
63
|
+
}
|
|
64
|
+
var RetryProvider = class {
|
|
65
|
+
name;
|
|
66
|
+
capabilities;
|
|
67
|
+
inner;
|
|
68
|
+
maxRetries;
|
|
69
|
+
initialDelayMs;
|
|
70
|
+
maxDelayMs;
|
|
71
|
+
constructor(inner, options = {}) {
|
|
72
|
+
this.inner = inner;
|
|
73
|
+
this.name = inner.name;
|
|
74
|
+
this.capabilities = inner.capabilities;
|
|
75
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
76
|
+
this.initialDelayMs = options.initialDelayMs ?? 500;
|
|
77
|
+
this.maxDelayMs = options.maxDelayMs ?? 3e4;
|
|
78
|
+
}
|
|
79
|
+
async complete(request) {
|
|
80
|
+
return this.withRetry(() => this.inner.complete(request));
|
|
81
|
+
}
|
|
82
|
+
async embed(request) {
|
|
83
|
+
const embedFn = this.inner.embed;
|
|
84
|
+
if (!embedFn) throw new Error(`Provider "${this.name}" does not support embeddings`);
|
|
85
|
+
return this.withRetry(() => embedFn.call(this.inner, request));
|
|
86
|
+
}
|
|
87
|
+
async withRetry(fn) {
|
|
88
|
+
let attempt = 0;
|
|
89
|
+
while (true) {
|
|
90
|
+
try {
|
|
91
|
+
return await fn();
|
|
92
|
+
} catch (err) {
|
|
93
|
+
attempt++;
|
|
94
|
+
if (attempt > this.maxRetries || !isRetryable(err)) throw err;
|
|
95
|
+
const base = this.initialDelayMs * 2 ** (attempt - 1);
|
|
96
|
+
const jitter = Math.random() * base * 0.2;
|
|
97
|
+
const wait = Math.min(base + jitter, this.maxDelayMs);
|
|
98
|
+
await delay(wait);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// ../core/src/providers/registry.ts
|
|
105
|
+
var factories = /* @__PURE__ */ new Map();
|
|
106
|
+
function registerProvider(type, factory) {
|
|
107
|
+
factories.set(type, factory);
|
|
108
|
+
}
|
|
109
|
+
function createProvider(config) {
|
|
110
|
+
const factory = factories.get(config.type);
|
|
111
|
+
if (!factory) {
|
|
112
|
+
throw new Error(`Unknown provider type "${config.type}". Registered: ${[...factories.keys()].join(", ")}`);
|
|
113
|
+
}
|
|
114
|
+
return factory(config);
|
|
115
|
+
}
|
|
116
|
+
function buildProvider(cfg, allConfigs) {
|
|
117
|
+
let provider = createProvider(cfg);
|
|
118
|
+
if (cfg.retry) {
|
|
119
|
+
provider = new RetryProvider(provider, cfg.retry);
|
|
120
|
+
}
|
|
121
|
+
if (cfg.fallback) {
|
|
122
|
+
const fallbackCfg = allConfigs.find((p) => p.name === cfg.fallback);
|
|
123
|
+
if (!fallbackCfg) {
|
|
124
|
+
throw new Error(`Fallback provider "${cfg.fallback}" not found in config for provider "${cfg.name}"`);
|
|
125
|
+
}
|
|
126
|
+
let fallbackProvider = createProvider(fallbackCfg);
|
|
127
|
+
if (fallbackCfg.retry) {
|
|
128
|
+
fallbackProvider = new RetryProvider(fallbackProvider, fallbackCfg.retry);
|
|
129
|
+
}
|
|
130
|
+
provider = new FallbackProvider([provider, fallbackProvider]);
|
|
131
|
+
}
|
|
132
|
+
return provider;
|
|
133
|
+
}
|
|
134
|
+
function getRegisteredProviders() {
|
|
135
|
+
return [...factories.keys()];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ../core/src/middleware/compose.ts
|
|
139
|
+
function composeMiddleware(fns, skill) {
|
|
140
|
+
if (fns.length === 0) return skill;
|
|
141
|
+
return {
|
|
142
|
+
name: skill.name,
|
|
143
|
+
description: skill.description,
|
|
144
|
+
execute(input, ctx) {
|
|
145
|
+
let index = 0;
|
|
146
|
+
const dispatch = () => {
|
|
147
|
+
if (index < fns.length) {
|
|
148
|
+
const fn = fns[index++];
|
|
149
|
+
return fn(skill.name, input, ctx, dispatch);
|
|
150
|
+
}
|
|
151
|
+
return skill.execute(input, ctx);
|
|
152
|
+
};
|
|
153
|
+
return dispatch();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ../core/src/types.ts
|
|
159
|
+
function contentToString(content) {
|
|
160
|
+
if (typeof content === "string") return content;
|
|
161
|
+
return content.map((p) => p.type === "text" ? p.text : `[image:${p.mimeType}]`).join("");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ../core/src/agents/agent.ts
|
|
165
|
+
var Agent = class {
|
|
166
|
+
config;
|
|
167
|
+
provider;
|
|
168
|
+
skills;
|
|
169
|
+
constructor(config, provider, skills = []) {
|
|
170
|
+
this.config = config;
|
|
171
|
+
this.provider = provider;
|
|
172
|
+
this.skills = new Map(skills.map((s) => [s.name, s]));
|
|
173
|
+
}
|
|
174
|
+
async run(options) {
|
|
175
|
+
const memCfg = this.config.memory;
|
|
176
|
+
const session = options.session;
|
|
177
|
+
const store = memCfg?.store;
|
|
178
|
+
const compactor = memCfg?.compactor;
|
|
179
|
+
const maxCtxTokens = memCfg?.maxContextTokens ?? 8e4;
|
|
180
|
+
const history = session && store ? (await store.loadMessages(session)).map((e) => ({ role: e.role, content: e.content })) : [];
|
|
181
|
+
const messages = [...history];
|
|
182
|
+
const newMessages = [];
|
|
183
|
+
const firstMsg = { role: "user", content: options.input };
|
|
184
|
+
messages.push(firstMsg);
|
|
185
|
+
newMessages.push(firstMsg);
|
|
186
|
+
const tools = this.buildToolDefinitions();
|
|
187
|
+
const ctx = { provider: this.provider, config: options.context ?? {} };
|
|
188
|
+
const maxIterations = this.config.maxIterations ?? 10;
|
|
189
|
+
let iterations = 0;
|
|
190
|
+
let totalInput = 0;
|
|
191
|
+
let totalOutput = 0;
|
|
192
|
+
while (iterations < maxIterations) {
|
|
193
|
+
iterations++;
|
|
194
|
+
if (compactor?.shouldCompact(messages, maxCtxTokens)) {
|
|
195
|
+
const compacted = await compactor.compact(messages, this.provider);
|
|
196
|
+
messages.splice(0, messages.length, ...compacted);
|
|
197
|
+
}
|
|
198
|
+
const response = await this.provider.complete({
|
|
199
|
+
messages,
|
|
200
|
+
model: this.config.model,
|
|
201
|
+
systemPrompt: this.config.systemPrompt,
|
|
202
|
+
temperature: this.config.temperature,
|
|
203
|
+
tools: tools.length > 0 ? tools : void 0,
|
|
204
|
+
onToken: options.onToken
|
|
205
|
+
});
|
|
206
|
+
if (response.usage) {
|
|
207
|
+
totalInput += response.usage.inputTokens;
|
|
208
|
+
totalOutput += response.usage.outputTokens;
|
|
209
|
+
}
|
|
210
|
+
if (!response.toolCalls || response.toolCalls.length === 0) {
|
|
211
|
+
const finalMsg = { role: "assistant", content: response.content };
|
|
212
|
+
messages.push(finalMsg);
|
|
213
|
+
newMessages.push(finalMsg);
|
|
214
|
+
await this.persist(store, session, newMessages);
|
|
215
|
+
return {
|
|
216
|
+
output: response.content,
|
|
217
|
+
iterations,
|
|
218
|
+
usage: { inputTokens: totalInput, outputTokens: totalOutput }
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const assistantMsg = { role: "assistant", content: response.content };
|
|
222
|
+
messages.push(assistantMsg);
|
|
223
|
+
newMessages.push(assistantMsg);
|
|
224
|
+
for (const call of response.toolCalls) {
|
|
225
|
+
const result = await this.executeToolCall(call, ctx);
|
|
226
|
+
const toolMsg = { role: "user", content: `[Tool ${call.name} result]: ${result}` };
|
|
227
|
+
messages.push(toolMsg);
|
|
228
|
+
newMessages.push(toolMsg);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
await this.persist(store, session, newMessages);
|
|
232
|
+
throw new Error(`Agent "${this.config.name}" exceeded maxIterations (${maxIterations})`);
|
|
233
|
+
}
|
|
234
|
+
async persist(store, session, messages) {
|
|
235
|
+
if (!store || !session || messages.length === 0) return;
|
|
236
|
+
const entries = messages.map((m) => ({
|
|
237
|
+
role: m.role,
|
|
238
|
+
content: contentToString(m.content),
|
|
239
|
+
timestamp: Date.now()
|
|
240
|
+
}));
|
|
241
|
+
await store.saveMessages(session, entries);
|
|
242
|
+
}
|
|
243
|
+
buildToolDefinitions() {
|
|
244
|
+
return [...this.skills.values()].map((skill) => ({
|
|
245
|
+
name: skill.name,
|
|
246
|
+
description: skill.description,
|
|
247
|
+
parameters: { type: "object", properties: {}, additionalProperties: true }
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
async executeToolCall(call, ctx) {
|
|
251
|
+
const skill = this.skills.get(call.name);
|
|
252
|
+
if (!skill) return `Error: skill "${call.name}" not found`;
|
|
253
|
+
try {
|
|
254
|
+
const middleware = this.config.middleware ?? [];
|
|
255
|
+
const wrapped = composeMiddleware(middleware, skill);
|
|
256
|
+
const result = await wrapped.execute(call.arguments, ctx);
|
|
257
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
return `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// ../core/src/agents/loader.ts
|
|
265
|
+
import { readFile } from "fs/promises";
|
|
266
|
+
import YAML from "yaml";
|
|
267
|
+
|
|
268
|
+
// ../core/src/config/schema.ts
|
|
269
|
+
import { z } from "zod";
|
|
270
|
+
var RetryConfigSchema = z.object({
|
|
271
|
+
maxRetries: z.number().int().positive().default(3),
|
|
272
|
+
initialDelayMs: z.number().int().positive().default(500),
|
|
273
|
+
maxDelayMs: z.number().int().positive().default(3e4)
|
|
274
|
+
});
|
|
275
|
+
var ProviderConfigSchema = z.object({
|
|
276
|
+
name: z.string(),
|
|
277
|
+
type: z.enum(["anthropic", "openai", "copilot", "google", "groq", "ollama", "custom"]),
|
|
278
|
+
// nullish handles YAML empty value (parsed as null) and absent key (undefined)
|
|
279
|
+
apiKey: z.string().nullish().transform((v) => v ?? void 0),
|
|
280
|
+
baseUrl: z.string().url().nullish().transform((v) => v ?? void 0),
|
|
281
|
+
defaultModel: z.string().nullish().transform((v) => v ?? void 0),
|
|
282
|
+
options: z.record(z.unknown()).optional(),
|
|
283
|
+
retry: RetryConfigSchema.optional(),
|
|
284
|
+
fallback: z.string().optional()
|
|
285
|
+
});
|
|
286
|
+
var AgentConfigSchema = z.object({
|
|
287
|
+
name: z.string(),
|
|
288
|
+
description: z.string(),
|
|
289
|
+
provider: z.string().optional(),
|
|
290
|
+
model: z.string().optional(),
|
|
291
|
+
systemPrompt: z.string(),
|
|
292
|
+
skills: z.array(z.string()).optional(),
|
|
293
|
+
maxIterations: z.number().int().positive().default(10),
|
|
294
|
+
temperature: z.number().min(0).max(2).optional()
|
|
295
|
+
});
|
|
296
|
+
var ScaffoldPathsSchema = z.object({
|
|
297
|
+
agents: z.string().default("agents"),
|
|
298
|
+
skills: z.string().default("src/skills"),
|
|
299
|
+
providers: z.string().default("packages")
|
|
300
|
+
});
|
|
301
|
+
var OmniAiConfigSchema = z.object({
|
|
302
|
+
version: z.string().default("1"),
|
|
303
|
+
providers: z.array(ProviderConfigSchema).min(1, "At least one provider must be configured"),
|
|
304
|
+
defaultProvider: z.string(),
|
|
305
|
+
agentsDir: z.string().default("agents"),
|
|
306
|
+
scaffoldPaths: ScaffoldPathsSchema.optional(),
|
|
307
|
+
agents: z.array(AgentConfigSchema).optional()
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// ../core/src/agents/loader.ts
|
|
311
|
+
async function loadAgent(yamlPath, config, skills) {
|
|
312
|
+
const raw = await readFile(yamlPath, "utf-8");
|
|
313
|
+
const data = YAML.parse(raw);
|
|
314
|
+
const agentConfig = AgentConfigSchema.parse(data);
|
|
315
|
+
const providerName = agentConfig.provider ?? config.defaultProvider;
|
|
316
|
+
const providerConfig = config.providers.find((p) => p.name === providerName);
|
|
317
|
+
if (!providerConfig) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`Provider "${providerName}" not found in config. Available: ${config.providers.map((p) => p.name).join(", ")}`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const provider = buildProvider(providerConfig, config.providers);
|
|
323
|
+
const resolvedSkills = (agentConfig.skills ?? []).map((name) => skills.get(name));
|
|
324
|
+
return new Agent(agentConfig, provider, resolvedSkills);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ../core/src/bootstrap.ts
|
|
328
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
329
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
330
|
+
import { glob } from "glob";
|
|
331
|
+
import YAML3 from "yaml";
|
|
332
|
+
|
|
333
|
+
// ../core/src/config/loader.ts
|
|
334
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
335
|
+
import { resolve } from "path";
|
|
336
|
+
import YAML2 from "yaml";
|
|
337
|
+
import { ZodError } from "zod";
|
|
338
|
+
function substituteEnvVars(text) {
|
|
339
|
+
return text.replace(/\$\{([^}]+)\}/g, (_, name) => process.env[name] ?? "");
|
|
340
|
+
}
|
|
341
|
+
function formatZodError(err) {
|
|
342
|
+
const lines = err.errors.map((e) => ` \u2022 ${e.path.join(".")}: ${e.message}`);
|
|
343
|
+
return `Invalid omni-ai.yaml configuration:
|
|
344
|
+
${lines.join("\n")}`;
|
|
345
|
+
}
|
|
346
|
+
async function loadConfig(configPath) {
|
|
347
|
+
const filePath = configPath ?? resolve(process.cwd(), "config", "omni-ai.yaml");
|
|
348
|
+
const raw = await readFile2(filePath, "utf-8");
|
|
349
|
+
const substituted = substituteEnvVars(raw);
|
|
350
|
+
const data = YAML2.parse(substituted);
|
|
351
|
+
try {
|
|
352
|
+
return OmniAiConfigSchema.parse(data);
|
|
353
|
+
} catch (err) {
|
|
354
|
+
if (err instanceof ZodError) {
|
|
355
|
+
throw new Error(formatZodError(err));
|
|
356
|
+
}
|
|
357
|
+
throw err;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ../core/src/skills/registry.ts
|
|
362
|
+
var SkillRegistry = class {
|
|
363
|
+
skills = /* @__PURE__ */ new Map();
|
|
364
|
+
register(skill) {
|
|
365
|
+
this.skills.set(skill.name, skill);
|
|
366
|
+
return this;
|
|
367
|
+
}
|
|
368
|
+
get(name) {
|
|
369
|
+
const skill = this.skills.get(name);
|
|
370
|
+
if (!skill) {
|
|
371
|
+
throw new Error(`Skill "${name}" not found. Available: ${[...this.skills.keys()].join(", ")}`);
|
|
372
|
+
}
|
|
373
|
+
return skill;
|
|
374
|
+
}
|
|
375
|
+
all() {
|
|
376
|
+
return [...this.skills.values()];
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// ../core/src/bootstrap.ts
|
|
381
|
+
async function createRuntime(options) {
|
|
382
|
+
const config = await loadConfig(options?.configPath);
|
|
383
|
+
const skills = new SkillRegistry();
|
|
384
|
+
for (const skill of options?.skills ?? []) {
|
|
385
|
+
skills.register(skill);
|
|
386
|
+
}
|
|
387
|
+
const configDir = options?.configPath ? dirname(resolve2(options.configPath)) : resolve2(process.cwd(), "config");
|
|
388
|
+
const agentsBaseDir = resolve2(configDir, "..", config.agentsDir);
|
|
389
|
+
async function resolveAgent(nameOrPath) {
|
|
390
|
+
if (nameOrPath.includes("/") || nameOrPath.endsWith(".yaml")) {
|
|
391
|
+
return loadAgent(resolve2(nameOrPath), config, skills);
|
|
392
|
+
}
|
|
393
|
+
const inlineCfg = config.agents?.find((a) => a.name === nameOrPath);
|
|
394
|
+
if (inlineCfg) {
|
|
395
|
+
const providerName = inlineCfg.provider ?? config.defaultProvider;
|
|
396
|
+
const providerConfig = config.providers.find((p) => p.name === providerName);
|
|
397
|
+
if (!providerConfig) {
|
|
398
|
+
throw new Error(`Provider "${providerName}" not found in config for agent "${nameOrPath}"`);
|
|
399
|
+
}
|
|
400
|
+
const provider = buildProvider(providerConfig, config.providers);
|
|
401
|
+
const resolvedSkills = (inlineCfg.skills ?? []).map((n) => skills.get(n));
|
|
402
|
+
return new Agent(inlineCfg, provider, resolvedSkills);
|
|
403
|
+
}
|
|
404
|
+
const files = await glob("**/*.yaml", { cwd: agentsBaseDir, absolute: true });
|
|
405
|
+
for (const file of files) {
|
|
406
|
+
const raw = await readFile3(file, "utf-8");
|
|
407
|
+
const data = YAML3.parse(raw);
|
|
408
|
+
if (data?.name === nameOrPath) return loadAgent(file, config, skills);
|
|
409
|
+
}
|
|
410
|
+
throw new Error(`Agent "${nameOrPath}" not found in ${agentsBaseDir}`);
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
config,
|
|
414
|
+
skills,
|
|
415
|
+
async run(nameOrPath, input, opts = {}) {
|
|
416
|
+
const agent = await resolveAgent(nameOrPath);
|
|
417
|
+
if (options?.memoryStore && opts.session && !agent.config.memory?.store) {
|
|
418
|
+
agent.config.memory = {
|
|
419
|
+
...agent.config.memory ?? {},
|
|
420
|
+
store: options.memoryStore
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
return agent.run({ ...opts, input });
|
|
424
|
+
},
|
|
425
|
+
async listAgents(agentsDir) {
|
|
426
|
+
const summaries = [];
|
|
427
|
+
for (const a of config.agents ?? []) {
|
|
428
|
+
summaries.push({ name: a.name, description: a.description, path: "(config)" });
|
|
429
|
+
}
|
|
430
|
+
const dir = agentsDir ?? agentsBaseDir;
|
|
431
|
+
const files = await glob("**/*.yaml", { cwd: dir, absolute: true });
|
|
432
|
+
for (const file of files) {
|
|
433
|
+
try {
|
|
434
|
+
const raw = await readFile3(file, "utf-8");
|
|
435
|
+
const data = YAML3.parse(raw);
|
|
436
|
+
if (data?.name && !summaries.find((s) => s.name === data.name)) {
|
|
437
|
+
summaries.push({ name: data.name, description: data.description ?? "", path: file });
|
|
438
|
+
}
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return summaries.sort((a, b) => a.name.localeCompare(b.name));
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export {
|
|
448
|
+
registerProvider,
|
|
449
|
+
getRegisteredProviders,
|
|
450
|
+
createRuntime
|
|
451
|
+
};
|