orbitx 0.0.1 → 0.0.2

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/.env.example ADDED
@@ -0,0 +1 @@
1
+ CHROME_PATH="C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
package/Readme.md ADDED
@@ -0,0 +1,81 @@
1
+ [![OrbitX Logo](./orbitx.png)](#)
2
+
3
+
4
+ Better DOCS Coming soon Sorry
5
+
6
+ Example of Simple Agent
7
+ ```ts
8
+ const ollamaProvider = new OllamaProvider("gemma3:latest");
9
+
10
+ const agent = new SimpleAgent({
11
+ aiProvider: ollamaProvider,
12
+ instruction: "",
13
+ tools: [GetCurrentTimeTool]
14
+ });
15
+
16
+ let oldRole: "assistant" | "tool" | undefined;
17
+ agent.run("what is current time?", (chunk) => {
18
+ if (oldRole != chunk.role) {
19
+ process.stdout.write(`\n${chunk.role}: `);
20
+ oldRole = chunk.role;
21
+ }
22
+ process.stdout.write(chunk.content);
23
+ if (chunk.done) {
24
+ process.stdout.write("\n\n");
25
+ oldRole = undefined;
26
+ }
27
+ });
28
+ ```
29
+
30
+ Example of Research Agent
31
+ ```ts
32
+ const ollamaProvider = new OllamaProvider("gemma3:latest");
33
+
34
+ const agent = new ResearcherAgent({ aiProvider: ollamaProvider });
35
+
36
+ let oldRole: "assistant" | "tool" | undefined;
37
+ agent.run("what is IRR Value In USD", (chunk) => {
38
+ if (oldRole != chunk.role) {
39
+ process.stdout.write(`\n${chunk.role}: `);
40
+ oldRole = chunk.role;
41
+ }
42
+ process.stdout.write(chunk.content);
43
+ if (chunk.done) {
44
+ process.stdout.write("\n\n");
45
+ oldRole = undefined;
46
+ }
47
+ });
48
+ ```
49
+
50
+
51
+
52
+ Writing from base
53
+ ```ts
54
+ const ollamaProvider = new OllamaProvider("gemma3:latest");
55
+
56
+ const connection = new MCPConnection();
57
+
58
+ const mcpServer = new MCPServer(connection);
59
+ mcpServer.registerTool(GetCurrentTimeTool);
60
+ const mcpClient = new MCPClient("DEFAULT_ENV", connection);
61
+
62
+ const agent = new BaseAgent({
63
+ aiProvider: ollamaProvider,
64
+ instruction: "",
65
+ mcpClient,
66
+ allowedTools: [GetCurrentTimeTool]
67
+ });
68
+
69
+ let oldRole: "assistant" | "tool" | undefined;
70
+ agent.run("what is current time?", (chunk) => {
71
+ if (oldRole != chunk.role) {
72
+ process.stdout.write(`\n${chunk.role}: `);
73
+ oldRole = chunk.role;
74
+ }
75
+ process.stdout.write(chunk.content);
76
+ if (chunk.done) {
77
+ process.stdout.write("\n\n");
78
+ oldRole = undefined;
79
+ }
80
+ });
81
+ ```
package/orbitx.png ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orbitx",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "An AI SDK providing agents, swarms, planners, skills, MCP tools, workflows, and multi-provider AI integrations.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,6 +14,21 @@
14
14
  "Skills",
15
15
  "Tools"
16
16
  ],
17
+ "type": "module",
17
18
  "author": "ali39",
18
- "license": "MIT"
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "express": "^5.2.1",
22
+ "ollama": "^0.6.3",
23
+ "openai": "^6.45.0",
24
+ "puppeteer-core": "^24.43.1",
25
+ "socket.io": "^4.8.3"
26
+ },
27
+ "devDependencies": {
28
+ "@types/express": "^5.0.6",
29
+ "@types/node": "^26.0.0",
30
+ "nodemon": "^3.1.14",
31
+ "tsx": "^4.22.4",
32
+ "typescript": "^6.0.3"
33
+ }
19
34
  }
@@ -0,0 +1,25 @@
1
+ export interface Message {
2
+ role: "user" | "system" | "assistant" | "tool";
3
+ content?: string;
4
+ }
5
+
6
+ export interface ChatResponse {
7
+ content: string;
8
+ inputTokens: number;
9
+ outputTokens: number;
10
+ }
11
+
12
+ export type StreamCallback = (chunk: {
13
+ role: "assistant" | "tool";
14
+ content: string;
15
+ done: boolean;
16
+ }) => void;
17
+
18
+ export abstract class AIProvider {
19
+ abstract chat(
20
+ messages: Message[],
21
+ streamCallback?: StreamCallback
22
+ ): Promise<ChatResponse>;
23
+ }
24
+
25
+ export default AIProvider;
@@ -0,0 +1,297 @@
1
+ import type { AIProvider, Message, StreamCallback } from "./ai-provider";
2
+ import type MCPClient from "./mcp-client";
3
+ import type MCPTool from "./mcp";
4
+ import type Skill from "./skill";
5
+
6
+ export interface ParsedToolCall {
7
+ id: string;
8
+ tool: string;
9
+ inputs: Record<string, any>;
10
+ }
11
+
12
+ export type ExtractedSegment =
13
+ | { type: "text"; context: string }
14
+ | { type: "tool"; context: ParsedToolCall };
15
+
16
+ export class BaseAgent {
17
+ #maxMemorizeToken = 16000;
18
+ #instruction: string = "";
19
+ #allowedTools: string[] = [];
20
+
21
+ #skills: Skill[] = [];
22
+
23
+ #memory: string = "";
24
+
25
+ #messagesFull: Message[] = [];
26
+
27
+ #fullInputMissTokens = 0;
28
+ #fullInputHitTokens = 0;
29
+ #fullOutputTokens = 0;
30
+
31
+ #messagesCompact: Message[] = [];
32
+ #currentInputMissTokens = 0;
33
+ #currentInputHitTokens = 0;
34
+ #currentLastOutputTokens = 0;
35
+ #currentOutputTokens = 0;
36
+
37
+ #mcpClient: MCPClient;
38
+ #aiProvider: AIProvider;
39
+
40
+ constructor({ instruction, allowedTools, aiProvider, mcpClient, initData = {
41
+ memory: "",
42
+ messagesFull: [],
43
+ fullInputMissTokens: 0,
44
+ fullInputHitTokens: 0,
45
+ fullOutputTokens: 0,
46
+ messagesCompact: [],
47
+ currentInputMissTokens: 0,
48
+ currentInputHitTokens: 0,
49
+ currentOutputTokens: 0,
50
+ }, skills = [], maxMemorizeToken = 16000 }: {
51
+ instruction: string;
52
+ allowedTools: MCPTool[];
53
+ aiProvider: AIProvider;
54
+ mcpClient: MCPClient;
55
+ skills?: Skill[];
56
+ maxMemorizeToken?: number;
57
+ initData?: {
58
+ memory: string;
59
+ messagesFull: Message[];
60
+ fullInputMissTokens: number;
61
+ fullInputHitTokens: number;
62
+ fullOutputTokens: number;
63
+ messagesCompact: Message[];
64
+ currentInputMissTokens: number;
65
+ currentInputHitTokens: number;
66
+ currentOutputTokens: number;
67
+ }
68
+ }) {
69
+ this.#instruction = instruction;
70
+ this.#allowedTools = allowedTools.map(t => t.getOptions().name);
71
+ this.#aiProvider = aiProvider;
72
+ this.#mcpClient = mcpClient;
73
+ this.#memory = initData.memory;
74
+ this.#messagesFull = initData.messagesFull;
75
+ this.#fullInputMissTokens = initData.fullInputMissTokens
76
+ this.#fullInputHitTokens = initData.fullInputHitTokens
77
+ this.#fullOutputTokens = initData.fullOutputTokens
78
+ this.#messagesCompact = initData.messagesCompact;
79
+ this.#currentInputMissTokens = initData.currentInputMissTokens
80
+ this.#currentInputHitTokens = initData.currentInputHitTokens
81
+ this.#currentOutputTokens = initData.currentOutputTokens
82
+ this.#maxMemorizeToken = maxMemorizeToken;
83
+ this.#skills = skills;
84
+ }
85
+
86
+ #extractJson(str: string): ExtractedSegment[] {
87
+ const segments: ExtractedSegment[] = [];
88
+ let cursor = 0;
89
+ let textStart = 0;
90
+
91
+ for (let i = 0; i < str.length; i++) {
92
+ if (str[i] !== "{") continue;
93
+
94
+ let brace = 0;
95
+ for (let j = i; j < str.length; j++) {
96
+ if (str[j] === "{") brace++;
97
+ if (str[j] === "}") brace--;
98
+
99
+ if (brace === 0) {
100
+ const candidate = str.slice(i, j + 1);
101
+ try {
102
+ const result = JSON.parse(candidate);
103
+ if (
104
+ result &&
105
+ typeof result === "object" &&
106
+ "tool" in result &&
107
+ "inputs" in result &&
108
+ typeof result.tool === "string"
109
+ ) {
110
+ const text = str.slice(textStart, i);
111
+ if (text.trim().length > 0) {
112
+ segments.push({ type: "text", context: text });
113
+ }
114
+ segments.push({ type: "tool", context: result as ParsedToolCall });
115
+
116
+ i = j;
117
+ textStart = j + 1;
118
+ cursor = j + 1;
119
+ }
120
+ } catch {
121
+ // not valid JSON, skip
122
+ }
123
+ break;
124
+ }
125
+ }
126
+ }
127
+
128
+ const remaining = str.slice(textStart);
129
+ if (remaining.trim().length > 0) {
130
+ segments.push({ type: "text", context: remaining });
131
+ }
132
+
133
+ return segments;
134
+ }
135
+
136
+ async #buildSystemPrompt(): Promise<string> {
137
+ const tools = (await this.#mcpClient.getTools())
138
+ .filter(t => this.#allowedTools.includes(t.name));
139
+
140
+ const toolBlock = tools.map(t => {
141
+ const params = t.inputs.map(ti =>
142
+ `${ti.name}${ti.required ? "" : "?"}:${ti.type}${ti.default !== undefined ? `=${JSON.stringify(ti.default)}` : ""} // ${ti.description}`
143
+ ).join(", ");
144
+
145
+ const example: Record<string, unknown> = {};
146
+ t.inputs.forEach(ti => {
147
+ example[ti.name] = ti.default !== undefined ? ti.default
148
+ : ti.type === "string" ? "..."
149
+ : ti.type === "number" ? 0
150
+ : ti.type === "boolean" ? true
151
+ : ti.type === "array" ? []
152
+ : {};
153
+ });
154
+
155
+ return `${t.name}(${params || ""})\n${t.description}\nex: {"tool":"${t.name}","inputs":${JSON.stringify(example)}}`;
156
+ }).join("\n\n");
157
+
158
+ return `You are an assistant with tools. CRITICAL RULES:
159
+ 1. Think step by step.
160
+ ${tools.length > 0 && `2. EVERY tool call MUST include a UNIQUE 10-digit random ID in the format: {"id":"1234567890", "tool":"<name>","inputs":{...}}
161
+ 3. The ID MUST be exactly 10 digits (numbers only) and MUST be different for EACH tool call.
162
+ 4. Example of VALID tool call: {"id":"7845123698", "tool":"search", "inputs":{"query":"weather"}}
163
+ 5. After each tool result, continue reasoning or call another tool.
164
+ 6. Never invent tool results.`}
165
+ ${tools.length > 0 ? "7" : "2"}. If any rules conflict with upper ones, align with upper rules.
166
+
167
+ ${tools.length > 0 && `REMEMBER: Every tool call MUST include a random 10-digit ID!
168
+
169
+ TOOLS:
170
+ ${toolBlock}
171
+ `}
172
+
173
+ ${this.#skills &&`
174
+ SKILLS:
175
+ ${this.#skills.map(skill => (`${skill.getSkill().name} - ${skill.getSkill().description}
176
+ ${skill.getSkill().instructions}
177
+ \n`))}
178
+ `}
179
+
180
+ MEMORY:
181
+ ${this.#memory || "(empty)"}
182
+
183
+ INSTRUCTIONS:
184
+ ${this.#instruction}
185
+ `;
186
+ }
187
+
188
+ #buildMemoryPrompt(): string {
189
+ return `Summarize all important facts and pending tasks from this conversation.
190
+ Output only this JSON line, nothing else:
191
+ {"tool":"set_memory","inputs":{"new_memory":"<compact summary>"}}`;
192
+ }
193
+
194
+ async #extractToolCalls(content: string): Promise<ParsedToolCall[]> {
195
+ return await this.#extractJson(content)
196
+ .filter((r: { type: string }) => r.type === "tool")
197
+ .map((t) => t.context as ParsedToolCall);
198
+ }
199
+
200
+ async #dispatchTool(toolCall: ParsedToolCall): Promise<string> {
201
+ if (toolCall.tool === "set_memory") {
202
+ if (typeof toolCall.inputs?.new_memory === "string") {
203
+ this.#memory = toolCall.inputs.new_memory;
204
+ return "Memory updated.";
205
+ }
206
+ return `Error: set_memory requires new_memory:string, got ${JSON.stringify(toolCall.inputs)}`;
207
+ }
208
+
209
+ if (this.#allowedTools.includes(toolCall.tool)) {
210
+ return JSON.stringify({ id: toolCall.id, output: await this.#mcpClient.callTool(toolCall.tool, toolCall.inputs) });
211
+ }
212
+
213
+ return `Error: tool "${toolCall.tool}" not found or not allowed.`;
214
+ }
215
+
216
+ async run(prompt: string, streamCallback?: StreamCallback): Promise<void> {
217
+ let toolsCalls: ParsedToolCall[] = [];
218
+
219
+ do {
220
+ const systemPrompt = await this.#buildSystemPrompt();
221
+
222
+ const chat = await this.#aiProvider.chat([
223
+ { role: "system", content: systemPrompt },
224
+ ...this.#messagesCompact,
225
+ { role: "user", content: prompt }
226
+ ], streamCallback);
227
+
228
+ this.#messagesFull.push({ role: "user", content: prompt });
229
+ this.#messagesCompact.push({ role: "user", content: prompt });
230
+ this.#messagesFull.push({ role: "assistant", content: chat.content });
231
+ this.#messagesCompact.push({ role: "assistant", content: chat.content });
232
+
233
+ this.#currentInputHitTokens += this.#currentInputMissTokens;
234
+ this.#currentInputMissTokens += chat.inputTokens - this.#currentInputMissTokens;
235
+ this.#currentOutputTokens += this.#currentOutputTokens;
236
+ this.#currentLastOutputTokens = this.#currentOutputTokens;
237
+
238
+ toolsCalls = await this.#extractToolCalls(chat.content);
239
+
240
+ for (const toolCall of toolsCalls) {
241
+ const toolResponse = await this.#dispatchTool(toolCall);
242
+ this.#messagesFull.push({ role: "system", content: toolResponse });
243
+ this.#messagesCompact.push({ role: "system", content: toolResponse });
244
+ streamCallback?.({ role: "tool", content: toolResponse, done: true });
245
+ }
246
+
247
+ if (this.#currentInputMissTokens + this.#currentLastOutputTokens > this.#maxMemorizeToken) {
248
+ const memChat = await this.#aiProvider.chat([
249
+ { role: "system", content: systemPrompt },
250
+ ...this.#messagesCompact,
251
+ { role: "user", content: this.#buildMemoryPrompt() }
252
+ ]);
253
+
254
+ this.#currentInputHitTokens += this.#currentInputMissTokens;
255
+ this.#currentInputMissTokens += memChat.inputTokens - this.#currentInputMissTokens;
256
+ this.#currentOutputTokens += this.#currentOutputTokens;
257
+
258
+ for (const mc of await this.#extractToolCalls(memChat.content)) {
259
+ const toolResponse = await this.#dispatchTool(mc);
260
+ streamCallback?.({ role: "tool", content: toolResponse, done: true });
261
+ }
262
+
263
+ this.#messagesCompact = [];
264
+ this.#fullInputHitTokens += this.#currentInputHitTokens;
265
+ this.#fullInputMissTokens += this.#currentInputMissTokens;
266
+ this.#fullOutputTokens += this.#currentOutputTokens;
267
+ this.#currentInputHitTokens = 0;
268
+ this.#currentInputMissTokens = 0;
269
+ this.#currentOutputTokens = 0;
270
+ this.#currentLastOutputTokens = 0;
271
+ }
272
+
273
+ prompt = "";
274
+
275
+ } while (toolsCalls.length > 0);
276
+ }
277
+
278
+ getCurrentTotalTokens() {
279
+ return {
280
+ total: this.#currentInputHitTokens + this.#currentInputMissTokens + this.#currentOutputTokens,
281
+ inputHit: this.#currentInputHitTokens,
282
+ inputMiss: this.#currentInputMissTokens,
283
+ output: this.#currentOutputTokens
284
+ };
285
+ }
286
+
287
+ getFullTotalTokens() {
288
+ return {
289
+ total: this.#fullInputHitTokens + this.#fullInputMissTokens + this.#fullOutputTokens,
290
+ inputHit: this.#fullInputHitTokens,
291
+ inputMiss: this.#fullInputMissTokens,
292
+ output: this.#fullOutputTokens
293
+ };
294
+ }
295
+ }
296
+
297
+ export default BaseAgent;
@@ -0,0 +1,71 @@
1
+ import OpenAI from "openai";
2
+ import AIProvider, { type Message, type ChatResponse, type StreamCallback } from "./ai-provider";
3
+
4
+ export class DeepSeekProvider extends AIProvider {
5
+ private client: OpenAI;
6
+ private model: string;
7
+
8
+ constructor(apiKey: string, model: string = "deepseek-v4-flash") {
9
+ super();
10
+ this.client = new OpenAI({
11
+ apiKey: apiKey,
12
+ baseURL: "https://api.deepseek.com"
13
+ });
14
+ this.model = model;
15
+ }
16
+
17
+ async chat(
18
+ messages: Message[],
19
+ streamCallback?: StreamCallback
20
+ ): Promise<ChatResponse> {
21
+ const formattedMessages = messages.map(msg => ({
22
+ role: msg.role,
23
+ content: msg.content || ""
24
+ }));
25
+
26
+ if (streamCallback) {
27
+ const stream = await this.client.chat.completions.create({
28
+ model: this.model,
29
+ messages: formattedMessages as any,
30
+ stream: true
31
+ });
32
+
33
+ let fullContent = "";
34
+ let inputTokens = 0;
35
+ let outputTokens = 0;
36
+
37
+ for await (const chunk of stream) {
38
+ const content = chunk.choices[0]?.delta?.content || "";
39
+ if (content) {
40
+ fullContent += content;
41
+ streamCallback({ role: "assistant", content, done: false });
42
+ }
43
+
44
+ if (chunk.usage) {
45
+ inputTokens = chunk.usage.prompt_tokens || 0;
46
+ outputTokens = chunk.usage.completion_tokens || 0;
47
+ }
48
+ }
49
+
50
+ streamCallback({ role: "assistant", content: "", done: true });
51
+ return {
52
+ content: fullContent,
53
+ inputTokens,
54
+ outputTokens,
55
+ };
56
+ } else {
57
+ const response = await this.client.chat.completions.create({
58
+ model: this.model,
59
+ messages: formattedMessages as any
60
+ });
61
+
62
+ return {
63
+ content: response.choices[0]?.message?.content || "",
64
+ inputTokens: response.usage?.prompt_tokens || 0,
65
+ outputTokens: response.usage?.completion_tokens || 0,
66
+ };
67
+ }
68
+ }
69
+ }
70
+
71
+ export default DeepSeekProvider;
@@ -0,0 +1,113 @@
1
+ import type MCPTool from "./mcp";
2
+ import type MCPConnection from "./mcp-connection";
3
+ import { randomUUID } from "crypto";
4
+
5
+ export class MCPClient {
6
+ #connections;
7
+ #tools: MCPTool[] = [];
8
+ #envID;
9
+ constructor(envID: string, connection: MCPConnection | MCPConnection[]) {
10
+ this.#connections = Array.isArray(connection) ? connection : [connection];
11
+ this.#envID = envID;
12
+ }
13
+
14
+ async getTools() {
15
+ const connectionPromises = this.#connections.map(conn => {
16
+ return new Promise<{
17
+ name: string;
18
+ description: string;
19
+ inputs: {
20
+ name: string;
21
+ type: "number" | "string" | "boolean" | "object" | "array";
22
+ description: string;
23
+ required?: boolean;
24
+ default?: any;
25
+ }[];
26
+ }[]>((resolve) => {
27
+ const pid = randomUUID();
28
+ let resolved = false;
29
+
30
+ const onRead = (data: any) => {
31
+ if (data.topic === "getToolsCallback" && data.pid === pid) {
32
+ resolved = true;
33
+ conn.off("read", onRead);
34
+ resolve(data.tools || []);
35
+ }
36
+ };
37
+
38
+ conn.on("read", onRead);
39
+ conn.emit("write", {
40
+ pid,
41
+ topic: "getTools"
42
+ });
43
+
44
+ setTimeout(() => {
45
+ if (!resolved) {
46
+ conn.off("read", onRead);
47
+ resolve([]);
48
+ }
49
+ }, 2000);
50
+ });
51
+ });
52
+
53
+ const results = await Promise.all(connectionPromises);
54
+
55
+ const connectionsTools = results.flat();
56
+ return [...this.#tools.map(t => ({
57
+ name: t.getOptions().name,
58
+ description: t.getOptions().description,
59
+ inputs: t.getOptions().inputs
60
+ })), ...connectionsTools];
61
+ }
62
+
63
+ async callTool(toolName: string, inputs: Record<string, any>) {
64
+ const clientTool = this.#tools.find(t => t.getOptions().name == toolName);
65
+ if (clientTool) {
66
+ return JSON.stringify(await clientTool.getOptions().execute(this.#envID, inputs));
67
+ } else {
68
+ return JSON.stringify(await new Promise<any>((resolve) => {
69
+ let resolvedResult = false;
70
+ this.#connections.map(conn => {
71
+ const pid = randomUUID();
72
+ let resolved = false;
73
+
74
+ const onRead = (data: any) => {
75
+ if (data.topic === "toolCallCallback" && data.pid === pid) {
76
+ resolved = true;
77
+ conn.off("read", onRead);
78
+ if (!resolvedResult) {
79
+ resolve(data.output || {});
80
+ resolvedResult = true;
81
+ }
82
+ }
83
+ };
84
+
85
+ conn.on("read", onRead);
86
+ conn.emit("write", {
87
+ pid,
88
+ topic: "toolCall",
89
+ tool: toolName,
90
+ envID: this.#envID,
91
+ inputs
92
+ });
93
+
94
+ setTimeout(() => {
95
+ if (!resolved) {
96
+ conn.off("read", onRead);
97
+ }
98
+ if (!resolvedResult) {
99
+ resolve({ error: "The tool call exceeded the 300second timeout limit." });
100
+ resolvedResult = true;
101
+ }
102
+ }, 300000);
103
+ });
104
+ }));
105
+ }
106
+ }
107
+
108
+ registerTool(tool: MCPTool) {
109
+ this.#tools.push(tool);
110
+ }
111
+ }
112
+
113
+ export default MCPClient;
@@ -0,0 +1,5 @@
1
+ export default class MCPComputer {
2
+ constructor() {
3
+
4
+ }
5
+ }
@@ -0,0 +1,12 @@
1
+ import EventEmitter from "events";
2
+
3
+ export class MCPConnection extends EventEmitter {
4
+ constructor(overrided: boolean = false) {
5
+ super();
6
+
7
+ if (!overrided)
8
+ this.on("write", (data) => this.emit("read", data))
9
+ }
10
+ }
11
+
12
+ export default MCPConnection;