jiva-core 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.
Files changed (94) hide show
  1. package/.env.example +18 -0
  2. package/.fluen/cache/state.json +7 -0
  3. package/README.md +350 -0
  4. package/actions/action_registry.py +75 -0
  5. package/actions/python_coder.py +470 -0
  6. package/api/main.py +269 -0
  7. package/dist/core/agent.d.ts +69 -0
  8. package/dist/core/agent.d.ts.map +1 -0
  9. package/dist/core/agent.js +214 -0
  10. package/dist/core/agent.js.map +1 -0
  11. package/dist/core/config.d.ts +222 -0
  12. package/dist/core/config.d.ts.map +1 -0
  13. package/dist/core/config.js +138 -0
  14. package/dist/core/config.js.map +1 -0
  15. package/dist/core/workspace.d.ts +53 -0
  16. package/dist/core/workspace.d.ts.map +1 -0
  17. package/dist/core/workspace.js +164 -0
  18. package/dist/core/workspace.js.map +1 -0
  19. package/dist/index.d.ts +17 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +17 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/interfaces/cli/index.d.ts +6 -0
  24. package/dist/interfaces/cli/index.d.ts.map +1 -0
  25. package/dist/interfaces/cli/index.js +257 -0
  26. package/dist/interfaces/cli/index.js.map +1 -0
  27. package/dist/interfaces/cli/repl.d.ts +9 -0
  28. package/dist/interfaces/cli/repl.d.ts.map +1 -0
  29. package/dist/interfaces/cli/repl.js +139 -0
  30. package/dist/interfaces/cli/repl.js.map +1 -0
  31. package/dist/interfaces/cli/setup-wizard.d.ts +9 -0
  32. package/dist/interfaces/cli/setup-wizard.d.ts.map +1 -0
  33. package/dist/interfaces/cli/setup-wizard.js +321 -0
  34. package/dist/interfaces/cli/setup-wizard.js.map +1 -0
  35. package/dist/mcp/client.d.ts +58 -0
  36. package/dist/mcp/client.d.ts.map +1 -0
  37. package/dist/mcp/client.js +178 -0
  38. package/dist/mcp/client.js.map +1 -0
  39. package/dist/mcp/server-manager.d.ts +58 -0
  40. package/dist/mcp/server-manager.d.ts.map +1 -0
  41. package/dist/mcp/server-manager.js +135 -0
  42. package/dist/mcp/server-manager.js.map +1 -0
  43. package/dist/models/base.d.ts +57 -0
  44. package/dist/models/base.d.ts.map +1 -0
  45. package/dist/models/base.js +5 -0
  46. package/dist/models/base.js.map +1 -0
  47. package/dist/models/harmony.d.ts +78 -0
  48. package/dist/models/harmony.d.ts.map +1 -0
  49. package/dist/models/harmony.js +226 -0
  50. package/dist/models/harmony.js.map +1 -0
  51. package/dist/models/krutrim.d.ts +30 -0
  52. package/dist/models/krutrim.d.ts.map +1 -0
  53. package/dist/models/krutrim.js +185 -0
  54. package/dist/models/krutrim.js.map +1 -0
  55. package/dist/models/orchestrator.d.ts +49 -0
  56. package/dist/models/orchestrator.d.ts.map +1 -0
  57. package/dist/models/orchestrator.js +140 -0
  58. package/dist/models/orchestrator.js.map +1 -0
  59. package/dist/utils/errors.d.ts +23 -0
  60. package/dist/utils/errors.d.ts.map +1 -0
  61. package/dist/utils/errors.js +45 -0
  62. package/dist/utils/errors.js.map +1 -0
  63. package/dist/utils/logger.d.ts +24 -0
  64. package/dist/utils/logger.d.ts.map +1 -0
  65. package/dist/utils/logger.js +74 -0
  66. package/dist/utils/logger.js.map +1 -0
  67. package/docs/BUILD.md +317 -0
  68. package/docs/DEV_WORKFLOW.md +197 -0
  69. package/docs/FILESYSTEM_ACCESS.md +244 -0
  70. package/docs/IMPLEMENTATION_SUMMARY.md +459 -0
  71. package/docs/QUICKSTART.md +162 -0
  72. package/docs/TROUBLESHOOTING.md +393 -0
  73. package/examples/code-review-directive.md +26 -0
  74. package/examples/data-analysis-directive.md +26 -0
  75. package/examples/programmatic-usage.ts +120 -0
  76. package/jiva-directive.md +24 -0
  77. package/package.json +46 -0
  78. package/setup.sh +65 -0
  79. package/src/core/agent.ts +275 -0
  80. package/src/core/config.ts +177 -0
  81. package/src/core/workspace.ts +205 -0
  82. package/src/index.ts +21 -0
  83. package/src/interfaces/cli/index.ts +290 -0
  84. package/src/interfaces/cli/repl.ts +182 -0
  85. package/src/interfaces/cli/setup-wizard.ts +355 -0
  86. package/src/mcp/client.ts +231 -0
  87. package/src/mcp/server-manager.ts +168 -0
  88. package/src/models/base.ts +63 -0
  89. package/src/models/harmony.ts +301 -0
  90. package/src/models/krutrim.ts +236 -0
  91. package/src/models/orchestrator.ts +180 -0
  92. package/src/utils/errors.ts +41 -0
  93. package/src/utils/logger.ts +87 -0
  94. package/tsconfig.json +22 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * MCP Server Manager
3
+ *
4
+ * Manages lifecycle of MCP servers based on configuration
5
+ */
6
+
7
+ import { MCPClient } from './client.js';
8
+ import { MCPServerConfig } from '../core/config.js';
9
+ import { logger } from '../utils/logger.js';
10
+ import { MCPError } from '../utils/errors.js';
11
+
12
+ export class MCPServerManager {
13
+ private mcpClient: MCPClient;
14
+ private serverConfigs: Map<string, MCPServerConfig> = new Map();
15
+
16
+ constructor() {
17
+ this.mcpClient = new MCPClient();
18
+ }
19
+
20
+ /**
21
+ * Initialize servers from configuration
22
+ */
23
+ async initialize(servers: Record<string, MCPServerConfig>): Promise<void> {
24
+ logger.info('Initializing MCP servers...');
25
+
26
+ const serverEntries = Object.entries(servers);
27
+ const enabledServers = serverEntries.filter(([_, config]) => config.enabled);
28
+
29
+ logger.info(`Found ${enabledServers.length} enabled MCP servers`);
30
+
31
+ // Store configs
32
+ for (const [name, config] of serverEntries) {
33
+ this.serverConfigs.set(name, config);
34
+ }
35
+
36
+ // Connect to enabled servers
37
+ const connectionPromises = enabledServers.map(async ([name, config]) => {
38
+ try {
39
+ await this.connectServer(name, config);
40
+ return { name, success: true };
41
+ } catch (error) {
42
+ logger.warn(`Failed to connect to MCP server '${name}': ${error instanceof Error ? error.message : String(error)}`);
43
+ // Continue with other servers even if one fails
44
+ return { name, success: false };
45
+ }
46
+ });
47
+
48
+ const results = await Promise.allSettled(connectionPromises);
49
+
50
+ const connectedServers = this.mcpClient.getConnectedServers();
51
+
52
+ if (connectedServers.length > 0) {
53
+ logger.success(`MCP servers connected: ${connectedServers.join(', ')}`);
54
+ } else if (enabledServers.length > 0) {
55
+ logger.warn('No MCP servers connected. Agent will run without external tools.');
56
+ } else {
57
+ logger.info('No MCP servers enabled. Agent will run without external tools.');
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Connect to a specific server
63
+ */
64
+ private async connectServer(name: string, config: MCPServerConfig): Promise<void> {
65
+ await this.mcpClient.connect(
66
+ name,
67
+ config.command,
68
+ config.args || [],
69
+ config.env
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Add and connect to a new server
75
+ */
76
+ async addServer(name: string, config: MCPServerConfig): Promise<void> {
77
+ if (this.serverConfigs.has(name)) {
78
+ throw new MCPError(`Server '${name}' already exists`, name);
79
+ }
80
+
81
+ this.serverConfigs.set(name, config);
82
+
83
+ if (config.enabled) {
84
+ await this.connectServer(name, config);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Remove and disconnect from a server
90
+ */
91
+ async removeServer(name: string): Promise<void> {
92
+ await this.mcpClient.disconnect(name);
93
+ this.serverConfigs.delete(name);
94
+ }
95
+
96
+ /**
97
+ * Enable a server and connect
98
+ */
99
+ async enableServer(name: string): Promise<void> {
100
+ const config = this.serverConfigs.get(name);
101
+ if (!config) {
102
+ throw new MCPError(`Server '${name}' not found in configuration`, name);
103
+ }
104
+
105
+ config.enabled = true;
106
+
107
+ if (!this.mcpClient.isConnected(name)) {
108
+ await this.connectServer(name, config);
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Disable a server and disconnect
114
+ */
115
+ async disableServer(name: string): Promise<void> {
116
+ const config = this.serverConfigs.get(name);
117
+ if (!config) {
118
+ throw new MCPError(`Server '${name}' not found in configuration`, name);
119
+ }
120
+
121
+ config.enabled = false;
122
+ await this.mcpClient.disconnect(name);
123
+ }
124
+
125
+ /**
126
+ * Get the MCP client instance
127
+ */
128
+ getClient(): MCPClient {
129
+ return this.mcpClient;
130
+ }
131
+
132
+ /**
133
+ * Get all server configurations
134
+ */
135
+ getServerConfigs(): Map<string, MCPServerConfig> {
136
+ return new Map(this.serverConfigs);
137
+ }
138
+
139
+ /**
140
+ * Cleanup all servers
141
+ */
142
+ async cleanup(): Promise<void> {
143
+ logger.info('Cleaning up MCP servers...');
144
+ await this.mcpClient.disconnectAll();
145
+ }
146
+
147
+ /**
148
+ * Get server status
149
+ */
150
+ getServerStatus(): Array<{
151
+ name: string;
152
+ enabled: boolean;
153
+ connected: boolean;
154
+ toolCount: number;
155
+ }> {
156
+ return Array.from(this.serverConfigs.entries()).map(([name, config]) => {
157
+ const connected = this.mcpClient.isConnected(name);
158
+ const tools = connected ? this.mcpClient.getServerTools(name) : [];
159
+
160
+ return {
161
+ name,
162
+ enabled: config.enabled,
163
+ connected,
164
+ toolCount: tools.length,
165
+ };
166
+ });
167
+ }
168
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Base model interfaces and types
3
+ */
4
+
5
+ export interface Message {
6
+ role: 'system' | 'developer' | 'user' | 'assistant' | 'tool';
7
+ content: string | MessageContent[];
8
+ name?: string;
9
+ tool_call_id?: string;
10
+ }
11
+
12
+ export interface MessageContent {
13
+ type: 'text' | 'image_url';
14
+ text?: string;
15
+ image_url?: {
16
+ url: string;
17
+ };
18
+ }
19
+
20
+ export interface ModelResponse {
21
+ content: string;
22
+ toolCalls?: ToolCall[];
23
+ usage?: {
24
+ promptTokens: number;
25
+ completionTokens: number;
26
+ totalTokens: number;
27
+ };
28
+ raw?: any;
29
+ }
30
+
31
+ export interface ToolCall {
32
+ id: string;
33
+ type: 'function';
34
+ function: {
35
+ name: string;
36
+ arguments: string;
37
+ };
38
+ }
39
+
40
+ export interface Tool {
41
+ name: string;
42
+ description: string;
43
+ parameters: {
44
+ type: 'object';
45
+ properties: Record<string, any>;
46
+ required?: string[];
47
+ };
48
+ }
49
+
50
+ export interface ChatCompletionOptions {
51
+ model: string;
52
+ messages: Message[];
53
+ tools?: Tool[];
54
+ temperature?: number;
55
+ maxTokens?: number;
56
+ stream?: boolean;
57
+ }
58
+
59
+ export interface IModel {
60
+ chat(options: ChatCompletionOptions): Promise<ModelResponse>;
61
+ supportsVision(): boolean;
62
+ supportsToolCalling(): boolean;
63
+ }
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Harmony Response Format Handler
3
+ *
4
+ * Implements the Harmony format required by gpt-oss-120b models.
5
+ * Handles multi-channel output, tool calling, and structured message formatting.
6
+ *
7
+ * Reference: https://github.com/openai/harmony
8
+ */
9
+
10
+ import { logger } from '../utils/logger.js';
11
+ import { ToolCallError } from '../utils/errors.js';
12
+
13
+ export interface HarmonyMessage {
14
+ role: 'system' | 'developer' | 'user' | 'assistant' | 'tool';
15
+ content: string | HarmonyContent[];
16
+ name?: string;
17
+ tool_call_id?: string;
18
+ }
19
+
20
+ export interface HarmonyContent {
21
+ type: 'text' | 'image_url';
22
+ text?: string;
23
+ image_url?: {
24
+ url: string;
25
+ };
26
+ }
27
+
28
+ export interface HarmonyToolDefinition {
29
+ name: string;
30
+ description: string;
31
+ parameters: {
32
+ type: 'object';
33
+ properties: Record<string, any>;
34
+ required?: string[];
35
+ };
36
+ }
37
+
38
+ export interface HarmonyToolCall {
39
+ id: string;
40
+ type: 'function';
41
+ function: {
42
+ name: string;
43
+ arguments: string;
44
+ };
45
+ }
46
+
47
+ export interface ParsedHarmonyResponse {
48
+ analysis?: string;
49
+ commentary?: string;
50
+ final?: string;
51
+ toolCalls: HarmonyToolCall[];
52
+ rawResponse: string;
53
+ }
54
+
55
+ /**
56
+ * Formats tools into Harmony's TypeScript-like syntax for the developer message
57
+ */
58
+ export function formatToolsForHarmony(tools: HarmonyToolDefinition[]): string {
59
+ if (tools.length === 0) return '';
60
+
61
+ const toolDefinitions = tools.map(tool => {
62
+ const params = Object.entries(tool.parameters.properties || {})
63
+ .map(([name, schema]: [string, any]) => {
64
+ const required = tool.parameters.required?.includes(name) ? '' : '?';
65
+ const type = schema.type || 'any';
66
+ const description = schema.description ? ` // ${schema.description}` : '';
67
+ return ` ${name}${required}: ${type};${description}`;
68
+ })
69
+ .join('\n');
70
+
71
+ return `/**
72
+ * ${tool.description}
73
+ */
74
+ function ${tool.name}(params: {
75
+ ${params}
76
+ }): void;`;
77
+ }).join('\n\n');
78
+
79
+ return `# Available Tools
80
+
81
+ You have access to the following tools:
82
+
83
+ <namespace name="functions">
84
+ ${toolDefinitions}
85
+ </namespace>
86
+
87
+ ## CRITICAL: How to Use Tools
88
+
89
+ To execute a tool, you MUST use this EXACT format:
90
+
91
+ <|call|>function_name({"param": "value"})<|return|>
92
+
93
+ Example:
94
+ <|call|>read_file({"path": "/path/to/file"})<|return|>
95
+
96
+ ## Rules:
97
+ 1. Use the EXACT function names from above (e.g., filesystem__read_file)
98
+ 2. Parameters MUST be valid JSON
99
+ 3. Do NOT output markdown code blocks with tool calls
100
+ 4. Do NOT explain what you're doing - just call the tool
101
+ 5. Output the tool call directly with the <|call|> and <|return|> markers
102
+
103
+ WRONG - Do not do this:
104
+ \`\`\`json
105
+ {"action": "read_file", "parameters": {...}}
106
+ \`\`\`
107
+
108
+ CORRECT - Do this:
109
+ <|call|>filesystem__read_file({"path": "/path"})<|return|>`;
110
+ }
111
+
112
+ /**
113
+ * Formats messages into Harmony format
114
+ */
115
+ export function formatMessagesForHarmony(
116
+ messages: HarmonyMessage[],
117
+ tools?: HarmonyToolDefinition[]
118
+ ): HarmonyMessage[] {
119
+ const formattedMessages: HarmonyMessage[] = [];
120
+
121
+ // Process messages based on role hierarchy
122
+ for (const msg of messages) {
123
+ if (msg.role === 'developer' && tools && tools.length > 0) {
124
+ // Inject tool definitions into developer message
125
+ const toolSection = formatToolsForHarmony(tools);
126
+ const existingContent = typeof msg.content === 'string' ? msg.content : '';
127
+ formattedMessages.push({
128
+ ...msg,
129
+ content: `${existingContent}\n\n${toolSection}`,
130
+ });
131
+ } else {
132
+ formattedMessages.push(msg);
133
+ }
134
+ }
135
+
136
+ return formattedMessages;
137
+ }
138
+
139
+ /**
140
+ * Parses Harmony response with multi-channel support
141
+ *
142
+ * Expected format:
143
+ * <|channel|>analysis
144
+ * [chain of thought content]
145
+ * <|channel|>final
146
+ * [final response]
147
+ * <|call|>function_name({"param": "value"})<|return|>
148
+ */
149
+ export function parseHarmonyResponse(response: string): ParsedHarmonyResponse {
150
+ const result: ParsedHarmonyResponse = {
151
+ toolCalls: [],
152
+ rawResponse: response,
153
+ };
154
+
155
+ // Parse channels
156
+ const channelRegex = /<\|channel\|>(\w+)\s*([\s\S]*?)(?=<\|channel\|>|<\|call\|>|$)/g;
157
+ let match;
158
+
159
+ while ((match = channelRegex.exec(response)) !== null) {
160
+ const channelName = match[1];
161
+ const channelContent = match[2].trim();
162
+
163
+ if (channelName === 'analysis') {
164
+ result.analysis = channelContent;
165
+ } else if (channelName === 'commentary') {
166
+ result.commentary = channelContent;
167
+ } else if (channelName === 'final') {
168
+ result.final = channelContent;
169
+ }
170
+ }
171
+
172
+ // Parse tool calls
173
+ const toolCallRegex = /<\|call\|>([\s\S]*?)<\|return\|>/g;
174
+ let toolMatch;
175
+ let callId = 0;
176
+
177
+ while ((toolMatch = toolCallRegex.exec(response)) !== null) {
178
+ const toolCallContent = toolMatch[1].trim();
179
+
180
+ try {
181
+ // Parse function call: function_name({"param": "value"})
182
+ const functionMatch = /^(\w+)\(([\s\S]*)\)$/.exec(toolCallContent);
183
+
184
+ if (functionMatch) {
185
+ const functionName = functionMatch[1];
186
+ const argsString = functionMatch[2];
187
+
188
+ // Validate JSON
189
+ let parsedArgs;
190
+ try {
191
+ parsedArgs = JSON.parse(argsString);
192
+ } catch (e) {
193
+ logger.warn(`Failed to parse tool call arguments for ${functionName}: ${argsString}`);
194
+ // Try to fix common JSON issues
195
+ const fixedArgs = argsString
196
+ .replace(/'/g, '"') // Replace single quotes
197
+ .replace(/(\w+):/g, '"$1":'); // Quote unquoted keys
198
+
199
+ try {
200
+ parsedArgs = JSON.parse(fixedArgs);
201
+ logger.debug(`Successfully fixed and parsed arguments: ${fixedArgs}`);
202
+ } catch (e2) {
203
+ throw new ToolCallError(
204
+ `Invalid JSON in tool call arguments: ${argsString}`,
205
+ functionName
206
+ );
207
+ }
208
+ }
209
+
210
+ result.toolCalls.push({
211
+ id: `call_${callId++}`,
212
+ type: 'function',
213
+ function: {
214
+ name: functionName,
215
+ arguments: JSON.stringify(parsedArgs),
216
+ },
217
+ });
218
+ } else {
219
+ logger.warn(`Failed to parse tool call format: ${toolCallContent}`);
220
+ }
221
+ } catch (error) {
222
+ logger.error('Error parsing tool call', error);
223
+ }
224
+ }
225
+
226
+ // If no channels found, treat entire response as final
227
+ if (!result.analysis && !result.commentary && !result.final && result.toolCalls.length === 0) {
228
+ result.final = response.trim();
229
+ }
230
+
231
+ return result;
232
+ }
233
+
234
+ /**
235
+ * Extracts assistant message with malformed tool call detection
236
+ * This handles cases where gpt-oss-120b generates incorrect tool call formats
237
+ */
238
+ export function extractAssistantMessage(response: string): string {
239
+ // Remove channel markers and tool calls to get clean message
240
+ let cleaned = response
241
+ .replace(/<\|channel\|>\w+/g, '')
242
+ .replace(/<\|call\|>[\s\S]*?<\|return\|>/g, '')
243
+ .replace(/<\|start\|>/g, '')
244
+ .replace(/<\|end\|>/g, '')
245
+ .replace(/<\|message\|>/g, '')
246
+ .trim();
247
+
248
+ // Handle malformed patterns like "assistant<|channel|>analysis"
249
+ cleaned = cleaned.replace(/assistant<\|channel\|>\w+/g, '');
250
+
251
+ return cleaned;
252
+ }
253
+
254
+ /**
255
+ * Validates tool call against available tools
256
+ */
257
+ export function validateToolCall(
258
+ toolCall: HarmonyToolCall,
259
+ availableTools: HarmonyToolDefinition[]
260
+ ): boolean {
261
+ const tool = availableTools.find(t => t.name === toolCall.function.name);
262
+
263
+ if (!tool) {
264
+ logger.warn(`Tool not found: ${toolCall.function.name}`);
265
+ return false;
266
+ }
267
+
268
+ try {
269
+ const args = JSON.parse(toolCall.function.arguments);
270
+ const required = tool.parameters.required || [];
271
+
272
+ // Check required parameters
273
+ for (const param of required) {
274
+ if (!(param in args)) {
275
+ logger.warn(`Missing required parameter '${param}' for tool ${tool.name}`);
276
+ return false;
277
+ }
278
+ }
279
+
280
+ return true;
281
+ } catch (error) {
282
+ logger.error(`Invalid arguments for tool ${tool.name}`, error);
283
+ return false;
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Formats tool result for Harmony format
289
+ */
290
+ export function formatToolResult(
291
+ toolCallId: string,
292
+ toolName: string,
293
+ result: any
294
+ ): HarmonyMessage {
295
+ return {
296
+ role: 'tool',
297
+ name: toolName,
298
+ tool_call_id: toolCallId,
299
+ content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
300
+ };
301
+ }