nyxora 26.7.2 → 26.7.4
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/CHANGELOG.md +112 -47
- package/README.md +25 -21
- package/bin/nyxora.mjs +4 -6
- package/dist/launcher.js +44 -8
- package/dist/packages/core/src/agent/bridgeWatcher.js +3 -2
- package/dist/packages/core/src/agent/cronManager.js +2 -2
- package/dist/packages/core/src/agent/llmProvider.js +245 -0
- package/dist/packages/core/src/agent/nyxDaemon.js +97 -0
- package/dist/packages/core/src/agent/osAgent.js +351 -29
- package/dist/packages/core/src/agent/reasoning.js +353 -60
- package/dist/packages/core/src/agent/reasoningScratchpad.js +47 -0
- package/dist/packages/core/src/agent/transactionManager.js +36 -31
- package/dist/packages/core/src/agent/web3Agent.js +263 -37
- package/dist/packages/core/src/cognitive/cognitiveManager.js +63 -10
- package/dist/packages/core/src/config/parser.js +10 -4
- package/dist/packages/core/src/gateway/chat.js +56 -13
- package/dist/packages/core/src/gateway/cli.js +3 -0
- package/dist/packages/core/src/gateway/discordAdapter.js +81 -0
- package/dist/packages/core/src/gateway/googleAuthModule.js +2 -0
- package/dist/packages/core/src/gateway/server.js +58 -9
- package/dist/packages/core/src/gateway/setup-ml.js +64 -0
- package/dist/packages/core/src/gateway/setup.js +39 -3
- package/dist/packages/core/src/gateway/telegram.js +123 -27
- package/dist/packages/core/src/memory/episodic.js +58 -3
- package/dist/packages/core/src/memory/logger.js +120 -2
- package/dist/packages/core/src/memory/promotionEngine.js +18 -5
- package/dist/packages/core/src/system/agentskills.js +10 -0
- package/dist/packages/core/src/system/plugins/GoogleWorkspacePlugin.js +10 -2
- package/dist/packages/core/src/system/plugins/SystemCorePlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemExternalPlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemPluginInstallerPlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemSocialPlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemWebPlugin.js +1 -1
- package/dist/packages/core/src/system/plugins/SystemWorkspacePlugin.js +3 -3
- package/dist/packages/core/src/system/plugins/createSkill.js +1 -1
- package/dist/packages/core/src/system/skillExtractor.js +16 -5
- package/dist/packages/core/src/system/skills/executeShell.js +31 -4
- package/dist/packages/core/src/system/skills/forgetMemory.js +6 -4
- package/dist/packages/core/src/system/skills/googleWorkspace.js +106 -1
- package/dist/packages/core/src/system/skills/scheduleTask.js +7 -2
- package/dist/packages/core/src/system/skills/searchWeb.js +13 -6
- package/dist/packages/core/src/test_mainnet.js +45 -0
- package/dist/packages/core/src/utils/contextSummarizer.js +82 -0
- package/dist/packages/core/src/utils/skillManager.js +1 -1
- package/dist/packages/core/src/utils/streamSimulator.js +85 -0
- package/dist/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.js +28 -11
- package/dist/packages/core/src/web3/aggregator/providers/OpBridgeProvider.js +41 -27
- package/dist/packages/core/src/web3/aggregator/providers/TestnetSwapProvider.js +65 -0
- package/dist/packages/core/src/web3/aggregator/routeSelector.js +7 -1
- package/dist/packages/core/src/web3/plugins/Web3DefiPlugin.js +1 -1
- package/dist/packages/core/src/web3/plugins/Web3MarketPlugin.js +5 -125
- package/dist/packages/core/src/web3/plugins/Web3SecurityPlugin.js +1 -1
- package/dist/packages/core/src/web3/plugins/Web3WalletPlugin.js +1 -1
- package/dist/packages/core/src/web3/skills/bridgeToken.js +19 -4
- package/dist/packages/core/src/web3/skills/checkAddress.js +2 -0
- package/dist/packages/core/src/web3/skills/checkPortfolio.js +22 -2
- package/dist/packages/core/src/web3/skills/checkSecurity.js +2 -0
- package/dist/packages/core/src/web3/skills/confirmPendingTx.js +1 -1
- package/dist/packages/core/src/web3/skills/createLimitOrder.js +15 -1
- package/dist/packages/core/src/web3/skills/customTx.js +3 -0
- package/dist/packages/core/src/web3/skills/defiLending.js +2 -0
- package/dist/packages/core/src/web3/skills/getBalance.js +2 -0
- package/dist/packages/core/src/web3/skills/getPrice.js +134 -27
- package/dist/packages/core/src/web3/skills/getTxHistory.js +2 -0
- package/dist/packages/core/src/web3/skills/marketAnalysis.js +28 -191
- package/dist/packages/core/src/web3/skills/mintNft.js +3 -0
- package/dist/packages/core/src/web3/skills/provideLiquidity.js +16 -12
- package/dist/packages/core/src/web3/skills/revokeApprovals.js +2 -0
- package/dist/packages/core/src/web3/skills/swapToken.js +20 -2
- package/dist/packages/core/src/web3/skills/transfer.js +2 -0
- package/dist/packages/core/src/web3/skills/yieldVault.js +2 -0
- package/dist/packages/core/src/web3/utils/chains.js +19 -0
- package/dist/packages/core/src/web3/utils/marketEngine.js +26 -33
- package/dist/packages/signer/src/NyxoraSigner.js +181 -0
- package/dist/packages/signer/src/index.js +17 -0
- package/dist/packages/signer/src/server.js +25 -161
- package/launcher.ts +38 -9
- package/package.json +8 -3
- package/packages/core/package.json +21 -10
- package/packages/core/src/agent/bridgeWatcher.ts +3 -2
- package/packages/core/src/agent/cronManager.ts +2 -2
- package/packages/core/src/agent/llmProvider.ts +221 -0
- package/packages/core/src/agent/nyxDaemon.ts +104 -0
- package/packages/core/src/agent/osAgent.ts +381 -32
- package/packages/core/src/agent/reasoning.ts +376 -64
- package/packages/core/src/agent/reasoningScratchpad.ts +45 -0
- package/packages/core/src/agent/transactionManager.ts +36 -28
- package/packages/core/src/agent/web3Agent.ts +291 -40
- package/packages/core/src/cognitive/cognitiveManager.ts +65 -10
- package/packages/core/src/cognitive/prompts/web3/market-analysis.md +24 -0
- package/packages/core/src/cognitive/prompts/web3/portfolio-review.md +29 -0
- package/packages/core/src/cognitive/prompts/web3/risk-assessment.md +28 -0
- package/packages/core/src/cognitive/prompts/web3/trade-planning.md +30 -0
- package/packages/core/src/config/parser.ts +15 -4
- package/packages/core/src/gateway/chat.ts +57 -15
- package/packages/core/src/gateway/cli.ts +4 -0
- package/packages/core/src/gateway/discordAdapter.ts +87 -0
- package/packages/core/src/gateway/googleAuthModule.ts +2 -0
- package/packages/core/src/gateway/server.ts +66 -10
- package/packages/core/src/gateway/setup-ml.ts +68 -0
- package/packages/core/src/gateway/setup.ts +41 -3
- package/packages/core/src/gateway/telegram.ts +141 -30
- package/packages/core/src/memory/episodic.ts +76 -3
- package/packages/core/src/memory/logger.ts +142 -2
- package/packages/core/src/memory/promotionEngine.ts +19 -5
- package/packages/core/src/system/agentskills.ts +10 -0
- package/packages/core/src/system/plugins/GoogleWorkspacePlugin.ts +15 -3
- package/packages/core/src/system/plugins/SystemCorePlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemExternalPlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemPluginInstallerPlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemSocialPlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemWebPlugin.ts +1 -1
- package/packages/core/src/system/plugins/SystemWorkspacePlugin.ts +3 -3
- package/packages/core/src/system/plugins/createSkill.ts +1 -1
- package/packages/core/src/system/skillExtractor.ts +16 -5
- package/packages/core/src/system/skills/executeShell.ts +38 -7
- package/packages/core/src/system/skills/forgetMemory.ts +7 -4
- package/packages/core/src/system/skills/googleWorkspace.ts +109 -0
- package/packages/core/src/system/skills/scheduleTask.ts +7 -2
- package/packages/core/src/system/skills/searchWeb.ts +12 -6
- package/packages/core/src/utils/contextSummarizer.ts +100 -0
- package/packages/core/src/utils/skillManager.ts +1 -1
- package/packages/core/src/utils/streamSimulator.ts +88 -0
- package/packages/core/src/web3/aggregator/providers/ArbitrumBridgeProvider.ts +30 -11
- package/packages/core/src/web3/aggregator/providers/OpBridgeProvider.ts +43 -29
- package/packages/core/src/web3/aggregator/routeSelector.ts +6 -1
- package/packages/core/src/web3/plugins/Web3DefiPlugin.ts +1 -1
- package/packages/core/src/web3/plugins/Web3MarketPlugin.ts +6 -125
- package/packages/core/src/web3/plugins/Web3SecurityPlugin.ts +1 -1
- package/packages/core/src/web3/plugins/Web3WalletPlugin.ts +1 -1
- package/packages/core/src/web3/skills/bridgeToken.ts +21 -7
- package/packages/core/src/web3/skills/checkAddress.ts +2 -0
- package/packages/core/src/web3/skills/checkPortfolio.ts +23 -2
- package/packages/core/src/web3/skills/checkSecurity.ts +2 -0
- package/packages/core/src/web3/skills/confirmPendingTx.ts +1 -1
- package/packages/core/src/web3/skills/createLimitOrder.ts +17 -2
- package/packages/core/src/web3/skills/customTx.ts +3 -0
- package/packages/core/src/web3/skills/defiLending.ts +2 -0
- package/packages/core/src/web3/skills/getBalance.ts +2 -0
- package/packages/core/src/web3/skills/getPrice.ts +134 -30
- package/packages/core/src/web3/skills/getTxHistory.ts +2 -0
- package/packages/core/src/web3/skills/marketAnalysis.ts +45 -188
- package/packages/core/src/web3/skills/mintNft.ts +3 -0
- package/packages/core/src/web3/skills/provideLiquidity.ts +16 -10
- package/packages/core/src/web3/skills/revokeApprovals.ts +2 -0
- package/packages/core/src/web3/skills/swapToken.ts +20 -3
- package/packages/core/src/web3/skills/transfer.ts +2 -0
- package/packages/core/src/web3/skills/yieldVault.ts +2 -0
- package/packages/core/src/web3/utils/chains.ts +12 -0
- package/packages/core/src/web3/utils/marketEngine.ts +27 -33
- package/packages/dashboard/dist/assets/index-Czxksiao.js +18 -0
- package/packages/dashboard/dist/assets/index-DK2sTU47.css +1 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package.json +10 -10
- package/packages/mcp-server/dist/server.js +5 -1
- package/packages/mcp-server/package.json +6 -6
- package/packages/mcp-server/src/server.ts +11 -7
- package/packages/ml-engine/__pycache__/config.cpython-313.pyc +0 -0
- package/packages/ml-engine/__pycache__/main.cpython-313.pyc +0 -0
- package/packages/ml-engine/config.py +59 -0
- package/packages/ml-engine/main.py +34 -0
- package/packages/ml-engine/requirements.txt +22 -0
- package/packages/ml-engine/routers/__init__.py +1 -0
- package/packages/ml-engine/routers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/__pycache__/cognitive.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/__pycache__/critic.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/__pycache__/llm.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/__pycache__/market.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/__pycache__/memory.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/cognitive.py +98 -0
- package/packages/ml-engine/routers/critic.py +111 -0
- package/packages/ml-engine/routers/llm.py +38 -0
- package/packages/ml-engine/routers/market.py +332 -0
- package/packages/ml-engine/routers/memory.py +125 -0
- package/packages/policy/package.json +3 -3
- package/packages/signer/package.json +16 -6
- package/packages/signer/src/NyxoraSigner.ts +161 -0
- package/packages/signer/src/index.ts +1 -0
- package/packages/signer/src/server.ts +25 -135
- package/dist/packages/core/src/agent/honchoDaemon.js +0 -91
- package/dist/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -54
- package/dist/packages/core/src/gateway/test.js +0 -15
- package/dist/packages/core/src/web3/Web3DefiPlugin.js +0 -28
- package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +0 -260
- package/dist/packages/core/src/web3/aggregator/aggregatorTestnet.js +0 -119
- package/dist/packages/core/src/web3/skills/nativeOpBridge.js +0 -71
- package/packages/core/src/agent/honchoDaemon.ts +0 -96
- package/packages/core/src/cognitive/prompts/autonomous/binance-trading-integration.md +0 -41
- package/packages/core/src/plugin/registry.test.ts +0 -46
- package/packages/core/src/utils/formatter.test.ts +0 -41
- package/packages/dashboard/dist/assets/index-CSoNa5cx.css +0 -1
- package/packages/dashboard/dist/assets/index-DbRWEoSr.js +0 -16
|
@@ -5,15 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.logger = void 0;
|
|
7
7
|
exports.processUserInput = processUserInput;
|
|
8
|
+
exports.processUserInputStream = processUserInputStream;
|
|
8
9
|
const fs_1 = __importDefault(require("fs"));
|
|
9
10
|
const parser_1 = require("../config/parser");
|
|
10
11
|
const logger_1 = require("../memory/logger");
|
|
11
12
|
const episodic_1 = require("../memory/episodic");
|
|
13
|
+
const streamSimulator_1 = require("../utils/streamSimulator");
|
|
12
14
|
const paths_1 = require("../config/paths");
|
|
13
15
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
14
16
|
exports.logger = new logger_1.Logger();
|
|
15
17
|
const llmUtils_1 = require("../utils/llmUtils");
|
|
16
|
-
|
|
18
|
+
const transactionManager_1 = require("./transactionManager");
|
|
19
|
+
async function getSystemPrompt(context = 'general', userInput = '') {
|
|
17
20
|
const config = (0, parser_1.loadConfig)();
|
|
18
21
|
const currentDateTime = new Date().toLocaleString('en-US');
|
|
19
22
|
let basePrompt = "";
|
|
@@ -27,7 +30,7 @@ IMPORTANT: The <think> block is strictly for internal monologue. Your final answ
|
|
|
27
30
|
|
|
28
31
|
[WEB3 EXECUTION WORKFLOW]
|
|
29
32
|
CRITICAL RULE 1: NEVER expose internal JSON tool calls. Explain the outcome naturally.
|
|
30
|
-
CRITICAL RULE
|
|
33
|
+
CRITICAL RULE: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt, UNLESS the Episodic Memories or Cognitive Skills specify a strict language preference.
|
|
31
34
|
CRITICAL RULE 3: DEFAULT CHAIN HANDLING. Default to: ${config.agent.default_chain} unless specified.
|
|
32
35
|
CRITICAL RULE 4: CONDITIONAL PARALLEL EXECUTION. Parallel tool execution is ONLY allowed if there are zero data dependencies.
|
|
33
36
|
CRITICAL RULE 5: TRANSACTION EXECUTION. For ALL state-changing transactions (swap, bridge, transfer), execute IMMEDIATELY. It will trigger a secure popup.
|
|
@@ -45,7 +48,7 @@ IMPORTANT: The <think> block is strictly for internal monologue. Your final answ
|
|
|
45
48
|
|
|
46
49
|
[OS EXECUTION WORKFLOW]
|
|
47
50
|
CRITICAL RULE 1: NEVER expose internal JSON tool calls. Explain the outcome naturally.
|
|
48
|
-
CRITICAL RULE
|
|
51
|
+
CRITICAL RULE: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt, UNLESS the Episodic Memories or Cognitive Skills specify a strict language preference.
|
|
49
52
|
CRITICAL RULE 3: FILE SYSTEM SAFETY. You are STRICTLY FORBIDDEN from modifying config.yaml, rpc_key.yaml, or policy.yaml using terminal commands like sed or echo.
|
|
50
53
|
CRITICAL RULE 4: CRON JOBS VS LIMIT ORDERS. Do NOT use schedule_task for price-based trading triggers. Use schedule_task for time-based recurring tasks.
|
|
51
54
|
CRITICAL RULE 5: TOOL CONFIDENCE. NEVER fabricate file contents or command outputs.`;
|
|
@@ -58,7 +61,7 @@ CRITICAL: You MUST use a Chain of Thought approach for every response. Enclose y
|
|
|
58
61
|
IMPORTANT: The <think> block is strictly for internal monologue. Your final answer must be OUTSIDE and AFTER the </think> tag.
|
|
59
62
|
|
|
60
63
|
[GENERAL WORKFLOW]
|
|
61
|
-
CRITICAL RULE 1: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt.
|
|
64
|
+
CRITICAL RULE 1: STRICT LANGUAGE MATCHING. Reply in the exact same language as the user's LATEST prompt, UNLESS the Episodic Memories or Cognitive Skills specify a strict language preference.
|
|
62
65
|
CRITICAL RULE 2: BE HELPFUL AND CONCISE. You do not have Web3 or OS tools in this context. If the user asks for Web3 or OS tasks, politely inform them to rephrase using clear keywords like 'transfer', 'harga', 'file', 'email', etc.`;
|
|
63
66
|
}
|
|
64
67
|
const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
|
|
@@ -121,46 +124,170 @@ Do NOT perform any web3 tasks or generic answers until they provide all 4 detail
|
|
|
121
124
|
catch (error) {
|
|
122
125
|
console.error('Failed to read policy.yaml:', error);
|
|
123
126
|
}
|
|
124
|
-
// Inject Episodic Memories
|
|
127
|
+
// Inject Episodic Memories via Python RAG
|
|
125
128
|
try {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const ragRes = await fetch('http://127.0.0.1:8000/memory/rag', {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: { 'Content-Type': 'application/json' },
|
|
132
|
+
body: JSON.stringify({ query: userInput, top_k: 5 })
|
|
133
|
+
});
|
|
134
|
+
if (ragRes.ok) {
|
|
135
|
+
const ragData = await ragRes.json();
|
|
136
|
+
if (ragData.memories && ragData.memories.length > 0) {
|
|
137
|
+
basePrompt += `\n\n--- EPISODIC MEMORIES (SMART SUGGESTIONS) ---\n`;
|
|
138
|
+
ragData.memories.forEach((mem) => {
|
|
139
|
+
basePrompt += `- ${mem}\n`;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
132
142
|
}
|
|
133
143
|
}
|
|
134
|
-
catch {
|
|
144
|
+
catch (e) {
|
|
145
|
+
// Fallback or ignore if Python ML engine is down
|
|
146
|
+
}
|
|
135
147
|
// V3: Inject Personalized Risk Profile
|
|
136
148
|
try {
|
|
137
149
|
const profile = exports.logger.getUserProfile();
|
|
138
|
-
|
|
139
|
-
if (profile || personas.length > 0) {
|
|
150
|
+
if (profile) {
|
|
140
151
|
basePrompt += `\n\n--- [USER_PERSONA] RISK PROFILE & PREFERENCES ---\n`;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
basePrompt += `Custom Rules: ${profile.custom_rules}\n`;
|
|
147
|
-
}
|
|
148
|
-
basePrompt += `CRITICAL: You MUST adhere to these risk parameters when advising the user or executing tools. If a requested action violates these parameters (e.g., buying a high-risk memecoin when 'Avoid Memecoins' is YES), you MUST warn the user and refuse execution unless they explicitly override.\n`;
|
|
149
|
-
}
|
|
150
|
-
if (personas.length > 0) {
|
|
151
|
-
basePrompt += `\nObserved Traits (Dialectic Modeling):\n`;
|
|
152
|
-
personas.forEach(p => {
|
|
153
|
-
basePrompt += `- ${p.trait} (Confidence: ${(p.confidence * 100).toFixed(0)}%)\n`;
|
|
154
|
-
});
|
|
155
|
-
basePrompt += `Adapt your tone, vocabulary, and suggestions to align with these observed user traits.\n`;
|
|
152
|
+
basePrompt += `Risk Level: ${profile.risk_level}\n`;
|
|
153
|
+
basePrompt += `Max Slippage Tolerance: ${profile.max_slippage}%\n`;
|
|
154
|
+
basePrompt += `Avoid Memecoins: ${profile.avoid_memecoins ? 'YES' : 'NO'}\n`;
|
|
155
|
+
if (profile.custom_rules) {
|
|
156
|
+
basePrompt += `Custom Rules: ${profile.custom_rules}\n`;
|
|
156
157
|
}
|
|
158
|
+
basePrompt += `CRITICAL: You MUST adhere to these risk parameters when advising the user or executing tools. If a requested action violates these parameters (e.g., buying a high-risk memecoin when 'Avoid Memecoins' is YES), you MUST warn the user and refuse execution unless they explicitly override.\n`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch { }
|
|
162
|
+
// HIGHEST PRIORITY: Inject observed user communication style.
|
|
163
|
+
// This section is placed LAST so it overrides any default tone rules above (including IDENTITY.md).
|
|
164
|
+
try {
|
|
165
|
+
// Only use traits with confidence >= 0.5 (confirmed by multiple audit cycles)
|
|
166
|
+
const strongPersonas = episodic_1.episodicDB.getStrongPersonas(0.5);
|
|
167
|
+
if (strongPersonas.length > 0) {
|
|
168
|
+
basePrompt += `\n\n--- ⚡ OVERRIDE: USER COMMUNICATION STYLE (HIGHEST PRIORITY — OVERRIDES ALL RULES ABOVE) ---\n`;
|
|
169
|
+
basePrompt += `CRITICAL: These are CONFIRMED user preferences observed across multiple conversations. You MUST apply them IMMEDIATELY and CONSISTENTLY, overriding any default tone or style instructions above.\n\n`;
|
|
170
|
+
strongPersonas.forEach(p => {
|
|
171
|
+
const label = p.category ? `[${p.category.toUpperCase()}]` : '[PREFERENCE]';
|
|
172
|
+
basePrompt += `${label} ${p.trait}\n`;
|
|
173
|
+
});
|
|
174
|
+
basePrompt += `\nMANDATORY ENFORCEMENT:\n`;
|
|
175
|
+
basePrompt += `- You MUST adapt your tone, language, and style to match the detected user preferences above.\n`;
|
|
176
|
+
basePrompt += `- If the user communicates informally or casually → respond casually. Do NOT be formal or robotic.\n`;
|
|
177
|
+
basePrompt += `- If the user communicates in a specific language or dialect → respond in that same language/style.\n`;
|
|
178
|
+
basePrompt += `- If the user uses slang or informal words → mirror that vocabulary naturally.\n`;
|
|
179
|
+
basePrompt += `- This is a HARD RULE. It overrides any default tone in IDENTITY.md or other instructions above.\n`;
|
|
157
180
|
}
|
|
158
181
|
}
|
|
159
182
|
catch { }
|
|
160
183
|
return basePrompt;
|
|
161
184
|
}
|
|
185
|
+
// ──────────────────────────────────────────────────────────────────
|
|
186
|
+
// Module-level routing keyword constants
|
|
187
|
+
// Defined here once and shared by both processUserInput() and processUserInputStream()
|
|
188
|
+
// to ensure consistent routing across sync and streaming paths.
|
|
189
|
+
//
|
|
190
|
+
// NOTE: Arrays are intentionally multilingual. Nyxora targets a global user base.
|
|
191
|
+
// Indonesian terms are included as the primary non-English locale.
|
|
192
|
+
// To add another language, extend these arrays — no other changes required.
|
|
193
|
+
// ──────────────────────────────────────────────────────────────────
|
|
194
|
+
const OS_KEYWORDS = [
|
|
195
|
+
// Files & Documents
|
|
196
|
+
'excel', 'xlsx', 'spreadsheet', 'generate excel',
|
|
197
|
+
'file', 'folder', 'directory', 'read file', 'write file', 'pdf', 'word', 'docx', 'document',
|
|
198
|
+
// Terminal & Git
|
|
199
|
+
'terminal', 'command', 'shell', 'bash', 'script', 'run command',
|
|
200
|
+
'git', 'commit', 'push', 'pull', 'clone', 'branch', 'merge',
|
|
201
|
+
// Web & Search
|
|
202
|
+
'search web', 'google', 'browse', 'scrape', 'weather', 'news',
|
|
203
|
+
// Email & Workspace
|
|
204
|
+
'email', 'gmail', 'google docs', 'google sheets', 'notion', 'calendar',
|
|
205
|
+
// Social & Media
|
|
206
|
+
'twitter', 'tweet', 'x post', 'transcribe', 'audio',
|
|
207
|
+
// AI Settings
|
|
208
|
+
'rename agent', 'change persona', 'update profile', 'update identity', 'setting',
|
|
209
|
+
// Summarization
|
|
210
|
+
'summarize',
|
|
211
|
+
];
|
|
212
|
+
const WEB3_KEYWORDS = [
|
|
213
|
+
// Transactions
|
|
214
|
+
'swap', 'bridge', 'transfer', 'send', 'buy', 'sell',
|
|
215
|
+
'mint', 'stake', 'unstake', 'claim', 'deposit', 'withdraw', 'approve',
|
|
216
|
+
// Assets & Wallets
|
|
217
|
+
'token', 'crypto', 'coin', 'nft', 'wallet', 'address',
|
|
218
|
+
'eth', 'bnb', 'usdt', 'usdc', 'sol', 'matic', 'arb', 'op', 'base',
|
|
219
|
+
// DeFi & Market
|
|
220
|
+
'defi', 'dex', 'liquidity', 'pool', 'aave', 'uniswap', 'apy', 'apr',
|
|
221
|
+
'price', 'chart', 'market', 'portfolio', 'balance',
|
|
222
|
+
'gas', 'fee', 'slippage', 'transaction', 'tx',
|
|
223
|
+
// Chains
|
|
224
|
+
'ethereum', 'polygon', 'arbitrum', 'optimism', 'bsc', 'mainnet', 'testnet',
|
|
225
|
+
'on-chain', 'blockchain',
|
|
226
|
+
// Fiat & Currency
|
|
227
|
+
'usd', 'eur', 'gbp', 'jpy', 'aud', 'idr', 'fiat', 'currency', 'convert', 'exchange', 'rate', 'value',
|
|
228
|
+
];
|
|
229
|
+
// ── P2: Multi-Intent Decomposer ─────────────────────────────────────────────
|
|
230
|
+
/**
|
|
231
|
+
* Detects if the user message contains BOTH web3 AND os intents.
|
|
232
|
+
* Returns 'compound' when both are present so we can route to both agents.
|
|
233
|
+
*/
|
|
234
|
+
function detectCompoundIntent(lowerInput, osKeywords, web3Keywords) {
|
|
235
|
+
return {
|
|
236
|
+
hasOs: osKeywords.some(kw => lowerInput.includes(kw)),
|
|
237
|
+
hasWeb3: web3Keywords.some(kw => lowerInput.includes(kw)),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
// ── P7: Task Planner ─────────────────────────────────────────────────────────
|
|
241
|
+
// Trigger keywords for complex requests that benefit from a planning step first.
|
|
242
|
+
// Intentionally multilingual: includes common terms from Indonesian (id), English (en),
|
|
243
|
+
// and universal finance/trading vocabulary to support Nyxora's global user base.
|
|
244
|
+
const PLAN_TRIGGER_KEYWORDS = [
|
|
245
|
+
// Indonesian
|
|
246
|
+
'buatkan', 'buat rencana', 'rencanakan', 'strategi', 'gimana cara', 'bagaimana cara',
|
|
247
|
+
'langkah', 'optimasi', 'apa yang harus',
|
|
248
|
+
// English
|
|
249
|
+
'strategy', 'plan', 'planning', 'step by step', 'breakdown', 'optimize',
|
|
250
|
+
'rebalance', 'what should i do', 'help me decide', 'how do i',
|
|
251
|
+
// Universal finance terms
|
|
252
|
+
'roadmap', 'approach', 'framework',
|
|
253
|
+
];
|
|
254
|
+
function shouldPlan(input) {
|
|
255
|
+
const lower = input.toLowerCase();
|
|
256
|
+
return PLAN_TRIGGER_KEYWORDS.some(kw => lower.includes(kw));
|
|
257
|
+
}
|
|
258
|
+
async function runTaskPlanner(input, context) {
|
|
259
|
+
const config = (0, parser_1.loadConfig)();
|
|
260
|
+
try {
|
|
261
|
+
const planRes = await (0, llmUtils_1.executeWithRetry)(async (client) => client.chat({
|
|
262
|
+
model: config.llm.model,
|
|
263
|
+
temperature: 0.2,
|
|
264
|
+
messages: [
|
|
265
|
+
{
|
|
266
|
+
role: 'system',
|
|
267
|
+
content: `You are a task planning assistant for a crypto AI agent.
|
|
268
|
+
The user has a complex request that needs structured execution.
|
|
269
|
+
Break it down into a clear, ordered execution plan with 3-6 concrete steps.
|
|
270
|
+
Each step should map to a specific action or tool call.
|
|
271
|
+
Be concise. Use bullet points. No fluff.
|
|
272
|
+
Context domain: ${context}`
|
|
273
|
+
},
|
|
274
|
+
{ role: 'user', content: `Create an execution plan for: ${input}` }
|
|
275
|
+
]
|
|
276
|
+
}));
|
|
277
|
+
const plan = planRes.message?.content?.trim() || '';
|
|
278
|
+
if (!plan)
|
|
279
|
+
return '';
|
|
280
|
+
console.log(picocolors_1.default.blue('[TaskPlanner] Plan generated, injecting into agent context.'));
|
|
281
|
+
return `\n\n--- 📋 TASK EXECUTION PLAN (follow this order) ---\n${plan}\n--- END PLAN ---\n`;
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return ''; // planning is best-effort, never block the agent
|
|
285
|
+
}
|
|
286
|
+
}
|
|
162
287
|
const web3Agent_1 = require("./web3Agent");
|
|
163
288
|
const osAgent_1 = require("./osAgent");
|
|
289
|
+
const web3Agent_2 = require("./web3Agent");
|
|
290
|
+
const osAgent_2 = require("./osAgent");
|
|
164
291
|
async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
165
292
|
const lowerInput = input.toLowerCase();
|
|
166
293
|
const config = (0, parser_1.loadConfig)();
|
|
@@ -169,46 +296,91 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
169
296
|
const textOnlyHistory = history
|
|
170
297
|
.filter(m => (m.role === 'user' || m.role === 'assistant') && m.content)
|
|
171
298
|
.map(m => ({ role: m.role === 'system' ? 'user' : m.role, content: m.content || "" }));
|
|
172
|
-
|
|
299
|
+
// Uses module-level OS_KEYWORDS and WEB3_KEYWORDS constants defined above.
|
|
300
|
+
// Routing logic: deterministic keyword check first (no LLM call needed),
|
|
301
|
+
// with LLM router as fallback for ambiguous inputs.
|
|
302
|
+
let context = 'general';
|
|
303
|
+
let preCheckMatched = false;
|
|
304
|
+
const compound = detectCompoundIntent(lowerInput, OS_KEYWORDS, WEB3_KEYWORDS);
|
|
305
|
+
// P2: Handle compound web3+os intent — run both agents and merge results
|
|
306
|
+
if (compound.hasWeb3 && compound.hasOs) {
|
|
307
|
+
console.log(picocolors_1.default.cyan('[Orchestrator] Compound intent detected (web3 + os). Running both agents sequentially.'));
|
|
308
|
+
exports.logger.addEntry({ role, content: input }, sessionId);
|
|
309
|
+
const [web3Result, osResult] = await Promise.allSettled([
|
|
310
|
+
(0, web3Agent_1.processWeb3Intent)(input, role, onProgress, sessionId),
|
|
311
|
+
(0, osAgent_1.processOsIntent)(input, role, onProgress, sessionId),
|
|
312
|
+
]);
|
|
313
|
+
const parts = [];
|
|
314
|
+
if (web3Result.status === 'fulfilled' && web3Result.value)
|
|
315
|
+
parts.push(web3Result.value);
|
|
316
|
+
if (osResult.status === 'fulfilled' && osResult.value)
|
|
317
|
+
parts.push(osResult.value);
|
|
318
|
+
return parts.join('\n\n---\n\n') || '⚠️ Both agents returned empty responses.';
|
|
319
|
+
}
|
|
320
|
+
// Single-intent routing
|
|
321
|
+
if (compound.hasOs) {
|
|
322
|
+
context = 'os';
|
|
323
|
+
preCheckMatched = true;
|
|
324
|
+
}
|
|
325
|
+
else if (compound.hasWeb3) {
|
|
326
|
+
context = 'web3';
|
|
327
|
+
preCheckMatched = true;
|
|
328
|
+
}
|
|
329
|
+
if (preCheckMatched) {
|
|
330
|
+
console.log(picocolors_1.default.cyan(`[Orchestrator] Intent pre-classified (keyword match) as: ${context.toUpperCase()}`));
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
// ── Fallback: LLM Router (untuk intent ambigu / percakapan umum) ─────────
|
|
334
|
+
const routerPrompt = `You are Nyxora's Semantic Intent Router. Your job is to classify the user's FINAL message into one of three categories: 'web3', 'os', or 'general'.
|
|
173
335
|
Rules:
|
|
174
336
|
1. FOCUS ONLY ON THE FINAL MESSAGE. History is only for context.
|
|
175
|
-
2. The user may speak in ANY language, including casual slang, idioms, or abbreviations
|
|
176
|
-
3. If the core intent involves blockchain, crypto, bridging, swapping, trading, sending/receiving, tokens, wallets,
|
|
177
|
-
4. If the core intent involves OS automation,
|
|
337
|
+
2. The user may speak in ANY language, including casual slang, idioms, or abbreviations.
|
|
338
|
+
3. If the core intent involves blockchain, crypto, bridging, swapping, trading, sending/receiving, tokens, wallets, transactions, OR asking for the price/conversion of ANY asset to fiat, reply 'web3'.
|
|
339
|
+
4. If the core intent involves OS automation, weather, emails, files, terminal, changing AI settings, OR asking ANY question that requires a web search or real-world factual lookup (e.g., 'who won the game', 'what is the registration date', 'cek info', 'cari tahu'), reply 'os'.
|
|
178
340
|
5. If it is purely casual conversation, chit-chat, or greetings, reply 'general'.
|
|
179
341
|
Reply with EXACTLY ONE WORD: web3, os, or general.`;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
342
|
+
const routerMessages = [
|
|
343
|
+
{ role: 'system', content: routerPrompt },
|
|
344
|
+
...textOnlyHistory.slice(-10),
|
|
345
|
+
{ role: 'user', content: input }
|
|
346
|
+
];
|
|
347
|
+
try {
|
|
348
|
+
const routerResponse = await (0, llmUtils_1.executeWithRetry)(async (client) => {
|
|
349
|
+
return await client.chat({
|
|
350
|
+
model: config.llm.model,
|
|
351
|
+
messages: routerMessages,
|
|
352
|
+
temperature: 0.1,
|
|
353
|
+
max_tokens: 1000
|
|
354
|
+
});
|
|
355
|
+
}, 3); // 3 retries for transient 503/429 errors
|
|
356
|
+
let contextResponse = (routerResponse.message.content || 'general').toLowerCase().trim();
|
|
357
|
+
if (contextResponse.includes('web3'))
|
|
358
|
+
context = 'web3';
|
|
359
|
+
else if (contextResponse.includes('os'))
|
|
360
|
+
context = 'os';
|
|
361
|
+
else
|
|
362
|
+
context = 'general';
|
|
363
|
+
}
|
|
364
|
+
catch (e) {
|
|
365
|
+
console.warn(`[Orchestrator] Router LLM failed, falling back to general. Error:`, e);
|
|
201
366
|
context = 'general';
|
|
367
|
+
}
|
|
368
|
+
console.log(picocolors_1.default.magenta(`[Orchestrator] Intent classified as: ${context.toUpperCase()}`));
|
|
202
369
|
}
|
|
203
|
-
catch (e) {
|
|
204
|
-
console.warn(`[Orchestrator] Router LLM failed, falling back to general. Error:`, e);
|
|
205
|
-
context = 'general';
|
|
206
|
-
}
|
|
207
|
-
console.log(picocolors_1.default.magenta(`[Orchestrator] Intent classified as: ${context.toUpperCase()}`));
|
|
208
370
|
if (context === 'web3') {
|
|
371
|
+
// P7: Inject task plan for complex requests
|
|
372
|
+
const planInjection = shouldPlan(input) ? await runTaskPlanner(input, 'web3') : '';
|
|
373
|
+
if (planInjection) {
|
|
374
|
+
// Prepend plan as a system note in the input so agent sees it
|
|
375
|
+
return await (0, web3Agent_1.processWeb3Intent)(planInjection + '\n\nUSER REQUEST: ' + input, role, onProgress, sessionId);
|
|
376
|
+
}
|
|
209
377
|
return await (0, web3Agent_1.processWeb3Intent)(input, role, onProgress, sessionId);
|
|
210
378
|
}
|
|
211
379
|
else if (context === 'os') {
|
|
380
|
+
const planInjection = shouldPlan(input) ? await runTaskPlanner(input, 'os') : '';
|
|
381
|
+
if (planInjection) {
|
|
382
|
+
return await (0, osAgent_1.processOsIntent)(planInjection + '\n\nUSER REQUEST: ' + input, role, onProgress, sessionId);
|
|
383
|
+
}
|
|
212
384
|
return await (0, osAgent_1.processOsIntent)(input, role, onProgress, sessionId);
|
|
213
385
|
}
|
|
214
386
|
else {
|
|
@@ -217,7 +389,7 @@ Reply with EXACTLY ONE WORD: web3, os, or general.`;
|
|
|
217
389
|
// Let's create a dummy general intent processing directly here.
|
|
218
390
|
exports.logger.addEntry({ role, content: input }, sessionId);
|
|
219
391
|
const messages = [
|
|
220
|
-
{ role: 'system', content: getSystemPrompt('general') },
|
|
392
|
+
{ role: 'system', content: await getSystemPrompt('general', input) },
|
|
221
393
|
...textOnlyHistory,
|
|
222
394
|
{ role: 'user', content: input }
|
|
223
395
|
];
|
|
@@ -254,3 +426,124 @@ Reply with EXACTLY ONE WORD: web3, os, or general.`;
|
|
|
254
426
|
}
|
|
255
427
|
}
|
|
256
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Streaming variant of processUserInput().
|
|
431
|
+
* Calls onChunk() for each LLM token as it arrives.
|
|
432
|
+
* Falls back to onChunk() with full response if streaming is not supported.
|
|
433
|
+
*/
|
|
434
|
+
async function processUserInputStream(input, originalOnChunk, onProgress, sessionId) {
|
|
435
|
+
const smartStream = (0, streamSimulator_1.createSmartStreamWrapper)(originalOnChunk);
|
|
436
|
+
const onChunk = smartStream.onChunk;
|
|
437
|
+
try {
|
|
438
|
+
const lowerInput = input.toLowerCase();
|
|
439
|
+
const config = (0, parser_1.loadConfig)();
|
|
440
|
+
const history = exports.logger.getHistory(sessionId);
|
|
441
|
+
const textOnlyHistory = history
|
|
442
|
+
.filter(m => (m.role === 'user' || m.role === 'assistant') && m.content)
|
|
443
|
+
.map(m => ({ role: m.role === 'system' ? 'user' : m.role, content: m.content || '' }));
|
|
444
|
+
// Use module-level keyword constants for consistent routing across sync and stream paths.
|
|
445
|
+
// detectCompoundIntent and shouldPlan are also shared from the module scope.
|
|
446
|
+
let context = 'general';
|
|
447
|
+
let preCheckMatched = false;
|
|
448
|
+
// P2: Compound intent detection (stream path)
|
|
449
|
+
const streamCompound = detectCompoundIntent(lowerInput, OS_KEYWORDS, WEB3_KEYWORDS);
|
|
450
|
+
const pendingTxs = transactionManager_1.txManager.getPending();
|
|
451
|
+
if (pendingTxs.length > 0) {
|
|
452
|
+
context = 'web3';
|
|
453
|
+
preCheckMatched = true;
|
|
454
|
+
}
|
|
455
|
+
else if (streamCompound.hasWeb3 && streamCompound.hasOs) {
|
|
456
|
+
// Compound: stream both agents and concatenate
|
|
457
|
+
console.log(picocolors_1.default.cyan('[Stream Orchestrator] Compound intent detected (web3 + os).'));
|
|
458
|
+
exports.logger.addEntry({ role: 'user', content: input }, sessionId);
|
|
459
|
+
const [web3R, osR] = await Promise.allSettled([
|
|
460
|
+
(0, web3Agent_2.processWeb3IntentStream)(input, onChunk, onProgress, sessionId),
|
|
461
|
+
(0, osAgent_2.processOsIntentStream)(input, onChunk, onProgress, sessionId),
|
|
462
|
+
]);
|
|
463
|
+
const parts = [];
|
|
464
|
+
if (web3R.status === 'fulfilled' && web3R.value)
|
|
465
|
+
parts.push(web3R.value);
|
|
466
|
+
if (osR.status === 'fulfilled' && osR.value)
|
|
467
|
+
parts.push(osR.value);
|
|
468
|
+
return parts.join('\n\n---\n\n') || '⚠️ Both agents returned empty responses.';
|
|
469
|
+
}
|
|
470
|
+
else if (streamCompound.hasOs) {
|
|
471
|
+
context = 'os';
|
|
472
|
+
preCheckMatched = true;
|
|
473
|
+
}
|
|
474
|
+
else if (streamCompound.hasWeb3) {
|
|
475
|
+
context = 'web3';
|
|
476
|
+
preCheckMatched = true;
|
|
477
|
+
}
|
|
478
|
+
if (!preCheckMatched) {
|
|
479
|
+
const routerPrompt = `You are Nyxora's Semantic Intent Router. Your job is to classify the user's FINAL message into one of three categories: 'web3', 'os', or 'general'.
|
|
480
|
+
Rules:
|
|
481
|
+
1. FOCUS ONLY ON THE FINAL MESSAGE. History is only for context.
|
|
482
|
+
2. The user may speak in ANY language, including casual slang, idioms, or abbreviations.
|
|
483
|
+
3. If the core intent involves blockchain, crypto, bridging, swapping, trading, sending/receiving, tokens, wallets, transactions, OR asking for the price/conversion of ANY asset to fiat, reply 'web3'.
|
|
484
|
+
4. If the core intent involves OS automation, weather, emails, files, terminal, changing AI settings, OR asking ANY question that requires a web search or real-world factual lookup (e.g., 'who won the game', 'what is the registration date', 'cek info', 'cari tahu'), reply 'os'.
|
|
485
|
+
5. If it is purely casual conversation, chit-chat, or greetings, reply 'general'.
|
|
486
|
+
Reply with EXACTLY ONE WORD: web3, os, or general.`;
|
|
487
|
+
const routerMessages = [
|
|
488
|
+
{ role: 'system', content: routerPrompt },
|
|
489
|
+
...textOnlyHistory.slice(-10),
|
|
490
|
+
{ role: 'user', content: input }
|
|
491
|
+
];
|
|
492
|
+
try {
|
|
493
|
+
const routerResponse = await (0, llmUtils_1.executeWithRetry)(async (client) => client.chat({ model: config.llm.model, messages: routerMessages, temperature: 0.1, max_tokens: 1000 }), 3);
|
|
494
|
+
const cr = (routerResponse.message.content || 'general').toLowerCase().trim();
|
|
495
|
+
if (cr.includes('web3'))
|
|
496
|
+
context = 'web3';
|
|
497
|
+
else if (cr.includes('os'))
|
|
498
|
+
context = 'os';
|
|
499
|
+
else
|
|
500
|
+
context = 'general';
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
context = 'general';
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
console.log(picocolors_1.default.cyan(`[Stream Orchestrator] Intent classified as: ${context.toUpperCase()}`));
|
|
507
|
+
let finalResult = '';
|
|
508
|
+
if (context === 'web3') {
|
|
509
|
+
// P7: Inject task plan for complex stream requests
|
|
510
|
+
const planInjection = shouldPlan(input) ? await runTaskPlanner(input, 'web3') : '';
|
|
511
|
+
const streamInput = planInjection ? planInjection + '\n\nUSER REQUEST: ' + input : input;
|
|
512
|
+
finalResult = await (0, web3Agent_2.processWeb3IntentStream)(streamInput, onChunk, onProgress, sessionId);
|
|
513
|
+
}
|
|
514
|
+
else if (context === 'os') {
|
|
515
|
+
const planInjection = shouldPlan(input) ? await runTaskPlanner(input, 'os') : '';
|
|
516
|
+
const streamInput = planInjection ? planInjection + '\n\nUSER REQUEST: ' + input : input;
|
|
517
|
+
finalResult = await (0, osAgent_2.processOsIntentStream)(streamInput, onChunk, onProgress, sessionId);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
exports.logger.addEntry({ role: 'user', content: input }, sessionId);
|
|
521
|
+
const messages = [
|
|
522
|
+
{ role: 'system', content: await getSystemPrompt('general', input) },
|
|
523
|
+
...textOnlyHistory,
|
|
524
|
+
{ role: 'user', content: input }
|
|
525
|
+
];
|
|
526
|
+
try {
|
|
527
|
+
let streamedContent = '';
|
|
528
|
+
const response = await (0, llmUtils_1.executeWithRetry)(async (client) => client.stream({ model: config.llm.model, messages: messages }, (chunk) => { streamedContent += chunk; onChunk(chunk); }));
|
|
529
|
+
let finalContent = response.message?.content || streamedContent || '';
|
|
530
|
+
finalContent = finalContent
|
|
531
|
+
.replace(/<(think|thought)[\s\S]*?<\/\1>\n?/gi, '')
|
|
532
|
+
.trim();
|
|
533
|
+
if (!finalContent)
|
|
534
|
+
finalContent = '⚠️ The LLM returned an empty response. Please try again.';
|
|
535
|
+
exports.logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
|
|
536
|
+
finalResult = finalContent;
|
|
537
|
+
}
|
|
538
|
+
catch (error) {
|
|
539
|
+
const errorMsg = '⚠️ The system is experiencing LLM API rate limits. Please try again.';
|
|
540
|
+
exports.logger.addEntry({ role: 'assistant', content: errorMsg }, sessionId);
|
|
541
|
+
finalResult = errorMsg;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return finalResult;
|
|
545
|
+
}
|
|
546
|
+
finally {
|
|
547
|
+
await smartStream.wait();
|
|
548
|
+
}
|
|
549
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReasoningScratchpad = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* ReasoningScratchpad — P1: Persistent Reasoning Memory
|
|
6
|
+
*
|
|
7
|
+
* Captures <think> blocks from LLM responses and makes the distilled
|
|
8
|
+
* reasoning available as context for subsequent turns within the same request.
|
|
9
|
+
* This mirrors how Claude/Gemini maintain internal chain-of-thought continuity.
|
|
10
|
+
*/
|
|
11
|
+
class ReasoningScratchpad {
|
|
12
|
+
entries = [];
|
|
13
|
+
/**
|
|
14
|
+
* Extract and store the think block from a raw LLM response.
|
|
15
|
+
* Returns the cleaned response (think tags removed) for display.
|
|
16
|
+
*/
|
|
17
|
+
capture(rawContent, turn) {
|
|
18
|
+
const thinkRegex = /<(think|thought|thinking|reasoning|analysis|reflection)>([\s\S]*?)<\/\1>/gi;
|
|
19
|
+
let match;
|
|
20
|
+
while ((match = thinkRegex.exec(rawContent)) !== null) {
|
|
21
|
+
const thinkText = match[2].trim();
|
|
22
|
+
if (thinkText.length > 20) {
|
|
23
|
+
// Distil to first 600 chars to avoid bloating the next prompt
|
|
24
|
+
this.entries.push({ turn, summary: thinkText.slice(0, 600) });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Return content with think blocks stripped
|
|
28
|
+
return rawContent
|
|
29
|
+
.replace(/<(think|thought|thinking|reasoning|analysis|reflection)>[\s\S]*?<\/\1>\n?/gi, '')
|
|
30
|
+
.trim();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build an injection string for the next turn's system prompt.
|
|
34
|
+
* Only surfaces the last 2 think blocks to keep token usage lean.
|
|
35
|
+
*/
|
|
36
|
+
getInjection() {
|
|
37
|
+
if (this.entries.length === 0)
|
|
38
|
+
return '';
|
|
39
|
+
const recent = this.entries.slice(-2);
|
|
40
|
+
const lines = recent.map(e => `[Turn ${e.turn}] ${e.summary}`).join('\n');
|
|
41
|
+
return `\n\n--- 🧠 REASONING CONTINUITY (your prior internal thoughts) ---\n${lines}\nUse these prior conclusions to avoid re-deriving the same logic.\n`;
|
|
42
|
+
}
|
|
43
|
+
isEmpty() {
|
|
44
|
+
return this.entries.length === 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.ReasoningScratchpad = ReasoningScratchpad;
|