@nexagent-cli/cli 0.2.3 → 0.3.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 +2 -2
- package/dist/agent/loop.d.ts +0 -1
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +0 -10
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/prompt.d.ts.map +1 -1
- package/dist/agent/prompt.js.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +167 -0
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dashboard.d.ts +7 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +273 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/mcp.d.ts +7 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +99 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/memory.d.ts.map +1 -1
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/models.d.ts.map +1 -1
- package/dist/commands/models.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +1 -16
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +5 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/wizard.d.ts.map +1 -1
- package/dist/config/wizard.js.map +1 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/database.js.map +1 -1
- package/dist/db/session-store.d.ts.map +1 -1
- package/dist/db/session-store.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/providers/anthropic.d.ts.map +1 -1
- package/dist/llm/providers/anthropic.js.map +1 -1
- package/dist/llm/providers/google.d.ts.map +1 -1
- package/dist/llm/providers/google.js.map +1 -1
- package/dist/llm/providers/ollama.d.ts.map +1 -1
- package/dist/llm/providers/ollama.js.map +1 -1
- package/dist/llm/providers/openai.d.ts.map +1 -1
- package/dist/llm/providers/openai.js +8 -0
- package/dist/llm/providers/openai.js.map +1 -1
- package/dist/llm/router.d.ts.map +1 -1
- package/dist/llm/router.js +2 -0
- package/dist/llm/router.js.map +1 -1
- package/dist/mcp/catalog.d.ts +11 -0
- package/dist/mcp/catalog.d.ts.map +1 -0
- package/dist/mcp/catalog.js +114 -0
- package/dist/mcp/catalog.js.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/installer.d.ts +13 -0
- package/dist/mcp/installer.d.ts.map +1 -0
- package/dist/mcp/installer.js +92 -0
- package/dist/mcp/installer.js.map +1 -0
- package/dist/mcp/registry.d.ts +13 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +81 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/types.d.ts +44 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +5 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/memory/manager.d.ts.map +1 -1
- package/dist/memory/manager.js.map +1 -1
- package/dist/orchestrator/index.d.ts +36 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +118 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/types.d.ts +73 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/orchestrator/types.js +5 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/orchestrator/worker.d.ts +2 -0
- package/dist/orchestrator/worker.d.ts.map +1 -0
- package/dist/orchestrator/worker.js +158 -0
- package/dist/orchestrator/worker.js.map +1 -0
- package/dist/safety/gate.d.ts.map +1 -1
- package/dist/safety/gate.js.map +1 -1
- package/dist/tools/file-delete.d.ts.map +1 -1
- package/dist/tools/file-delete.js.map +1 -1
- package/dist/tools/file-edit.d.ts.map +1 -1
- package/dist/tools/file-edit.js.map +1 -1
- package/dist/tools/file-list.d.ts.map +1 -1
- package/dist/tools/file-list.js.map +1 -1
- package/dist/tools/file-read.d.ts.map +1 -1
- package/dist/tools/file-read.js.map +1 -1
- package/dist/tools/file-search.d.ts.map +1 -1
- package/dist/tools/file-search.js.map +1 -1
- package/dist/tools/file-write.d.ts.map +1 -1
- package/dist/tools/file-write.js.map +1 -1
- package/dist/tools/memory-read.d.ts.map +1 -1
- package/dist/tools/memory-read.js.map +1 -1
- package/dist/tools/memory-write.d.ts.map +1 -1
- package/dist/tools/memory-write.js.map +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/shell-exec.d.ts.map +1 -1
- package/dist/tools/shell-exec.js.map +1 -1
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js.map +1 -1
- package/dist/ui/ChatApp.d.ts +8 -1
- package/dist/ui/ChatApp.d.ts.map +1 -1
- package/dist/ui/ChatApp.js +60 -3
- package/dist/ui/ChatApp.js.map +1 -1
- package/dist/ui/ConfirmDialog.d.ts.map +1 -1
- package/dist/ui/ConfirmDialog.js.map +1 -1
- package/dist/ui/InputBar.d.ts +1 -0
- package/dist/ui/InputBar.d.ts.map +1 -1
- package/dist/ui/InputBar.js +38 -6
- package/dist/ui/InputBar.js.map +1 -1
- package/dist/ui/MessageList.d.ts.map +1 -1
- package/dist/ui/MessageList.js.map +1 -1
- package/dist/ui/Spinner.d.ts.map +1 -1
- package/dist/ui/Spinner.js.map +1 -1
- package/dist/ui/StatusBar.d.ts +3 -1
- package/dist/ui/StatusBar.d.ts.map +1 -1
- package/dist/ui/StatusBar.js +18 -3
- package/dist/ui/StatusBar.js.map +1 -1
- package/dist/ui/ToolPanel.d.ts.map +1 -1
- package/dist/ui/ToolPanel.js.map +1 -1
- package/dist/ui/quick-commands.d.ts +9 -0
- package/dist/ui/quick-commands.d.ts.map +1 -0
- package/dist/ui/quick-commands.js +32 -0
- package/dist/ui/quick-commands.js.map +1 -0
- package/dist/ui/splash.d.ts +3 -0
- package/dist/ui/splash.d.ts.map +1 -0
- package/dist/ui/splash.js +34 -0
- package/dist/ui/splash.js.map +1 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/env.d.ts.map +1 -1
- package/dist/utils/env.js.map +1 -1
- package/package.json +33 -33
- package/dist/subagents/orchestrator.d.ts +0 -20
- package/dist/subagents/orchestrator.d.ts.map +0 -1
- package/dist/subagents/orchestrator.js +0 -151
- package/dist/subagents/orchestrator.js.map +0 -1
- package/proxy-server.mjs +0 -272
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexagent-cli/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "NexAgent — locally-runnable AI agent CLI with file editing, tools, and beautiful TUI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"nexagent": "./dist/index.js"
|
|
@@ -12,65 +12,65 @@
|
|
|
12
12
|
"dist/**/*",
|
|
13
13
|
"README.md",
|
|
14
14
|
"CHANGELOG.md",
|
|
15
|
-
"LICENSE"
|
|
16
|
-
"proxy-server.mjs"
|
|
15
|
+
"LICENSE"
|
|
17
16
|
],
|
|
18
17
|
"publishConfig": {
|
|
19
18
|
"access": "public"
|
|
20
19
|
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsc -p tsconfig.json",
|
|
23
|
-
"build:npm": "node compile-tsc.cjs",
|
|
24
|
-
"bundle": "node ../../build/bundle.mjs",
|
|
25
|
-
"dev": "tsx watch src/index.ts",
|
|
26
|
-
"start": "tsx src/index.ts",
|
|
27
|
-
"typecheck": "tsc --noEmit",
|
|
28
|
-
"test": "vitest run",
|
|
29
|
-
"test:watch": "vitest",
|
|
30
|
-
"lint": "eslint src/",
|
|
31
|
-
"clean": "rm -rf dist/",
|
|
32
|
-
"prepublishOnly": "npm run build:npm"
|
|
33
|
-
},
|
|
34
20
|
"dependencies": {
|
|
35
|
-
"commander": "^12.1.0",
|
|
36
|
-
"ink": "^5.0.1",
|
|
37
|
-
"ink-spinner": "^5.0.0",
|
|
38
|
-
"react": "^18.3.0",
|
|
39
|
-
"@inquirer/prompts": "^5.0.0",
|
|
40
|
-
"better-sqlite3": "^11.0.0",
|
|
41
|
-
"openai": "^4.57.0",
|
|
42
21
|
"@anthropic-ai/sdk": "^0.29.0",
|
|
43
22
|
"@google/generative-ai": "^0.19.0",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"ora": "^8.1.0",
|
|
23
|
+
"@inquirer/prompts": "^5.0.0",
|
|
24
|
+
"better-sqlite3": "^11.0.0",
|
|
47
25
|
"boxen": "^8.0.0",
|
|
26
|
+
"chalk": "^5.3.0",
|
|
27
|
+
"commander": "^12.1.0",
|
|
28
|
+
"cors": "^2.8.5",
|
|
29
|
+
"diff": "^7.0.0",
|
|
30
|
+
"dotenv": "^16.4.5",
|
|
31
|
+
"express": "^4.21.0",
|
|
48
32
|
"figures": "^6.1.0",
|
|
49
33
|
"glob": "^11.0.0",
|
|
50
34
|
"ignore": "^5.3.0",
|
|
35
|
+
"ink": "^5.0.1",
|
|
36
|
+
"ink-spinner": "^5.0.0",
|
|
51
37
|
"js-tiktoken": "^1.0.12",
|
|
38
|
+
"openai": "^4.57.0",
|
|
39
|
+
"ora": "^8.1.0",
|
|
40
|
+
"react": "^18.3.0",
|
|
52
41
|
"strip-ansi": "^7.1.0",
|
|
53
42
|
"wrap-ansi": "^9.0.0",
|
|
54
43
|
"yaml": "^2.5.0",
|
|
55
44
|
"zod": "^3.23.0",
|
|
56
|
-
"@nexagent-cli/shared": "
|
|
57
|
-
"express": "^4.21.0",
|
|
58
|
-
"cors": "^2.8.5",
|
|
59
|
-
"dotenv": "^16.4.5"
|
|
45
|
+
"@nexagent-cli/shared": "0.3.0"
|
|
60
46
|
},
|
|
61
47
|
"optionalDependencies": {
|
|
62
48
|
"keytar": "^7.9.0"
|
|
63
49
|
},
|
|
64
50
|
"devDependencies": {
|
|
65
51
|
"@types/better-sqlite3": "^7.6.11",
|
|
52
|
+
"@types/cors": "^2.8.19",
|
|
66
53
|
"@types/diff": "^5.2.1",
|
|
67
|
-
"@types/
|
|
54
|
+
"@types/express": "^4.17.25",
|
|
68
55
|
"@types/node": "^22.0.0",
|
|
69
|
-
"
|
|
56
|
+
"@types/react": "^18.3.0",
|
|
70
57
|
"tsx": "^4.15.0",
|
|
58
|
+
"typescript": "^5.5.0",
|
|
71
59
|
"vitest": "^2.0.0"
|
|
72
60
|
},
|
|
73
61
|
"engines": {
|
|
74
62
|
"node": ">=22.0.0"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "tsc -p tsconfig.json",
|
|
66
|
+
"build:npm": "node compile-tsc.cjs",
|
|
67
|
+
"bundle": "node ../../build/bundle.mjs",
|
|
68
|
+
"dev": "tsx watch src/index.ts",
|
|
69
|
+
"start": "tsx src/index.ts",
|
|
70
|
+
"typecheck": "tsc --noEmit",
|
|
71
|
+
"test": "vitest run",
|
|
72
|
+
"test:watch": "vitest",
|
|
73
|
+
"lint": "eslint src/",
|
|
74
|
+
"clean": "rm -rf dist/"
|
|
75
75
|
}
|
|
76
|
-
}
|
|
76
|
+
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { AgentConfig, LLMProvider } from '@nexagent-cli/shared';
|
|
2
|
-
import type { MemoryManager } from '../memory/manager.js';
|
|
3
|
-
import type { SessionStore } from '../db/session-store.js';
|
|
4
|
-
import type { AgentEventEmitter } from '../agent/loop.js';
|
|
5
|
-
export declare class SubagentOrchestrator {
|
|
6
|
-
private llm;
|
|
7
|
-
private memory;
|
|
8
|
-
private sessions;
|
|
9
|
-
private events;
|
|
10
|
-
private config;
|
|
11
|
-
constructor(llm: LLMProvider, memory: MemoryManager, sessions: SessionStore, events: AgentEventEmitter, config: AgentConfig);
|
|
12
|
-
runPostSession(sessionId: string): Promise<void>;
|
|
13
|
-
/** Skill Scanner: detect new tools/patterns the agent should learn */
|
|
14
|
-
private runSkillScanner;
|
|
15
|
-
/** Prompt Improver: suggest better system prompt based on session */
|
|
16
|
-
private runPromptImprover;
|
|
17
|
-
/** Context Refiner: compress/archive old conversation summaries */
|
|
18
|
-
private runContextRefiner;
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../../../src/subagents/orchestrator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAQ,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAS,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,qBAAa,oBAAoB;IAE7B,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;gBAJN,GAAG,EAAO,WAAW,EACrB,MAAM,EAAI,aAAa,EACvB,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAI,iBAAiB,EAC3B,MAAM,EAAI,WAAW;IAGzB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCtD,sEAAsE;YACxD,eAAe;IAmC7B,qEAAqE;YACvD,iBAAiB;IAgD/B,mEAAmE;YACrD,iBAAiB;CA6BhC"}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Subagent Orchestrator — post-session background intelligence
|
|
3
|
-
// Runs: Skill Scanner, Prompt Improver, Context Refiner
|
|
4
|
-
// All operate on read-only memory snapshots.
|
|
5
|
-
// Writes go through proposal queue, NOT direct memory writes.
|
|
6
|
-
// ============================================================
|
|
7
|
-
export class SubagentOrchestrator {
|
|
8
|
-
llm;
|
|
9
|
-
memory;
|
|
10
|
-
sessions;
|
|
11
|
-
events;
|
|
12
|
-
config;
|
|
13
|
-
constructor(llm, memory, sessions, events, config) {
|
|
14
|
-
this.llm = llm;
|
|
15
|
-
this.memory = memory;
|
|
16
|
-
this.sessions = sessions;
|
|
17
|
-
this.events = events;
|
|
18
|
-
this.config = config;
|
|
19
|
-
}
|
|
20
|
-
async runPostSession(sessionId) {
|
|
21
|
-
const turns = this.sessions.getTurns(sessionId);
|
|
22
|
-
if (turns.length < 2)
|
|
23
|
-
return; // Not enough data to learn from
|
|
24
|
-
const conversationText = turns
|
|
25
|
-
.map(t => `${t.role.toUpperCase()}: ${t.content.slice(0, 500)}`)
|
|
26
|
-
.join('\n\n');
|
|
27
|
-
// Run subagents in parallel (read-only)
|
|
28
|
-
const [skillResult, improverResult, refinerResult] = await Promise.allSettled([
|
|
29
|
-
this.runSkillScanner(conversationText),
|
|
30
|
-
this.runPromptImprover(conversationText),
|
|
31
|
-
this.runContextRefiner(conversationText, sessionId),
|
|
32
|
-
]);
|
|
33
|
-
// Log any errors silently (subagents must never crash the main flow)
|
|
34
|
-
for (const [name, result] of [
|
|
35
|
-
['skill_scanner', skillResult],
|
|
36
|
-
['prompt_improver', improverResult],
|
|
37
|
-
['context_refiner', refinerResult],
|
|
38
|
-
]) {
|
|
39
|
-
if (result.status === 'rejected') {
|
|
40
|
-
console.error(`[subagent:${name}] Error: ${String(result.reason)}`);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
this.events.emit('subagent:done', {
|
|
44
|
-
subagentType: name,
|
|
45
|
-
proposalCount: result.value,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
/** Skill Scanner: detect new tools/patterns the agent should learn */
|
|
51
|
-
async runSkillScanner(conversationText) {
|
|
52
|
-
this.events.emit('subagent:spawn', {
|
|
53
|
-
subagentType: 'skill_scanner',
|
|
54
|
-
reason: 'Post-session skill analysis',
|
|
55
|
-
});
|
|
56
|
-
const prompt = [
|
|
57
|
-
'You are a skill scanner analysing a conversation to identify reusable patterns.',
|
|
58
|
-
'Review the conversation and identify:',
|
|
59
|
-
'1. Recurring task patterns that could be templated',
|
|
60
|
-
'2. User preferences revealed (coding style, file naming, etc.)',
|
|
61
|
-
'3. Domain knowledge demonstrated',
|
|
62
|
-
'',
|
|
63
|
-
'Respond with a JSON object:',
|
|
64
|
-
'{ "patterns": [{ "type": "string", "description": "string", "example": "string" }] }',
|
|
65
|
-
'',
|
|
66
|
-
'Conversation:',
|
|
67
|
-
conversationText.slice(0, 4000),
|
|
68
|
-
].join('\n');
|
|
69
|
-
const response = await this.llm.complete({
|
|
70
|
-
model: this.config.model,
|
|
71
|
-
messages: [{ role: 'user', content: prompt }],
|
|
72
|
-
temperature: 0.3,
|
|
73
|
-
maxTokens: 512,
|
|
74
|
-
});
|
|
75
|
-
try {
|
|
76
|
-
const parsed = JSON.parse(response.content);
|
|
77
|
-
return parsed.patterns?.length ?? 0;
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
return 0;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
/** Prompt Improver: suggest better system prompt based on session */
|
|
84
|
-
async runPromptImprover(conversationText) {
|
|
85
|
-
this.events.emit('subagent:spawn', {
|
|
86
|
-
subagentType: 'prompt_improver',
|
|
87
|
-
reason: 'Post-session prompt analysis',
|
|
88
|
-
});
|
|
89
|
-
const currentMemory = await this.memory.read({ limit: 5 });
|
|
90
|
-
const memoryText = currentMemory.map(b => `[${b.blockType}]: ${b.content.slice(0, 200)}`).join('\n');
|
|
91
|
-
const prompt = [
|
|
92
|
-
'You are a prompt improver. Based on the conversation and current memory,',
|
|
93
|
-
'identify if any memory blocks should be updated to better serve the user.',
|
|
94
|
-
'',
|
|
95
|
-
'Current memory blocks:',
|
|
96
|
-
memoryText || '(empty)',
|
|
97
|
-
'',
|
|
98
|
-
'Conversation excerpt:',
|
|
99
|
-
conversationText.slice(0, 2000),
|
|
100
|
-
'',
|
|
101
|
-
'If you identify improvements, respond with JSON:',
|
|
102
|
-
'{ "updates": [{ "blockType": "string", "content": "string", "rationale": "string" }] }',
|
|
103
|
-
'Otherwise respond with: { "updates": [] }',
|
|
104
|
-
].join('\n');
|
|
105
|
-
const response = await this.llm.complete({
|
|
106
|
-
model: this.config.model,
|
|
107
|
-
messages: [{ role: 'user', content: prompt }],
|
|
108
|
-
temperature: 0.2,
|
|
109
|
-
maxTokens: 512,
|
|
110
|
-
});
|
|
111
|
-
try {
|
|
112
|
-
const parsed = JSON.parse(response.content);
|
|
113
|
-
// Queue proposals (don't write directly)
|
|
114
|
-
for (const update of parsed.updates ?? []) {
|
|
115
|
-
this.memory.upsertBlock(update.blockType, update.content);
|
|
116
|
-
}
|
|
117
|
-
return parsed.updates?.length ?? 0;
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return 0;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
/** Context Refiner: compress/archive old conversation summaries */
|
|
124
|
-
async runContextRefiner(conversationText, _sessionId) {
|
|
125
|
-
this.events.emit('subagent:spawn', {
|
|
126
|
-
subagentType: 'context_refiner',
|
|
127
|
-
reason: 'Post-session context compression',
|
|
128
|
-
});
|
|
129
|
-
const prompt = [
|
|
130
|
-
'You are a context refiner. Summarise the key facts from this conversation',
|
|
131
|
-
'that should be remembered for future sessions. Be concise (max 200 words).',
|
|
132
|
-
'',
|
|
133
|
-
'Conversation:',
|
|
134
|
-
conversationText.slice(0, 3000),
|
|
135
|
-
'',
|
|
136
|
-
'Respond with ONLY the summary text, no JSON.',
|
|
137
|
-
].join('\n');
|
|
138
|
-
const response = await this.llm.complete({
|
|
139
|
-
model: this.config.model,
|
|
140
|
-
messages: [{ role: 'user', content: prompt }],
|
|
141
|
-
temperature: 0.2,
|
|
142
|
-
maxTokens: 300,
|
|
143
|
-
});
|
|
144
|
-
if (response.content.trim().length > 20) {
|
|
145
|
-
this.memory.upsertBlock('conversation_summary', response.content.trim());
|
|
146
|
-
return 1;
|
|
147
|
-
}
|
|
148
|
-
return 0;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../../../src/subagents/orchestrator.ts"],"names":[],"mappings":"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"}
|
package/proxy-server.mjs
DELETED
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import "dotenv/config";
|
|
2
|
-
import express from "express";
|
|
3
|
-
import cors from "cors";
|
|
4
|
-
// ------------------------------------------------------------------
|
|
5
|
-
// Configuration
|
|
6
|
-
// ------------------------------------------------------------------
|
|
7
|
-
const PORT = Number(process.env.PORT) || 18080;
|
|
8
|
-
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
9
|
-
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
10
|
-
const VALID_API_KEYS = new Set(["nxag_test_123456789"]);
|
|
11
|
-
// ------------------------------------------------------------------
|
|
12
|
-
// Express setup
|
|
13
|
-
// ------------------------------------------------------------------
|
|
14
|
-
const app = express();
|
|
15
|
-
app.use(cors());
|
|
16
|
-
app.use(express.json({ limit: "10mb" }));
|
|
17
|
-
// ------------------------------------------------------------------
|
|
18
|
-
// Logging helpers
|
|
19
|
-
// ------------------------------------------------------------------
|
|
20
|
-
function logInfo(msg, meta) {
|
|
21
|
-
const metaStr = meta ? " " + JSON.stringify(meta) : "";
|
|
22
|
-
console.log(`[INFO] ${msg}${metaStr}`);
|
|
23
|
-
}
|
|
24
|
-
function logWarn(msg, meta) {
|
|
25
|
-
const metaStr = meta ? " " + JSON.stringify(meta) : "";
|
|
26
|
-
console.warn(`[WARN] ${msg}${metaStr}`);
|
|
27
|
-
}
|
|
28
|
-
function logError(msg, meta) {
|
|
29
|
-
const metaStr = meta ? " " + JSON.stringify(meta) : "";
|
|
30
|
-
console.error(`[ERROR] ${msg}${metaStr}`);
|
|
31
|
-
}
|
|
32
|
-
// ------------------------------------------------------------------
|
|
33
|
-
// Auth middleware
|
|
34
|
-
// ------------------------------------------------------------------
|
|
35
|
-
function authMiddleware(req, res, next) {
|
|
36
|
-
const authHeader = req.headers.authorization;
|
|
37
|
-
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
38
|
-
res.status(401).json({ error: "Missing or invalid Authorization header" });
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
const token = authHeader.slice("Bearer ".length).trim();
|
|
42
|
-
if (!VALID_API_KEYS.has(token)) {
|
|
43
|
-
res.status(403).json({ error: "Invalid API key" });
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
// Attach key identifier (safe prefix) for logging
|
|
47
|
-
req.apiKeyPrefix = token.slice(0, 12) + "…";
|
|
48
|
-
next();
|
|
49
|
-
}
|
|
50
|
-
// ------------------------------------------------------------------
|
|
51
|
-
// Usage tracking helpers
|
|
52
|
-
// ------------------------------------------------------------------
|
|
53
|
-
function logUsage(provider, apiKeyPrefix, model, usage) {
|
|
54
|
-
logInfo("Usage", {
|
|
55
|
-
provider,
|
|
56
|
-
apiKey: apiKeyPrefix,
|
|
57
|
-
model,
|
|
58
|
-
promptTokens: usage?.prompt_tokens ?? null,
|
|
59
|
-
completionTokens: usage?.completion_tokens ?? null,
|
|
60
|
-
totalTokens: usage?.total_tokens ?? null,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
// ------------------------------------------------------------------
|
|
64
|
-
// Health endpoint
|
|
65
|
-
// ------------------------------------------------------------------
|
|
66
|
-
app.get("/health", (_req, res) => {
|
|
67
|
-
res.json({ status: "ok", providers: { openai: !!OPENAI_API_KEY, anthropic: !!ANTHROPIC_API_KEY } });
|
|
68
|
-
});
|
|
69
|
-
// ------------------------------------------------------------------
|
|
70
|
-
// OpenAI proxy: POST /v1/chat/completions
|
|
71
|
-
// ------------------------------------------------------------------
|
|
72
|
-
app.post("/v1/chat/completions", authMiddleware, async (req, res) => {
|
|
73
|
-
if (!OPENAI_API_KEY) {
|
|
74
|
-
res.status(503).json({ error: "OpenAI provider is not configured" });
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const model = req.body.model || "unknown";
|
|
78
|
-
const apiKeyPrefix = req.apiKeyPrefix;
|
|
79
|
-
logInfo("OpenAI request", { apiKey: apiKeyPrefix, model });
|
|
80
|
-
try {
|
|
81
|
-
const upstreamRes = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
82
|
-
method: "POST",
|
|
83
|
-
headers: {
|
|
84
|
-
"Content-Type": "application/json",
|
|
85
|
-
Authorization: `Bearer ${OPENAI_API_KEY}`,
|
|
86
|
-
},
|
|
87
|
-
body: JSON.stringify(req.body),
|
|
88
|
-
});
|
|
89
|
-
if (!upstreamRes.ok) {
|
|
90
|
-
const text = await upstreamRes.text();
|
|
91
|
-
logError("OpenAI upstream error", { status: upstreamRes.status, body: text });
|
|
92
|
-
res.status(upstreamRes.status).type("json").send(text);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
// Streamed response
|
|
96
|
-
res.status(200);
|
|
97
|
-
upstreamRes.headers.forEach((value, key) => {
|
|
98
|
-
if (key.toLowerCase() !== "content-encoding") {
|
|
99
|
-
res.setHeader(key, value);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
const reader = upstreamRes.body?.getReader();
|
|
103
|
-
if (!reader) {
|
|
104
|
-
res.status(502).json({ error: "Empty upstream body" });
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
let usageLogged = false;
|
|
108
|
-
const decoder = new TextDecoder();
|
|
109
|
-
try {
|
|
110
|
-
while (true) {
|
|
111
|
-
const { done, value } = await reader.read();
|
|
112
|
-
if (done)
|
|
113
|
-
break;
|
|
114
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
115
|
-
res.write(chunk);
|
|
116
|
-
// Attempt to extract usage from the final chunk in the stream
|
|
117
|
-
if (!usageLogged && chunk.includes('"usage"')) {
|
|
118
|
-
try {
|
|
119
|
-
const lines = chunk.split("\n").filter((l) => l.trim().startsWith("data:"));
|
|
120
|
-
for (const line of lines) {
|
|
121
|
-
const jsonStr = line.replace(/^data:\s*/, "").trim();
|
|
122
|
-
if (jsonStr === "[DONE]")
|
|
123
|
-
continue;
|
|
124
|
-
const parsed = JSON.parse(jsonStr);
|
|
125
|
-
if (parsed?.usage) {
|
|
126
|
-
logUsage("openai", apiKeyPrefix, model, parsed.usage);
|
|
127
|
-
usageLogged = true;
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
// Ignore JSON parse errors in stream chunks
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
finally {
|
|
139
|
-
reader.releaseLock();
|
|
140
|
-
}
|
|
141
|
-
if (!usageLogged) {
|
|
142
|
-
logUsage("openai", apiKeyPrefix, model);
|
|
143
|
-
}
|
|
144
|
-
res.end();
|
|
145
|
-
}
|
|
146
|
-
catch (err) {
|
|
147
|
-
logError("OpenAI proxy error", { error: err.message });
|
|
148
|
-
if (!res.headersSent) {
|
|
149
|
-
res.status(502).json({ error: "Failed to reach OpenAI" });
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
res.end();
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
// ------------------------------------------------------------------
|
|
157
|
-
// Anthropic proxy: POST /v1/messages
|
|
158
|
-
// ------------------------------------------------------------------
|
|
159
|
-
app.post("/v1/messages", authMiddleware, async (req, res) => {
|
|
160
|
-
if (!ANTHROPIC_API_KEY) {
|
|
161
|
-
res.status(503).json({ error: "Anthropic provider is not configured" });
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
const model = req.body.model || "unknown";
|
|
165
|
-
const apiKeyPrefix = req.apiKeyPrefix;
|
|
166
|
-
logInfo("Anthropic request", { apiKey: apiKeyPrefix, model });
|
|
167
|
-
try {
|
|
168
|
-
const upstreamRes = await fetch("https://api.anthropic.com/v1/messages", {
|
|
169
|
-
method: "POST",
|
|
170
|
-
headers: {
|
|
171
|
-
"Content-Type": "application/json",
|
|
172
|
-
"x-api-key": ANTHROPIC_API_KEY,
|
|
173
|
-
"anthropic-version": "2023-06-01",
|
|
174
|
-
},
|
|
175
|
-
body: JSON.stringify(req.body),
|
|
176
|
-
});
|
|
177
|
-
if (!upstreamRes.ok) {
|
|
178
|
-
const text = await upstreamRes.text();
|
|
179
|
-
logError("Anthropic upstream error", { status: upstreamRes.status, body: text });
|
|
180
|
-
res.status(upstreamRes.status).type("json").send(text);
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
res.status(200);
|
|
184
|
-
upstreamRes.headers.forEach((value, key) => {
|
|
185
|
-
if (key.toLowerCase() !== "content-encoding") {
|
|
186
|
-
res.setHeader(key, value);
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
const reader = upstreamRes.body?.getReader();
|
|
190
|
-
if (!reader) {
|
|
191
|
-
res.status(502).json({ error: "Empty upstream body" });
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
let usageLogged = false;
|
|
195
|
-
const decoder = new TextDecoder();
|
|
196
|
-
try {
|
|
197
|
-
while (true) {
|
|
198
|
-
const { done, value } = await reader.read();
|
|
199
|
-
if (done)
|
|
200
|
-
break;
|
|
201
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
202
|
-
res.write(chunk);
|
|
203
|
-
// Anthropic sends usage in message_delta events
|
|
204
|
-
if (!usageLogged && chunk.includes('"usage"')) {
|
|
205
|
-
try {
|
|
206
|
-
const lines = chunk.split("\n").filter((l) => l.trim().startsWith("data:"));
|
|
207
|
-
for (const line of lines) {
|
|
208
|
-
const jsonStr = line.replace(/^data:\s*/, "").trim();
|
|
209
|
-
const parsed = JSON.parse(jsonStr);
|
|
210
|
-
if (parsed?.type === "message_delta" && parsed?.delta?.usage) {
|
|
211
|
-
logUsage("anthropic", apiKeyPrefix, model, {
|
|
212
|
-
prompt_tokens: parsed.delta.usage.input_tokens,
|
|
213
|
-
completion_tokens: parsed.delta.usage.output_tokens,
|
|
214
|
-
total_tokens: (parsed.delta.usage.input_tokens ?? 0) +
|
|
215
|
-
(parsed.delta.usage.output_tokens ?? 0),
|
|
216
|
-
});
|
|
217
|
-
usageLogged = true;
|
|
218
|
-
break;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
// Ignore JSON parse errors in stream chunks
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
finally {
|
|
229
|
-
reader.releaseLock();
|
|
230
|
-
}
|
|
231
|
-
if (!usageLogged) {
|
|
232
|
-
logUsage("anthropic", apiKeyPrefix, model);
|
|
233
|
-
}
|
|
234
|
-
res.end();
|
|
235
|
-
}
|
|
236
|
-
catch (err) {
|
|
237
|
-
logError("Anthropic proxy error", { error: err.message });
|
|
238
|
-
if (!res.headersSent) {
|
|
239
|
-
res.status(502).json({ error: "Failed to reach Anthropic" });
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
res.end();
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
// ------------------------------------------------------------------
|
|
247
|
-
// Global error handler
|
|
248
|
-
// ------------------------------------------------------------------
|
|
249
|
-
app.use((err, _req, res, _next) => {
|
|
250
|
-
logError("Unhandled error", { error: err.message });
|
|
251
|
-
if (!res.headersSent) {
|
|
252
|
-
res.status(500).json({ error: "Internal server error" });
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
// ------------------------------------------------------------------
|
|
256
|
-
// Startup
|
|
257
|
-
// ------------------------------------------------------------------
|
|
258
|
-
app.listen(PORT, "0.0.0.0", () => {
|
|
259
|
-
console.log(`\n╔══════════════════════════════════════════╗`);
|
|
260
|
-
console.log(`║ NexAgent Proxy Gateway ║`);
|
|
261
|
-
console.log(`╠══════════════════════════════════════════╣`);
|
|
262
|
-
console.log(`║ Port: ${PORT.toString().padEnd(27)}║`);
|
|
263
|
-
console.log(`║ OpenAI: ${(OPENAI_API_KEY ? "configured" : "NOT CONFIGURED").padEnd(27)}║`);
|
|
264
|
-
console.log(`║ Anthropic: ${(ANTHROPIC_API_KEY ? "configured" : "NOT CONFIGURED").padEnd(27)}║`);
|
|
265
|
-
console.log(`╚══════════════════════════════════════════╝\n`);
|
|
266
|
-
if (!OPENAI_API_KEY) {
|
|
267
|
-
logWarn("OPENAI_API_KEY is missing. OpenAI requests will return 503.");
|
|
268
|
-
}
|
|
269
|
-
if (!ANTHROPIC_API_KEY) {
|
|
270
|
-
logWarn("ANTHROPIC_API_KEY is missing. Anthropic requests will return 503.");
|
|
271
|
-
}
|
|
272
|
-
});
|