nyxora 26.7.4 → 26.7.8
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 +64 -0
- package/README.md +43 -8
- package/dist/launcher.js +67 -9
- package/dist/packages/core/src/agent/bridgeWatcher.js +1 -1
- package/dist/packages/core/src/agent/cronManager.js +1 -1
- package/dist/packages/core/src/agent/llmProvider.js +78 -7
- package/dist/packages/core/src/agent/nyxDaemon.js +2 -2
- package/dist/packages/core/src/agent/osAgent.js +172 -206
- package/dist/packages/core/src/agent/promptBuilder.js +452 -0
- package/dist/packages/core/src/agent/reasoning.js +155 -208
- package/dist/packages/core/src/agent/reasoningScratchpad.js +19 -2
- package/dist/packages/core/src/agent/threatPatterns.js +106 -0
- package/dist/packages/core/src/agent/web3Agent.js +81 -115
- package/dist/packages/core/src/agent/workspaceUtils.js +56 -0
- package/dist/packages/core/src/channels/ChannelManager.js +36 -0
- package/dist/packages/core/src/channels/discordAdapter.js +81 -0
- package/dist/packages/core/src/channels/googlechatAdapter.js +45 -0
- package/dist/packages/core/src/channels/imessageAdapter.js +34 -0
- package/dist/packages/core/src/channels/index.js +89 -0
- package/dist/packages/core/src/channels/ircAdapter.js +34 -0
- package/dist/packages/core/src/channels/lineAdapter.js +133 -0
- package/dist/packages/core/src/channels/matrixAdapter.js +34 -0
- package/dist/packages/core/src/channels/mattermostAdapter.js +45 -0
- package/dist/packages/core/src/channels/msteamsAdapter.js +45 -0
- package/dist/packages/core/src/channels/nextcloudtalkAdapter.js +45 -0
- package/dist/packages/core/src/channels/nostrAdapter.js +34 -0
- package/dist/packages/core/src/channels/qqbotAdapter.js +34 -0
- package/dist/packages/core/src/channels/slackAdapter.js +116 -0
- package/dist/packages/core/src/channels/smsAdapter.js +45 -0
- package/dist/packages/core/src/channels/synologychatAdapter.js +45 -0
- package/dist/packages/core/src/channels/telegram.js +593 -0
- package/dist/packages/core/src/channels/twitchAdapter.js +34 -0
- package/dist/packages/core/src/channels/voicecallAdapter.js +45 -0
- package/dist/packages/core/src/channels/whatsappAdapter.js +136 -0
- package/dist/packages/core/src/channels/zaloAdapter.js +45 -0
- package/dist/packages/core/src/config/parser.js +2 -1
- package/dist/packages/core/src/gateway/chat.js +10 -2
- package/dist/packages/core/src/gateway/cli.js +4 -1
- package/dist/packages/core/src/gateway/doctor.js +56 -22
- package/dist/packages/core/src/gateway/googleAuthModule.js +111 -13
- package/dist/packages/core/src/gateway/server.js +139 -3
- package/dist/packages/core/src/gateway/setup-ml.js +65 -2
- package/dist/packages/core/src/gateway/setup.js +61 -22
- package/dist/packages/core/src/gateway/telegram.js +21 -48
- package/dist/packages/core/src/memory/episodic.js +4 -0
- package/dist/packages/core/src/memory/logger.js +27 -9
- package/dist/packages/core/src/plugin/PluginManager.js +46 -16
- package/dist/packages/core/src/system/plugins/GoogleWorkspacePlugin.js +69 -30
- package/dist/packages/core/src/system/plugins/SystemNotesPlugin.js +148 -0
- package/dist/packages/core/src/system/plugins/SystemSocialPlugin.js +2 -14
- package/dist/packages/core/src/system/plugins/SystemWebPlugin.js +0 -5
- package/dist/packages/core/src/system/plugins/SystemWorkspacePlugin.js +25 -6
- package/dist/packages/core/src/system/skills/analyzeImage.js +82 -0
- package/dist/packages/core/src/system/skills/fileDownloader.js +45 -0
- package/dist/packages/core/src/system/skills/generateExcel.js +1 -1
- package/dist/packages/core/src/system/skills/googleWorkspace.js +278 -271
- package/dist/packages/core/src/system/skills/playbookManager.js +269 -0
- package/dist/packages/core/src/system/skills/telegramUpload.js +25 -0
- package/dist/packages/core/src/utils/fileLinker.js +57 -0
- package/dist/packages/core/src/utils/historySanitizer.js +19 -5
- package/dist/packages/core/src/utils/llmUtils.js +5 -4
- package/dist/packages/core/src/utils/skillManager.js +12 -4
- package/dist/packages/core/src/utils/streamSimulator.js +40 -40
- package/dist/packages/core/src/web3/skills/checkPortfolio.js +7 -1
- package/dist/packages/core/src/web3/skills/getTxHistory.js +44 -7
- package/dist/packages/core/src/web3/utils/vaultClient.js +20 -22
- package/dist/packages/policy/src/server.js +38 -51
- package/dist/packages/signer/src/server.js +32 -15
- package/launcher.ts +61 -9
- package/package.json +7 -7
- package/packages/core/package.json +27 -9
- package/packages/core/src/agent/bridgeWatcher.ts +1 -1
- package/packages/core/src/agent/cronManager.ts +1 -1
- package/packages/core/src/agent/llmProvider.ts +87 -7
- package/packages/core/src/agent/nyxDaemon.ts +3 -3
- package/packages/core/src/agent/osAgent.ts +180 -209
- package/packages/core/src/agent/promptBuilder.ts +476 -0
- package/packages/core/src/agent/reasoning.ts +156 -213
- package/packages/core/src/agent/reasoningScratchpad.ts +21 -2
- package/packages/core/src/agent/threatPatterns.ts +115 -0
- package/packages/core/src/agent/web3Agent.ts +77 -115
- package/packages/core/src/agent/workspaceUtils.ts +53 -0
- package/packages/core/src/channels/ChannelManager.ts +45 -0
- package/packages/core/src/channels/googlechatAdapter.ts +48 -0
- package/packages/core/src/channels/imessageAdapter.ts +37 -0
- package/packages/core/src/channels/index.ts +57 -0
- package/packages/core/src/channels/ircAdapter.ts +37 -0
- package/packages/core/src/channels/lineAdapter.ts +111 -0
- package/packages/core/src/channels/matrixAdapter.ts +37 -0
- package/packages/core/src/channels/mattermostAdapter.ts +48 -0
- package/packages/core/src/channels/msteamsAdapter.ts +48 -0
- package/packages/core/src/channels/nextcloudtalkAdapter.ts +48 -0
- package/packages/core/src/channels/nostrAdapter.ts +37 -0
- package/packages/core/src/channels/qqbotAdapter.ts +37 -0
- package/packages/core/src/channels/slackAdapter.ts +92 -0
- package/packages/core/src/channels/smsAdapter.ts +48 -0
- package/packages/core/src/channels/synologychatAdapter.ts +48 -0
- package/packages/core/src/channels/telegram.ts +643 -0
- package/packages/core/src/channels/twitchAdapter.ts +37 -0
- package/packages/core/src/channels/voicecallAdapter.ts +48 -0
- package/packages/core/src/channels/whatsappAdapter.ts +113 -0
- package/packages/core/src/channels/zaloAdapter.ts +48 -0
- package/packages/core/src/config/parser.ts +7 -1
- package/packages/core/src/gateway/chat.ts +10 -2
- package/packages/core/src/gateway/cli.ts +4 -1
- package/packages/core/src/gateway/doctor.ts +56 -22
- package/packages/core/src/gateway/googleAuthModule.ts +113 -11
- package/packages/core/src/gateway/server.ts +146 -3
- package/packages/core/src/gateway/setup-ml.ts +65 -4
- package/packages/core/src/gateway/setup.ts +60 -22
- package/packages/core/src/memory/episodic.ts +4 -0
- package/packages/core/src/memory/logger.ts +26 -9
- package/packages/core/src/plugin/PluginManager.ts +78 -16
- package/packages/core/src/system/plugins/GoogleWorkspacePlugin.ts +36 -45
- package/packages/core/src/system/plugins/SystemNotesPlugin.ts +141 -0
- package/packages/core/src/system/plugins/SystemSocialPlugin.ts +2 -14
- package/packages/core/src/system/plugins/SystemWebPlugin.ts +0 -5
- package/packages/core/src/system/plugins/SystemWorkspacePlugin.ts +25 -6
- package/packages/core/src/system/skills/analyzeImage.ts +79 -0
- package/packages/core/src/system/skills/fileDownloader.ts +43 -0
- package/packages/core/src/system/skills/generateExcel.ts +1 -1
- package/packages/core/src/system/skills/googleWorkspace.ts +262 -283
- package/packages/core/src/system/skills/playbookManager.ts +285 -0
- package/packages/core/src/system/skills/telegramUpload.ts +23 -0
- package/packages/core/src/utils/fileLinker.ts +48 -0
- package/packages/core/src/utils/historySanitizer.ts +21 -5
- package/packages/core/src/utils/llmUtils.ts +5 -4
- package/packages/core/src/utils/skillManager.ts +14 -4
- package/packages/core/src/utils/streamSimulator.ts +46 -44
- package/packages/core/src/web3/skills/checkPortfolio.ts +8 -1
- package/packages/core/src/web3/skills/getTxHistory.ts +42 -8
- package/packages/core/src/web3/utils/vaultClient.ts +23 -22
- package/packages/dashboard/dist/assets/index-Dg9eiK4h.css +1 -0
- package/packages/dashboard/dist/assets/index-Dq64NWt0.js +26 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/ml-engine/__pycache__/main.cpython-313.pyc +0 -0
- package/packages/ml-engine/main.py +4 -1
- package/packages/ml-engine/routers/__pycache__/background_review.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/__pycache__/llm.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/__pycache__/memory_writer.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/__pycache__/skill_manager.cpython-313.pyc +0 -0
- package/packages/ml-engine/routers/background_review.py +121 -0
- package/packages/ml-engine/routers/llm.py +15 -1
- package/packages/ml-engine/routers/memory_writer.py +72 -0
- package/packages/ml-engine/routers/skill_manager.py +123 -0
- package/packages/policy/package.json +1 -1
- package/packages/policy/src/server.ts +43 -49
- package/packages/signer/package.json +1 -1
- package/packages/signer/src/server.ts +32 -13
- package/packages/core/src/gateway/telegram.ts +0 -357
- package/packages/core/src/system/skills/analyzeDocument.ts +0 -117
- package/packages/core/src/system/skills/gitManager.ts +0 -84
- package/packages/core/src/system/skills/notionWorkspace.ts +0 -78
- package/packages/core/src/system/skills/xManager.ts +0 -76
- package/packages/dashboard/dist/assets/index-Czxksiao.js +0 -18
- package/packages/dashboard/dist/assets/index-DK2sTU47.css +0 -1
- /package/packages/core/src/{gateway → channels}/discordAdapter.ts +0 -0
|
@@ -6,151 +6,84 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.logger = void 0;
|
|
7
7
|
exports.processOsIntent = processOsIntent;
|
|
8
8
|
exports.processOsIntentStream = processOsIntentStream;
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
9
|
const parser_1 = require("../config/parser");
|
|
11
10
|
const logger_1 = require("../memory/logger");
|
|
12
11
|
const tracker_1 = require("../gateway/tracker");
|
|
13
|
-
const episodic_1 = require("../memory/episodic");
|
|
14
12
|
const skillManager_1 = require("../utils/skillManager");
|
|
15
|
-
const cognitiveManager_1 = require("../cognitive/cognitiveManager");
|
|
16
13
|
const reasoningScratchpad_1 = require("./reasoningScratchpad");
|
|
17
14
|
const contextSummarizer_1 = require("../utils/contextSummarizer");
|
|
18
|
-
const
|
|
19
|
-
<tool_persistence>
|
|
20
|
-
Use tools whenever they can increase the accuracy, completeness, or factual correctness of your response.
|
|
21
|
-
Do NOT stop early if another tool call would materially improve the result.
|
|
22
|
-
Continue using tools until the task is completely finished and verified.
|
|
23
|
-
</tool_persistence>
|
|
24
|
-
|
|
25
|
-
<mandatory_tool_use>
|
|
26
|
-
NEVER answer the following using only your internal memory — ALWAYS use the relevant tool:
|
|
27
|
-
- Arithmetic, math, calculations
|
|
28
|
-
- System State: OS version, RAM, processes
|
|
29
|
-
- File contents, file sizes
|
|
30
|
-
- Real-world current events
|
|
31
|
-
</mandatory_tool_use>
|
|
32
|
-
|
|
33
|
-
<web_search_accuracy>
|
|
34
|
-
When using the search_web tool to look up news, current events, or factual data:
|
|
35
|
-
1. NEVER pass casual, conversational, or highly localized queries directly to the tool (e.g. do not pass "hasil piala dunia tadi pagi").
|
|
36
|
-
2. ALWAYS translate and optimize the query into an absolute, highly specific, and globally-understood English search query (e.g. "World Cup 2026 match results June 25 2026").
|
|
37
|
-
3. Use depth: 2 (deep research) for anything that requires high factual accuracy, such as sports scores, news, or complex topics.
|
|
38
|
-
</web_search_accuracy>
|
|
39
|
-
|
|
40
|
-
<act_dont_ask>
|
|
41
|
-
When a user's request has a clear, standard interpretation, take immediate ACTION instead of asking for clarification.
|
|
42
|
-
NEVER show a command as a markdown code block and wait. CALL the tool directly.
|
|
43
|
-
NEVER ask "do you want me to run this?" — just run it.
|
|
44
|
-
NEVER say "you need to run this yourself" — you have direct shell access.
|
|
45
|
-
If a command requires sudo and may need a password, just run it and report what happens.
|
|
46
|
-
Only report failure AFTER actually attempting the tool call and receiving an error.
|
|
47
|
-
</act_dont_ask>
|
|
48
|
-
|
|
49
|
-
<anti_hallucination_execution>
|
|
50
|
-
CRITICAL: It is STRICTLY FORBIDDEN to write a bash/shell command in a markdown code block (e.g. \`\`\`bash ... \`\`\`) as a substitute for calling the run_terminal_command tool.
|
|
51
|
-
Writing a code block does NOT execute anything. It is a lie to the user.
|
|
52
|
-
If you write \`\`\`bash\nsudo apt install steam\n\`\`\` instead of calling run_terminal_command, you are hallucinating execution.
|
|
53
|
-
The ONLY way to run a command is to emit a proper tool_call for run_terminal_command.
|
|
54
|
-
If the tool is available, USE IT. Do not simulate or describe running it.
|
|
55
|
-
</anti_hallucination_execution>
|
|
56
|
-
|
|
57
|
-
<task_completion>
|
|
58
|
-
The deliverable must be a working artifact backed by real tool output — not just a description or a plan of how you would do it.
|
|
59
|
-
NEVER fabricate, hallucinate, or forge tool outputs.
|
|
60
|
-
</task_completion>
|
|
61
|
-
|
|
62
|
-
<self_correction>
|
|
63
|
-
Before providing a final answer to the user (especially regarding dates, events, news, or factual data), you MUST evaluate your tool results inside a <think> block.
|
|
64
|
-
Ask yourself: "Is my answer based on absolute facts, or circumstantial evidence (e.g., guessing a registration date based on a video upload date)?"
|
|
65
|
-
If the evidence is circumstantial or incomplete, you MUST NOT answer the user. Instead, call the search_web tool again with a highly optimized query and depth=2.
|
|
66
|
-
</self_correction>
|
|
67
|
-
`;
|
|
15
|
+
const promptBuilder_1 = require("./promptBuilder");
|
|
68
16
|
const registry_1 = require("../plugin/registry");
|
|
69
|
-
const paths_1 = require("../config/paths");
|
|
70
17
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
[
|
|
84
|
-
|
|
85
|
-
CRITICAL RULE 2: 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.
|
|
86
|
-
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.
|
|
87
|
-
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.
|
|
88
|
-
CRITICAL RULE 5: TOOL CONFIDENCE. NEVER fabricate file contents or command outputs.
|
|
89
|
-
|
|
90
|
-
[SUDO & PACKAGE INSTALL STRATEGY]
|
|
91
|
-
This tool runs in a NON-INTERACTIVE shell (no TTY). Therefore:
|
|
92
|
-
- ALWAYS prefix apt/apt-get commands with: DEBIAN_FRONTEND=noninteractive
|
|
93
|
-
- ALWAYS use the -y flag for package installations to auto-confirm.
|
|
94
|
-
- If a command fails with "sudo: a password is required" or similar, DO NOT promise to retry without actually retrying. Instead:
|
|
95
|
-
1. First retry with: echo 'USER_PASSWORD' | sudo -S <command> — but since you don't know the password, skip this.
|
|
96
|
-
2. Instead, try running the command WITHOUT sudo if possible (e.g. for user-space tools).
|
|
97
|
-
3. If sudo is truly required and unavailable, clearly tell the user EXACTLY what command to run manually in their terminal, with a copy-paste ready command. Do not just say it failed without providing a solution.
|
|
98
|
-
- NEVER promise to retry ("let me try again", "I'll run it again", etc.) without immediately making another tool_call in the same response.
|
|
99
|
-
|
|
100
|
-
${EXECUTION_DISCIPLINE}
|
|
101
|
-
`;
|
|
102
|
-
// Inject Active Cognitive Skills
|
|
103
|
-
const activeSOP = cognitiveManager_1.cognitiveManager.loadActiveCognitiveSkills(userInput);
|
|
104
|
-
if (activeSOP) {
|
|
105
|
-
basePrompt += `\n\n[ACTIVE COGNITIVE SKILLS]\n${activeSOP}\n`;
|
|
106
|
-
}
|
|
107
|
-
// Inject Episodic Memories via Python RAG
|
|
108
|
-
try {
|
|
109
|
-
const ragRes = await fetch('http://127.0.0.1:8000/memory/rag', {
|
|
110
|
-
method: 'POST',
|
|
111
|
-
headers: { 'Content-Type': 'application/json' },
|
|
112
|
-
body: JSON.stringify({ query: userInput, top_k: 5 })
|
|
113
|
-
});
|
|
114
|
-
if (ragRes.ok) {
|
|
115
|
-
const ragData = await ragRes.json();
|
|
116
|
-
if (ragData.memories && ragData.memories.length > 0) {
|
|
117
|
-
basePrompt += `\n\n--- EPISODIC MEMORIES (SMART SUGGESTIONS) ---\n`;
|
|
118
|
-
ragData.memories.forEach((mem) => {
|
|
119
|
-
basePrompt += `- ${mem}\n`;
|
|
120
|
-
});
|
|
18
|
+
// --- REGISTER LIFECYCLE HOOKS ---
|
|
19
|
+
registry_1.pluginManager.registerHook({
|
|
20
|
+
name: 'ReasoningGate',
|
|
21
|
+
beforeToolCall: async (toolName, args, context) => {
|
|
22
|
+
// FIX: Use actual Nyxora tool names (not legacy names)
|
|
23
|
+
const toolsRequiringReasoning = ['run_terminal_command', 'write_local_file', 'edit_local_file', 'execute_script'];
|
|
24
|
+
if (toolsRequiringReasoning.includes(toolName)) {
|
|
25
|
+
const responseMessage = context.responseMessage || {};
|
|
26
|
+
const hasThinkingTag = /<(think|thought|thinking|reasoning|analysis|reflection)>[\s\S]*?<\/\1>/i.test(responseMessage.content || '');
|
|
27
|
+
const hasNativeReasoning = !!responseMessage.reasoning_content;
|
|
28
|
+
if (!hasThinkingTag && !hasNativeReasoning) {
|
|
29
|
+
const msg = `[System Error] BLOCKED BY REASONING GATE: You MUST output a <think>...</think> block to plan your actions BEFORE calling the '${toolName}' tool. Please rethink and try again.`;
|
|
30
|
+
console.log(picocolors_1.default.red(`[❌ Blocked] Tool ${toolName} blocked by Reasoning Gate.`));
|
|
31
|
+
return { block: true, reason: msg };
|
|
121
32
|
}
|
|
122
33
|
}
|
|
123
34
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
35
|
+
});
|
|
36
|
+
registry_1.pluginManager.registerHook({
|
|
37
|
+
name: 'Web3FastReturn',
|
|
38
|
+
afterToolCall: async (toolName, args, result, context) => {
|
|
39
|
+
// FIX: Only financial Web3 transactions need fast-return (to show approval popup ASAP).
|
|
40
|
+
// send_telegram_file is NOT a financial transaction — removed from this list.
|
|
41
|
+
const fastReturnTools = ['transfer_token', 'transfer_native', 'swap_token', 'bridge_token', 'mint_nft', 'custom_tx', 'revoke_approval', 'supply_aave', 'deposit_yield_vault', 'provide_liquidity_v3'];
|
|
42
|
+
if (fastReturnTools.includes(toolName)) {
|
|
43
|
+
return { terminate: true };
|
|
133
44
|
}
|
|
134
45
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
46
|
+
});
|
|
47
|
+
// --- END LIFECYCLE HOOKS ---
|
|
48
|
+
exports.logger = new logger_1.Logger();
|
|
49
|
+
// Helper to trigger background review async
|
|
50
|
+
const triggerBackgroundReview = async (sessionId) => {
|
|
139
51
|
try {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
52
|
+
const history = exports.logger.getHistory(sessionId, 30);
|
|
53
|
+
fetch('http://127.0.0.1:8000/cognitive/review', {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
messages: history,
|
|
58
|
+
session_id: sessionId || 'default',
|
|
59
|
+
trigger: 'turn_end'
|
|
60
|
+
})
|
|
61
|
+
}).catch(() => { });
|
|
149
62
|
}
|
|
150
63
|
catch (e) {
|
|
151
|
-
//
|
|
64
|
+
// silently fail
|
|
152
65
|
}
|
|
153
|
-
|
|
66
|
+
};
|
|
67
|
+
const llmUtils_1 = require("../utils/llmUtils");
|
|
68
|
+
async function getSystemPrompt(context = 'os', userInput = '') {
|
|
69
|
+
const config = (0, parser_1.loadConfig)();
|
|
70
|
+
// Safely deduce model family from config
|
|
71
|
+
const provider = (config?.llm?.provider || '').toLowerCase();
|
|
72
|
+
let modelFamily = 'unknown';
|
|
73
|
+
if (provider.includes('openai'))
|
|
74
|
+
modelFamily = 'openai';
|
|
75
|
+
else if (provider.includes('gemini') || provider.includes('google'))
|
|
76
|
+
modelFamily = 'google';
|
|
77
|
+
else if (provider.includes('anthropic') || provider.includes('claude'))
|
|
78
|
+
modelFamily = 'anthropic';
|
|
79
|
+
else if (provider.includes('grok') || provider.includes('xai'))
|
|
80
|
+
modelFamily = 'grok';
|
|
81
|
+
return await promptBuilder_1.promptBuilder.buildSystemPrompt({
|
|
82
|
+
agentType: context,
|
|
83
|
+
userInput,
|
|
84
|
+
config,
|
|
85
|
+
modelFamily
|
|
86
|
+
});
|
|
154
87
|
}
|
|
155
88
|
async function processOsIntent(input, role = 'user', onProgress, sessionId) {
|
|
156
89
|
const config = (0, parser_1.loadConfig)();
|
|
@@ -180,8 +113,20 @@ CRITICAL INSTRUCTIONS:
|
|
|
180
113
|
4. Base your new answer strictly on the NEW tool results.`
|
|
181
114
|
}, sessionId);
|
|
182
115
|
}
|
|
116
|
+
// FIX: Lazy-init guard — ensure plugins are always initialized before use.
|
|
117
|
+
// This handles cases where processOsIntent is called before startServer() completes,
|
|
118
|
+
// e.g., direct Telegram messages arriving before plugin initialization.
|
|
119
|
+
if (registry_1.pluginManager.getPlugins().length === 0) {
|
|
120
|
+
console.warn('[OsAgent] ⚠️ Plugins not initialized! Running lazy initializePlugins()...');
|
|
121
|
+
await (0, registry_1.initializePlugins)();
|
|
122
|
+
}
|
|
183
123
|
let activeTools = [...registry_1.pluginManager.getAllToolDefinitions()];
|
|
184
124
|
activeTools = activeTools.filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
|
|
125
|
+
if (activeTools.length === 0) {
|
|
126
|
+
console.error('[OsAgent] ❌ CRITICAL: No active tools found after initialization! Retrying...');
|
|
127
|
+
await (0, registry_1.initializePlugins)();
|
|
128
|
+
activeTools = [...registry_1.pluginManager.getAllToolDefinitions()].filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
|
|
129
|
+
}
|
|
185
130
|
// P1: Init reasoning scratchpad for this request
|
|
186
131
|
const scratchpad = new reasoningScratchpad_1.ReasoningScratchpad();
|
|
187
132
|
// P3: Build system prompt ONCE per request — not per turn
|
|
@@ -213,7 +158,8 @@ CRITICAL INSTRUCTIONS:
|
|
|
213
158
|
model: config.llm.model,
|
|
214
159
|
temperature: config.llm.temperature,
|
|
215
160
|
messages: messages,
|
|
216
|
-
tools: activeTools
|
|
161
|
+
tools: activeTools,
|
|
162
|
+
reasoning_effort: (!config.llm.reasoning_effort || config.llm.reasoning_effort === 'none') ? undefined : config.llm.reasoning_effort
|
|
217
163
|
});
|
|
218
164
|
});
|
|
219
165
|
const responseMessage = response.message;
|
|
@@ -232,41 +178,16 @@ CRITICAL INSTRUCTIONS:
|
|
|
232
178
|
tool_calls: responseMessage.tool_calls,
|
|
233
179
|
}, sessionId);
|
|
234
180
|
if (!responseMessage.tool_calls || responseMessage.tool_calls.length === 0) {
|
|
235
|
-
// --- CRITIC PASS
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
try {
|
|
240
|
-
const criticRes = await fetch('http://127.0.0.1:8000/cognitive/critic', {
|
|
241
|
-
method: 'POST',
|
|
242
|
-
headers: { 'Content-Type': 'application/json' },
|
|
243
|
-
body: JSON.stringify({ user_input: input, draft_answer: cleanedContent, current_utc_datetime: new Date().toISOString() })
|
|
244
|
-
});
|
|
245
|
-
if (criticRes.ok) {
|
|
246
|
-
const evaluation = await criticRes.json();
|
|
247
|
-
if (evaluation.needs_revision) {
|
|
248
|
-
console.log(picocolors_1.default.cyan(`[🧠 Critic] Revision needed. Confidence: ${evaluation.factual_confidence?.toFixed(2)}. Completeness: ${evaluation.completeness?.toFixed(2)}. Re-generating...`));
|
|
249
|
-
exports.logger.addEntry({
|
|
250
|
-
role: 'system',
|
|
251
|
-
content: `[SELF-CRITIQUE — MANDATORY REVISION] Your previous answer was REJECTED. Issues:\n${evaluation.revision_instructions}\n\nCRITICAL REVISION RULES:\n1. Look at the tool results (search_web, etc.) ALREADY in this conversation history above.\n2. Base your revised answer EXCLUSIVELY on those tool results — NEVER use training data memory for facts, dates, or events.\n3. If tool results contain the answer, state it directly and confidently. Do NOT say an event hasn't happened if the tool results show it has.\n4. Do NOT call any tools again — the results are already in your history. USE THEM NOW.`
|
|
252
|
-
}, sessionId);
|
|
253
|
-
continue; // Loop kembali ke Generator untuk revisi
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
console.log(picocolors_1.default.green(`[🧠 Critic] Passed. Confidence: ${evaluation.factual_confidence?.toFixed(2)}, Completeness: ${evaluation.completeness?.toFixed(2)}.`));
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
// Python ML Engine tidak aktif — skip Critic, lanjut seperti biasa
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
// --- END CRITIC PASS ---
|
|
181
|
+
// --- CRITIC PASS REMOVED ---
|
|
182
|
+
// The new PromptBuilder handles robust reasoning. Post-generation critic
|
|
183
|
+
// causes aggressive loops and UI artifacts.
|
|
184
|
+
triggerBackgroundReview(sessionId);
|
|
265
185
|
return cleanedContent || 'No response generated.';
|
|
266
186
|
}
|
|
267
187
|
let canFastReturnAll = true;
|
|
268
188
|
let accumulatedResults = [];
|
|
269
189
|
// Enabled fastReturnTools to eliminate 2nd LLM latency for transaction popups
|
|
190
|
+
// FIX: Removed send_telegram_file — not a financial transaction
|
|
270
191
|
const fastReturnTools = [
|
|
271
192
|
'transfer_token', 'transfer_native', 'swap_token', 'bridge_token',
|
|
272
193
|
'mint_nft', 'custom_tx', 'revoke_approval', 'supply_aave',
|
|
@@ -277,9 +198,32 @@ CRITICAL INSTRUCTIONS:
|
|
|
277
198
|
let result = "";
|
|
278
199
|
let args = {};
|
|
279
200
|
const toolName = toolCall.function.name;
|
|
201
|
+
const getToolEmoji = (n) => {
|
|
202
|
+
if (n.includes('file') || n.includes('read') || n.includes('write'))
|
|
203
|
+
return '📄';
|
|
204
|
+
if (n.includes('dir') || n.includes('folder'))
|
|
205
|
+
return '📁';
|
|
206
|
+
if (n.includes('cmd') || n.includes('shell') || n.includes('run'))
|
|
207
|
+
return '🖥️';
|
|
208
|
+
if (n.includes('search') || n.includes('find'))
|
|
209
|
+
return '🔍';
|
|
210
|
+
if (n.includes('git'))
|
|
211
|
+
return '🐙';
|
|
212
|
+
return '⚙️';
|
|
213
|
+
};
|
|
214
|
+
const emoji = getToolEmoji(toolName);
|
|
215
|
+
let argsPreview = "";
|
|
216
|
+
try {
|
|
217
|
+
const parsedArgs = JSON.parse(toolCall.function.arguments || "{}");
|
|
218
|
+
const firstKey = Object.keys(parsedArgs)[0];
|
|
219
|
+
if (firstKey)
|
|
220
|
+
argsPreview = `"${parsedArgs[firstKey]}"`;
|
|
221
|
+
}
|
|
222
|
+
catch (e) { }
|
|
223
|
+
const previewMsg = argsPreview ? `${toolName}: ${argsPreview}` : toolName;
|
|
280
224
|
console.log(picocolors_1.default.yellow(`[⚡ Tool Execution] AI is calling ${toolName}...`));
|
|
281
225
|
if (onProgress)
|
|
282
|
-
onProgress(
|
|
226
|
+
onProgress(`*${emoji} ${previewMsg}*`);
|
|
283
227
|
try {
|
|
284
228
|
let argStr = toolCall.function.arguments;
|
|
285
229
|
// [Auto-Recovery] Fix common LLM JSON errors (e.g. missing closing brace)
|
|
@@ -335,7 +279,8 @@ CRITICAL INSTRUCTIONS:
|
|
|
335
279
|
content: result,
|
|
336
280
|
}, sessionId);
|
|
337
281
|
accumulatedResults.push(result);
|
|
338
|
-
|
|
282
|
+
// FIX: Removed send_telegram_file from fast-return set
|
|
283
|
+
if (!['transfer_token', 'transfer_native', 'swap_token', 'bridge_token', 'mint_nft', 'custom_tx', 'revoke_approval', 'supply_aave', 'deposit_yield_vault', 'provide_liquidity_v3'].includes(toolName)) {
|
|
339
284
|
canFastReturnAll = false;
|
|
340
285
|
}
|
|
341
286
|
}
|
|
@@ -352,6 +297,7 @@ CRITICAL INSTRUCTIONS:
|
|
|
352
297
|
if (consecutiveToolErrors >= 2) {
|
|
353
298
|
const errorSummary = `⚠️ Unable to complete this request after multiple attempts.\n${accumulatedResults.join('\n')}`;
|
|
354
299
|
exports.logger.addEntry({ role: 'assistant', content: errorSummary }, sessionId);
|
|
300
|
+
triggerBackgroundReview(sessionId);
|
|
355
301
|
return errorSummary;
|
|
356
302
|
}
|
|
357
303
|
}
|
|
@@ -362,12 +308,14 @@ CRITICAL INSTRUCTIONS:
|
|
|
362
308
|
if (canFastReturnAll && accumulatedResults.length > 0) {
|
|
363
309
|
const finalContent = accumulatedResults.join('\n\n---\n\n');
|
|
364
310
|
exports.logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
|
|
311
|
+
triggerBackgroundReview(sessionId);
|
|
365
312
|
return finalContent;
|
|
366
313
|
}
|
|
367
314
|
// Loop continues, sending tool results in the next turn
|
|
368
315
|
}
|
|
369
316
|
const maxTurnMsg = "⚠️ Reached maximum interaction limit (10 turns). Please be more specific.";
|
|
370
317
|
exports.logger.addEntry({ role: 'assistant', content: maxTurnMsg }, sessionId);
|
|
318
|
+
triggerBackgroundReview(sessionId);
|
|
371
319
|
return maxTurnMsg;
|
|
372
320
|
}
|
|
373
321
|
catch (error) {
|
|
@@ -408,8 +356,20 @@ CRITICAL INSTRUCTIONS:
|
|
|
408
356
|
4. Base your new answer strictly on the NEW tool results.`
|
|
409
357
|
}, sessionId);
|
|
410
358
|
}
|
|
359
|
+
// FIX: Lazy-init guard — same as processOsIntent
|
|
360
|
+
if (registry_1.pluginManager.getPlugins().length === 0) {
|
|
361
|
+
console.warn('[OsAgentStream] ⚠️ Plugins not initialized! Running lazy initializePlugins()...');
|
|
362
|
+
await (0, registry_1.initializePlugins)();
|
|
363
|
+
}
|
|
411
364
|
const pluginTools = registry_1.pluginManager.getAllToolDefinitions();
|
|
412
365
|
let activeTools = [...pluginTools].filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
|
|
366
|
+
if (activeTools.length === 0) {
|
|
367
|
+
console.error('[OsAgentStream] ❌ CRITICAL: No active tools found after initialization! Retrying...');
|
|
368
|
+
await (0, registry_1.initializePlugins)();
|
|
369
|
+
activeTools = [...registry_1.pluginManager.getAllToolDefinitions()].filter(t => (0, skillManager_1.isSkillActive)(t.function.name));
|
|
370
|
+
}
|
|
371
|
+
// FIX: Cache system prompt ONCE before loop (was being rebuilt every turn — wasteful)
|
|
372
|
+
const cachedSystemPromptStream = await getSystemPrompt('os', input);
|
|
413
373
|
const { sanitizeHistoryForLLM } = require('../utils/historySanitizer');
|
|
414
374
|
try {
|
|
415
375
|
let turnCount = 0;
|
|
@@ -420,13 +380,20 @@ CRITICAL INSTRUCTIONS:
|
|
|
420
380
|
turnCount++;
|
|
421
381
|
const currentHistory = exports.logger.getHistory(sessionId);
|
|
422
382
|
const sanitizedHistory = sanitizeHistoryForLLM(currentHistory, activeTools, config.llm.provider);
|
|
383
|
+
// FIX: Use cached system prompt — no longer rebuilt every turn
|
|
423
384
|
const messages = [
|
|
424
|
-
{ role: 'system', content:
|
|
385
|
+
{ role: 'system', content: cachedSystemPromptStream },
|
|
425
386
|
...sanitizedHistory
|
|
426
387
|
];
|
|
427
388
|
let streamedContent = '';
|
|
428
389
|
const response = await (0, llmUtils_1.executeWithRetry)(async (client) => {
|
|
429
|
-
|
|
390
|
+
streamedContent = '';
|
|
391
|
+
// RC#1 FIX: Only clear the Telegram buffer on the FIRST turn.
|
|
392
|
+
// On subsequent turns (after tool calls), the buffer already shows useful
|
|
393
|
+
// progress info. Resetting it causes visible content to disappear.
|
|
394
|
+
if (turnCount === 1)
|
|
395
|
+
onChunk('[CLEAR_STREAM]');
|
|
396
|
+
return await client.stream({ model: config.llm.model, temperature: config.llm.temperature, messages, tools: activeTools, reasoning_effort: (!config.llm.reasoning_effort || config.llm.reasoning_effort === 'none') ? undefined : config.llm.reasoning_effort }, (chunk) => {
|
|
430
397
|
streamedContent += chunk;
|
|
431
398
|
onChunk(chunk);
|
|
432
399
|
});
|
|
@@ -440,53 +407,33 @@ CRITICAL INSTRUCTIONS:
|
|
|
440
407
|
exports.logger.addEntry({
|
|
441
408
|
role: 'assistant',
|
|
442
409
|
content: responseMessage.content || '',
|
|
410
|
+
reasoning_content: responseMessage.reasoning_content,
|
|
443
411
|
tool_calls: responseMessage.tool_calls,
|
|
444
412
|
}, sessionId);
|
|
445
413
|
if (!responseMessage.tool_calls || responseMessage.tool_calls.length === 0) {
|
|
446
414
|
let finalContent = responseMessage.content || 'No response generated.';
|
|
447
415
|
finalContent = finalContent.replace(/<(think|thought|thinking|reasoning|analysis|reflection)[\s\S]*?<\/\1>\n?/gi, '').trim();
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
criticHasFiredStream = true;
|
|
452
|
-
try {
|
|
453
|
-
const criticRes = await fetch('http://127.0.0.1:8000/cognitive/critic', {
|
|
454
|
-
method: 'POST',
|
|
455
|
-
headers: { 'Content-Type': 'application/json' },
|
|
456
|
-
body: JSON.stringify({ user_input: input, draft_answer: finalContent, current_utc_datetime: new Date().toISOString() })
|
|
457
|
-
});
|
|
458
|
-
if (criticRes.ok) {
|
|
459
|
-
const evaluation = await criticRes.json();
|
|
460
|
-
if (evaluation.needs_revision) {
|
|
461
|
-
console.log(picocolors_1.default.cyan(`[🧠 Critic] Revision needed. Confidence: ${evaluation.factual_confidence?.toFixed(2)}. Completeness: ${evaluation.completeness?.toFixed(2)}. Re-generating...`));
|
|
462
|
-
exports.logger.addEntry({
|
|
463
|
-
role: 'system',
|
|
464
|
-
content: `[SELF-CRITIQUE — MANDATORY REVISION] Your previous answer was REJECTED. Issues:\n${evaluation.revision_instructions}\n\nCRITICAL REVISION RULES:\n1. Look at the tool results (search_web, etc.) ALREADY in this conversation history above.\n2. Base your revised answer EXCLUSIVELY on those tool results — NEVER use training data memory for facts, dates, or events.\n3. If tool results contain the answer, state it directly and confidently. Do NOT say an event hasn't happened if the tool results show it has.\n4. Do NOT call any tools again — the results are already in your history. USE THEM NOW.`
|
|
465
|
-
}, sessionId);
|
|
466
|
-
continue; // Loop kembali ke Generator untuk revisi
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
console.log(picocolors_1.default.green(`[🧠 Critic] Passed. Confidence: ${evaluation.factual_confidence?.toFixed(2)}, Completeness: ${evaluation.completeness?.toFixed(2)}.`));
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
catch {
|
|
474
|
-
// Python ML Engine tidak aktif — skip Critic, lanjut seperti biasa
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
// --- END CRITIC PASS ---
|
|
416
|
+
finalContent = finalContent.replace(/^\s*(?:\*\*)?(?:think|thought|thinking|reasoning|analysis|reflection)(?:\*\*)?\s*?\n[\s\S]*?\n\n/i, '').trim();
|
|
417
|
+
// --- CRITIC PASS REMOVED ---
|
|
418
|
+
// Prevents leaking internal reasoning into the stream and confusing the user.
|
|
478
419
|
fullResponse = finalContent;
|
|
420
|
+
triggerBackgroundReview(sessionId);
|
|
479
421
|
break;
|
|
480
422
|
}
|
|
481
|
-
// Tool calls detected — pause stream visually and execute tools
|
|
482
|
-
|
|
483
|
-
|
|
423
|
+
// Tool calls detected — pause stream visually and execute tools concurrently
|
|
424
|
+
// BUG#1 FIX: Signal to Telegram to wipe turn-1 planning text from the buffer.
|
|
425
|
+
// LLM often generates "thinking out loud" text before calling a tool (e.g. "Let me check...").
|
|
426
|
+
// This text should NOT be shown to users. [TOOL_CALL_DETECTED] resets the buffer to a
|
|
427
|
+
// clean progress indicator, hiding the planning text without losing the message handle.
|
|
428
|
+
onChunk('[TOOL_CALL_DETECTED]');
|
|
429
|
+
let shouldFastReturn = false;
|
|
484
430
|
const accumulatedResults = [];
|
|
485
|
-
|
|
431
|
+
const promises = responseMessage.tool_calls.map(async (_toolCall) => {
|
|
486
432
|
const toolCall = _toolCall;
|
|
487
433
|
const toolName = toolCall.function.name;
|
|
488
434
|
let result = '';
|
|
489
435
|
let args = {};
|
|
436
|
+
const context = { sessionId, toolCallId: toolCall.id, responseMessage };
|
|
490
437
|
console.log(picocolors_1.default.yellow(`[⚡ Tool Execution] AI is calling ${toolName}...`));
|
|
491
438
|
if (onProgress)
|
|
492
439
|
onProgress(`_⚡ Running tool: ${toolName}..._`);
|
|
@@ -499,17 +446,28 @@ CRITICAL INSTRUCTIONS:
|
|
|
499
446
|
catch (parseError) {
|
|
500
447
|
result = `[System Error] Arguments for ${toolName} must be valid JSON. Error: ${parseError.message}`;
|
|
501
448
|
exports.logger.addEntry({ role: 'tool', tool_call_id: toolCall.id, content: result }, sessionId);
|
|
502
|
-
|
|
449
|
+
return { toolName, result };
|
|
503
450
|
}
|
|
504
451
|
if (!(0, skillManager_1.isSkillActive)(toolName)) {
|
|
505
452
|
result = `[System Error] Access denied: Skill '${toolName}' is currently disabled.`;
|
|
506
453
|
exports.logger.addEntry({ role: 'tool', tool_call_id: toolCall.id, content: result }, sessionId);
|
|
507
|
-
|
|
454
|
+
return { toolName, result };
|
|
455
|
+
}
|
|
456
|
+
// --- LIFECYCLE: BEFORE TOOL CALL ---
|
|
457
|
+
const beforeRes = await registry_1.pluginManager.triggerBeforeHooks(toolName, args, context);
|
|
458
|
+
if (beforeRes && beforeRes.block) {
|
|
459
|
+
result = beforeRes.reason || `[System Error] Blocked by hook for ${toolName}`;
|
|
460
|
+
exports.logger.addEntry({ role: 'tool', tool_call_id: toolCall.id, content: result }, sessionId);
|
|
461
|
+
return { toolName, result };
|
|
508
462
|
}
|
|
509
463
|
try {
|
|
510
|
-
const pluginResult = await registry_1.pluginManager.executeTool(toolName, args,
|
|
464
|
+
const pluginResult = await registry_1.pluginManager.executeTool(toolName, args, context, (partialResult) => {
|
|
465
|
+
// Partial Streaming callback
|
|
466
|
+
if (onProgress)
|
|
467
|
+
onProgress(`_⏳ [${toolName}] ${partialResult}..._`);
|
|
468
|
+
});
|
|
511
469
|
result = pluginResult !== null ? pluginResult : `Error: Tool ${toolName} is not implemented.`;
|
|
512
|
-
if (result.includes('[Security Blocked]') || result.startsWith('Error:')) {
|
|
470
|
+
if (typeof result === 'string' && (result.includes('[Security Blocked]') || result.startsWith('Error:'))) {
|
|
513
471
|
console.log(picocolors_1.default.red(`[❌ Failed] Tool ${toolName} returned an error or was blocked.`));
|
|
514
472
|
}
|
|
515
473
|
else {
|
|
@@ -520,16 +478,23 @@ CRITICAL INSTRUCTIONS:
|
|
|
520
478
|
result = `Error executing ${toolName}: ${toolError.message}`;
|
|
521
479
|
console.error(picocolors_1.default.red(`[❌ Error Crash] Execution of ${toolName} failed: ${toolError.message}`));
|
|
522
480
|
}
|
|
481
|
+
// --- LIFECYCLE: AFTER TOOL CALL ---
|
|
482
|
+
const afterRes = await registry_1.pluginManager.triggerAfterHooks(toolName, args, result, context);
|
|
483
|
+
result = afterRes.content || result;
|
|
484
|
+
if (afterRes.terminate) {
|
|
485
|
+
shouldFastReturn = true;
|
|
486
|
+
}
|
|
523
487
|
exports.logger.addEntry({ role: 'tool', tool_call_id: toolCall.id, name: toolName, content: result }, sessionId);
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (
|
|
488
|
+
return { toolName, result };
|
|
489
|
+
});
|
|
490
|
+
const results = await Promise.all(promises);
|
|
491
|
+
results.forEach(r => accumulatedResults.push(r.result));
|
|
492
|
+
if (shouldFastReturn && accumulatedResults.length > 0) {
|
|
529
493
|
const finalContent = accumulatedResults.join('\n\n---\n\n');
|
|
530
494
|
exports.logger.addEntry({ role: 'assistant', content: finalContent }, sessionId);
|
|
531
495
|
onChunk(finalContent);
|
|
532
496
|
fullResponse = finalContent;
|
|
497
|
+
triggerBackgroundReview(sessionId);
|
|
533
498
|
break;
|
|
534
499
|
}
|
|
535
500
|
}
|
|
@@ -537,6 +502,7 @@ CRITICAL INSTRUCTIONS:
|
|
|
537
502
|
const maxTurnMsg = '⚠️ Reached maximum interaction limit (10 turns). Please be more specific.';
|
|
538
503
|
exports.logger.addEntry({ role: 'assistant', content: maxTurnMsg }, sessionId);
|
|
539
504
|
fullResponse = maxTurnMsg;
|
|
505
|
+
triggerBackgroundReview(sessionId);
|
|
540
506
|
}
|
|
541
507
|
return fullResponse;
|
|
542
508
|
}
|