@xuda.io/ai_module 1.1.5611 → 1.1.5613

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.mjs +358 -24
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -232,7 +232,7 @@ const email_ms = await import(`${module_path}/email_module/index_ms.mjs`);
232
232
  const ws_dashboard_msa = await import(`${module_path}/ws_dashboard_module/index_msa.mjs`);
233
233
  const account_msa = await import(`${module_path}/account_module/index_msa.mjs`);
234
234
  const drive_msa = await import(`${module_path}/drive_module/index_msa.mjs`);
235
- const npm_publish_msa = await import(`${module_path}/npm_publish_module/index_msa.mjs`);
235
+ const misc_msa = await import(`${module_path}/misc_module/index_msa.mjs`);
236
236
 
237
237
  var open_ai_status = {};
238
238
  const report_ai_status = function (model, err) {
@@ -2151,7 +2151,7 @@ const get_studio_doc = async function (req) {
2151
2151
  };
2152
2152
 
2153
2153
  export const get_ai_agent_info = async function (uid, job_id, headers, doc, from_marketplace) {
2154
- const account_profile_info = await get_active_account_profile_info(uid);
2154
+ const account_profile_info = await get_active_account_profile_info(uid, from_marketplace ? doc?.studio_meta?.account_profiles?.[0] : undefined);
2155
2155
 
2156
2156
  const get_contact_border = function (doc) {
2157
2157
  const disable_color = `#1a3557`;
@@ -2673,7 +2673,7 @@ export const delete_ai_agent = async function (req) {
2673
2673
  const save_ret = await db_module.save_app_couch_doc_native(account_profile_info.app_id, agent_doc);
2674
2674
 
2675
2675
  if (agent_doc?.agentConfig?.npm_package_name) {
2676
- npm_publish_msa.sync_agent_npm_state({ app_id: account_profile_info.app_id, prog_id: agent_id, event: 'deleted' });
2676
+ misc_msa.sync_agent_npm_state({ app_id: account_profile_info.app_id, prog_id: agent_id, event: 'deleted' });
2677
2677
  }
2678
2678
 
2679
2679
  for (const [key, val] of Object.entries(agent_doc?.agentConfig?.agent_tools || [])) {
@@ -2894,6 +2894,8 @@ export const start_cli_agent_conversation = async function (req) {
2894
2894
  if (!agent_id) return { code: -1, data: 'agent_id required' };
2895
2895
  try {
2896
2896
  const account_profile_info = await get_active_account_profile_info(uid);
2897
+ const profile_id = account_profile_info.account_profile_id;
2898
+ const conversation_obj = await create_openai_conversation();
2897
2899
  const conversation_id = await _common.xuda_get_uuid('chat_conversation');
2898
2900
  const now = Date.now();
2899
2901
  const doc = {
@@ -2907,23 +2909,25 @@ export const start_cli_agent_conversation = async function (req) {
2907
2909
  messages: [],
2908
2910
  reference_type: 'ai_agents',
2909
2911
  reference_id: agent_id,
2912
+ conversation_obj,
2913
+ reference_conversation_id: conversation_obj.id,
2910
2914
  conversation_type: 'ai_chat',
2911
2915
  initiator_uid: uid,
2912
- account_profiles: [account_profile_info._id],
2916
+ account_profiles: [profile_id],
2913
2917
  source: 'cli',
2914
2918
  };
2915
2919
  const save_ret = await db_module.save_app_couch_doc(account_profile_info.app_id, doc);
2916
2920
  if (save_ret.code < 0) return save_ret;
2917
- return { code: 1, data: { conversation_id, profile_id: account_profile_info._id, app_id: account_profile_info.app_id } };
2921
+ return { code: 1, data: { conversation_id, profile_id, app_id: account_profile_info.app_id } };
2918
2922
  } catch (err) {
2919
2923
  return { code: -1, data: err.message };
2920
2924
  }
2921
2925
  };
2922
2926
 
2923
2927
  export const update_ai_agent = async function (req, job_id, headers) {
2924
- let { agent_id, agentConfig, uid } = req;
2928
+ let { agent_id, agentConfig, uid, profile_id } = req;
2925
2929
 
2926
- const account_profile_info = await get_active_account_profile_info(uid);
2930
+ const account_profile_info = await get_active_account_profile_info(uid, profile_id);
2927
2931
 
2928
2932
  try {
2929
2933
  let agent_doc = await await db_module.get_app_couch_doc_native(account_profile_info.app_id, agent_id);
@@ -3329,11 +3333,11 @@ const save_agent_status = async function (uid, agent_id, stat, agentConfig) {
3329
3333
  };
3330
3334
 
3331
3335
  export const create_ai_agent = async function (req, job_id, headers) {
3332
- const { agentConfig, uid } = req;
3336
+ const { agentConfig, uid, profile_id } = req;
3333
3337
 
3334
3338
  const { account_project_id: app_id, account_info } = await account_ms.get_default_project_account_doc(uid);
3335
3339
 
3336
- const account_profile_info = await get_active_account_profile_info(uid);
3340
+ const account_profile_info = await get_active_account_profile_info(uid, profile_id);
3337
3341
  const { account_profile_obj } = account_profile_info;
3338
3342
  try {
3339
3343
  const param = { uid, properties: { menuName: agentConfig.agent_name, menuTitle: agentConfig.agent_name, menuType: 'ai_agent' }, checkedInUserName: account_info.first_name + ' ' + account_info.last_name, app_id: account_profile_info.app_id, parentId: 'ai_agents' };
@@ -3611,7 +3615,7 @@ export const update_thumbnail = async function (type, doc, app_id, uid, job_id,
3611
3615
 
3612
3616
  switch (type) {
3613
3617
  case 'ai_agent': {
3614
- const images_arr = await create_ai_agent_image({ app_id, uid, ai_agent_id: doc._id, tags }, job_id, headers);
3618
+ const images_arr = await create_ai_agent_image({ app_id, uid, ai_agent_id: doc._id, tags, account_profile_info }, job_id, headers);
3615
3619
  db_doc = await get_db_doc();
3616
3620
  db_doc.studio_meta.thumbnail_request_ts = Date.now();
3617
3621
  db_doc.studio_meta.agent_image = [images_arr.data];
@@ -4262,14 +4266,17 @@ export const create_conversation = async function (req, job_id, headers) {
4262
4266
  // write conversation to host after thumbnail update
4263
4267
  // get sender conversation
4264
4268
 
4265
- const conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_doc._id);
4269
+ // Re-fetch the freshest copy of the conversation doc after process_conversation
4270
+ // had a chance to write thumbnail / title updates. Renamed (was `conversation_doc`)
4271
+ // to avoid TDZ shadowing the outer-scope `conversation_doc` we read for the lookup id.
4272
+ const fresh_conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_doc._id);
4266
4273
 
4267
4274
  const recipient_account_profile_info = await get_active_account_profile_info(recipient_uid);
4268
- let recipient_conversation_doc = await db_module.get_app_couch_doc_native(recipient_account_profile_info.app_id, conversation_doc._id);
4269
- recipient_conversation_doc.title = conversation_doc.title;
4275
+ let recipient_conversation_doc = await db_module.get_app_couch_doc_native(recipient_account_profile_info.app_id, fresh_conversation_doc._id);
4276
+ recipient_conversation_doc.title = fresh_conversation_doc.title;
4270
4277
 
4271
- recipient_conversation_doc.thumbnail_request_ts = conversation_doc.thumbnail_request_ts;
4272
- recipient_conversation_doc.chat_image = conversation_doc.chat_image;
4278
+ recipient_conversation_doc.thumbnail_request_ts = fresh_conversation_doc.thumbnail_request_ts;
4279
+ recipient_conversation_doc.chat_image = fresh_conversation_doc.chat_image;
4273
4280
  recipient_conversation_doc.direction = 'in';
4274
4281
  await db_module.save_app_couch_doc_native(recipient_account_profile_info.app_id, recipient_conversation_doc);
4275
4282
 
@@ -5742,9 +5749,328 @@ const get_ai_agent_tools = async function ({ ai_agent_doc, agent_id, reference_t
5742
5749
  let tool_resources = {};
5743
5750
  let eligible_agent = true;
5744
5751
 
5752
+ const add_internal_codex_tool = function ({ name, description, system_prompt }) {
5753
+ if (reference_type !== 'ai_agents' && !prompt_suggestion_activated && !chat_suggestion_activated) {
5754
+ eligible_agent = false;
5755
+ return;
5756
+ }
5757
+
5758
+ tools.push(
5759
+ tool({
5760
+ name: name.substring(0, 60),
5761
+ description,
5762
+ parameters: z.object({
5763
+ question: z.string().describe('The support question to investigate.'),
5764
+ }),
5765
+ async execute(e) {
5766
+ const question = String(e?.question || '').trim();
5767
+ if (!question) {
5768
+ return 'question is required';
5769
+ }
5770
+
5771
+ const dev_host = _conf?.internal_ai_agents?.dev_server_host || 'dev.xuda.ai';
5772
+ const codex_ret = await execute_codex_request(
5773
+ {
5774
+ ip: dev_host,
5775
+ uid,
5776
+ account_profile_info,
5777
+ job_id,
5778
+ stream: false,
5779
+ prompt: `${system_prompt}
5780
+
5781
+ User question:
5782
+ ${question}
5783
+
5784
+ Return a concise answer. Include only non-sensitive findings and mention when the requested information is outside the allowed scope.`,
5785
+ },
5786
+ job_id,
5787
+ headers,
5788
+ );
5789
+
5790
+ if (codex_ret.code < 0) {
5791
+ return `Codex readonly lookup failed: ${get_error_message(codex_ret.data, 'unknown error')}`;
5792
+ }
5793
+
5794
+ const events = codex_ret?.data?.events || [];
5795
+ const final_message = [...events].reverse().find((event) => event?.item?.type === 'agent_message' && event.item.text)?.item?.text;
5796
+ return final_message || 'Codex readonly lookup completed without a final answer.';
5797
+ },
5798
+ }),
5799
+ );
5800
+ };
5801
+
5802
+ const add_xuda_public_website_tool = function ({ name, description }) {
5803
+ if (reference_type !== 'ai_agents' && !prompt_suggestion_activated && !chat_suggestion_activated) {
5804
+ eligible_agent = false;
5805
+ return;
5806
+ }
5807
+
5808
+ const website_origin = _conf?.internal_ai_agents?.public_website_url || 'https://xuda.ai';
5809
+ const dump_dir = path.join(os.tmpdir(), 'xuda_public_website_dump', new URL(website_origin).hostname.replace(/[^a-z0-9.-]/gi, '_'));
5810
+ const manifest_path = path.join(dump_dir, 'manifest.json');
5811
+ const ttl_ms = Number(_conf?.internal_ai_agents?.public_website_dump_ttl_ms || 10 * 60 * 1000);
5812
+ const cache_version = 2;
5813
+ const website_base = website_origin.replace(/\/$/, '');
5814
+
5815
+ const decode_html = function (value = '') {
5816
+ return String(value)
5817
+ .replace(/&nbsp;/gi, ' ')
5818
+ .replace(/&amp;/gi, '&')
5819
+ .replace(/&lt;/gi, '<')
5820
+ .replace(/&gt;/gi, '>')
5821
+ .replace(/&quot;/gi, '"')
5822
+ .replace(/&#39;/g, "'");
5823
+ };
5824
+
5825
+ const strip_html = function (html = '') {
5826
+ return decode_html(
5827
+ String(html)
5828
+ .replace(/<script[\s\S]*?<\/script>/gi, ' ')
5829
+ .replace(/<style[\s\S]*?<\/style>/gi, ' ')
5830
+ .replace(/<noscript[\s\S]*?<\/noscript>/gi, ' ')
5831
+ .replace(/<svg[\s\S]*?<\/svg>/gi, ' ')
5832
+ .replace(/<[^>]+>/g, ' ')
5833
+ .replace(/\s+/g, ' ')
5834
+ .trim(),
5835
+ );
5836
+ };
5837
+
5838
+ const extract_title = function (html = '') {
5839
+ const match = String(html).match(/<title[^>]*>([\s\S]*?)<\/title>/i);
5840
+ return match ? decode_html(match[1]).replace(/\s+/g, ' ').trim() : '';
5841
+ };
5842
+
5843
+ const get_terms = function (question) {
5844
+ const terms = String(question)
5845
+ .toLowerCase()
5846
+ .replace(/[^a-z0-9\s-]/g, ' ')
5847
+ .split(/\s+/)
5848
+ .filter((term) => term.length > 2 && !['the', 'and', 'for', 'about', 'tell', 'with', 'from', 'what', 'who', 'how', 'xuda'].includes(term));
5849
+ if (/\baddress|where|location|located|office|contact\b/i.test(question)) {
5850
+ terms.push('address', 'contact', 'llc', 'boca', 'raton', 'florida');
5851
+ }
5852
+ return [...new Set(terms)];
5853
+ };
5854
+
5855
+ const score_page = function (page, terms, question) {
5856
+ const haystack = `${page.url} ${page.title} ${page.text}`.toLowerCase();
5857
+ let score = 0;
5858
+ for (const term of terms) {
5859
+ if (haystack.includes(term)) score += term.length > 4 ? 3 : 1;
5860
+ }
5861
+ if (/\b(company|about|xuda)\b/i.test(question) && new URL(page.url).pathname === '/') score += 20;
5862
+ if (/\bdocs?|guide|how|cli|dashboard|deploy|agent|app|vps|database|couchdb\b/i.test(question) && page.url.includes('/docs')) score += 8;
5863
+ if (/\bnetwork|ambassador|mentor|city|region\b/i.test(question) && page.url.includes('network')) score += 8;
5864
+ if (/\baddress|where|location|located|office|contact\b/i.test(question) && /\/legal\/privacy-policy/i.test(page.url)) score += 100;
5865
+ if (/\baddress|where|location|located|office|contact\b/i.test(question) && /\/legal\/terms|contact/i.test(page.url)) score += 30;
5866
+ return score;
5867
+ };
5868
+
5869
+ const make_snippet = function (text = '', terms = [], max_length = 3000) {
5870
+ const lower = text.toLowerCase();
5871
+ const anchors = ['address', 'contact us', 'xuda llc', 'boca raton', 'florida', ...terms];
5872
+ let index = -1;
5873
+ for (const anchor of anchors) {
5874
+ index = lower.indexOf(String(anchor).toLowerCase());
5875
+ if (index > -1) break;
5876
+ }
5877
+ if (index < 0) return text.slice(0, max_length);
5878
+ const start = Math.max(0, index - Math.floor(max_length / 3));
5879
+ const end = Math.min(text.length, start + max_length);
5880
+ return `${start > 0 ? '...' : ''}${text.slice(start, end)}${end < text.length ? '...' : ''}`;
5881
+ };
5882
+
5883
+ const extract_public_facts = function (page, question) {
5884
+ const facts = [];
5885
+ if (/\baddress|where|location|located|office|contact\b/i.test(question)) {
5886
+ const address_match = String(page.text || '').match(/Address:\s*([^|]+?)(?=\s+By using|\s+Privacy Policy|\s+Terms|\s*$)/i);
5887
+ if (address_match?.[1]) {
5888
+ facts.push({
5889
+ label: 'Address',
5890
+ value: address_match[1].replace(/\s+/g, ' ').trim(),
5891
+ url: page.url,
5892
+ });
5893
+ }
5894
+ }
5895
+ return facts;
5896
+ };
5897
+
5898
+ const fetch_text = async function (url, timeout_ms = 8000) {
5899
+ const controller = new AbortController();
5900
+ const timer = setTimeout(() => controller.abort(), timeout_ms);
5901
+ try {
5902
+ const res = await fetch(url, {
5903
+ signal: controller.signal,
5904
+ headers: { 'User-Agent': 'Xuda Ambassador Support Readonly Website Indexer' },
5905
+ });
5906
+ if (!res.ok) throw new Error(`${url} returned ${res.status}`);
5907
+ return await res.text();
5908
+ } finally {
5909
+ clearTimeout(timer);
5910
+ }
5911
+ };
5912
+
5913
+ const load_dump = async function () {
5914
+ try {
5915
+ const manifest = JSON.parse(await fs.promises.readFile(manifest_path, 'utf8'));
5916
+ if (Number(manifest.cache_version || 0) === cache_version && Date.now() - Number(manifest.created_ts || 0) < ttl_ms && Array.isArray(manifest.pages) && manifest.pages.length) {
5917
+ const pages = [];
5918
+ for (const page of manifest.pages) {
5919
+ try {
5920
+ pages.push(JSON.parse(await fs.promises.readFile(path.join(dump_dir, page.file), 'utf8')));
5921
+ } catch (_) {
5922
+ /* ignore corrupt cache item */
5923
+ }
5924
+ }
5925
+ if (pages.length) return { pages, from_cache: true };
5926
+ }
5927
+ } catch (_) {
5928
+ /* build cache below */
5929
+ }
5930
+
5931
+ await fs.promises.mkdir(dump_dir, { recursive: true });
5932
+ const sitemap_xml = await fetch_text(`${website_base}/sitemap.xml`, 8000);
5933
+ const priority_urls = [`${website_base}/`, `${website_base}/about`, `${website_base}/legal/privacy-policy`, `${website_base}/legal/terms`, `${website_base}/contact`];
5934
+ const sitemap_urls = [...sitemap_xml.matchAll(/<loc>([\s\S]*?)<\/loc>/gi)]
5935
+ .map((match) => decode_html(match[1]).trim())
5936
+ .filter((url) => {
5937
+ try {
5938
+ const parsed = new URL(url);
5939
+ return parsed.origin === website_base && !/\.(png|jpe?g|gif|webp|svg|pdf|zip)$/i.test(parsed.pathname);
5940
+ } catch (_) {
5941
+ return false;
5942
+ }
5943
+ })
5944
+ .slice(0, 120);
5945
+ const urls = [...new Set([...priority_urls, ...sitemap_urls])];
5946
+
5947
+ const pages = [];
5948
+ for (const url of urls) {
5949
+ try {
5950
+ const html = await fetch_text(url, 8000);
5951
+ const page = {
5952
+ url,
5953
+ title: extract_title(html),
5954
+ text: strip_html(html).slice(0, 80000),
5955
+ fetched_ts: Date.now(),
5956
+ };
5957
+ if (page.text.length < 80) continue;
5958
+ const file = `${crypto.createHash('sha1').update(url).digest('hex')}.json`;
5959
+ await fs.promises.writeFile(path.join(dump_dir, file), JSON.stringify(page, null, 2));
5960
+ pages.push({ ...page, file });
5961
+ } catch (err) {
5962
+ pages.push({ url, title: '', text: `Fetch failed: ${err.message || String(err)}`, fetch_error: true });
5963
+ }
5964
+ }
5965
+
5966
+ await fs.promises.writeFile(
5967
+ manifest_path,
5968
+ JSON.stringify(
5969
+ {
5970
+ source: website_origin,
5971
+ cache_version,
5972
+ created_ts: Date.now(),
5973
+ pages: pages.filter((page) => !page.fetch_error).map((page) => ({ url: page.url, file: page.file, title: page.title })),
5974
+ },
5975
+ null,
5976
+ 2,
5977
+ ),
5978
+ );
5979
+ return { pages, from_cache: false };
5980
+ };
5981
+
5982
+ tools.push(
5983
+ tool({
5984
+ name: (name || 'Xuda Ambassador Public Website Readonly').substring(0, 60),
5985
+ description: description || 'Readonly lookup against public xuda.ai website pages for ambassador support answers.',
5986
+ parameters: z.object({
5987
+ question: z.string().describe('The support question to answer from public xuda.ai website pages.'),
5988
+ }),
5989
+ async execute(e) {
5990
+ const question = String(e?.question || '').trim();
5991
+ if (!question) {
5992
+ return 'question is required';
5993
+ }
5994
+
5995
+ const run_lookup = async function () {
5996
+ const { pages } = await load_dump();
5997
+ const terms = get_terms(question);
5998
+ const ranked = pages
5999
+ .filter((page) => !page.fetch_error)
6000
+ .map((page) => ({ page, score: score_page(page, terms, question) }))
6001
+ .filter((item) => item.score > 0)
6002
+ .sort((a, b) => b.score - a.score)
6003
+ .slice(0, 5)
6004
+ .map((item) => ({
6005
+ score: item.score,
6006
+ url: item.page.url,
6007
+ title: item.page.title,
6008
+ snippet: make_snippet(item.page.text, terms),
6009
+ }));
6010
+ const facts = pages
6011
+ .filter((page) => !page.fetch_error)
6012
+ .flatMap((page) => extract_public_facts(page, question))
6013
+ .slice(0, 5);
6014
+
6015
+ return {
6016
+ source: website_origin,
6017
+ readonly: true,
6018
+ question,
6019
+ facts,
6020
+ result_count: ranked.length,
6021
+ results: ranked,
6022
+ instruction: ranked.length
6023
+ ? 'Answer only from these public xuda.ai website facts and snippets. If facts are present, use them as the primary source. Cite page URLs inline when useful. Do not mention internal cache, dump folders, or implementation details.'
6024
+ : 'No relevant public xuda.ai pages were found. Say that the public xuda.ai website did not contain enough information and ask for a narrower page or topic. Do not mention internal cache, dump folders, or implementation details.',
6025
+ };
6026
+ };
6027
+
6028
+ try {
6029
+ const data = await Promise.race([run_lookup(), new Promise((_, reject) => setTimeout(() => reject(new Error('xuda.ai website lookup timed out')), 20000))]);
6030
+ return JSON.stringify(data, null, 2);
6031
+ } catch (err) {
6032
+ return `xuda.ai website lookup failed: ${err.message || String(err)}`;
6033
+ }
6034
+ },
6035
+ }),
6036
+ );
6037
+ };
6038
+
5745
6039
  for (const [key, val] of Object.entries(ai_agent_doc?.agentConfig?.agent_tools || [])) {
5746
6040
  const { name = '', description = '' } = val;
5747
6041
  switch (val.type) {
6042
+ case 'xuda_network_ambassador_dev_couchdb_readonly': {
6043
+ add_xuda_public_website_tool({
6044
+ name: name || 'Xuda Ambassador Public Website Readonly',
6045
+ description: description || 'Readonly lookup against public xuda.ai website pages for ambassador support answers.',
6046
+ });
6047
+ break;
6048
+ }
6049
+
6050
+ case 'xuda_network_mentor_dev_server_readonly': {
6051
+ add_internal_codex_tool({
6052
+ name: name || 'Xuda Mentor Dev Server Readonly',
6053
+ description: description || 'Readonly Codex lookup on /var/xuda of the dev server for technical mentor answers.',
6054
+ system_prompt: `You are Codex helping a Xuda Network mentor answer technical questions about the Xuda codebase.
6055
+
6056
+ Scope:
6057
+ - You may inspect only the dev server ${_conf?.internal_ai_agents?.dev_server_host || 'dev.xuda.ai'}.
6058
+ - You may use SSH only for readonly inspection.
6059
+ - You may read only under /var/xuda.
6060
+ - Prefer safe inspection commands such as pwd, ls, find, rg, sed -n, head, tail, wc, git status, git diff --stat, git log --oneline, and git show.
6061
+
6062
+ Hard restrictions:
6063
+ - Never access config, config_dev, .env files, secret folders, SSH keys, certificates, credential files, database dumps, backups, logs likely to contain credentials, or any path outside /var/xuda.
6064
+ - Never query CouchDB or any database.
6065
+ - Never use network commands to call internal services.
6066
+ - Never run commands that write, mutate, install, delete, restart, deploy, publish, chmod/chown, kill processes, or change system state.
6067
+ - Never reveal passwords, tokens, API keys, private keys, names of private people, private emails, personal information, customer data, security details, or anything that could harm Xuda, its team, customers, or product security.
6068
+ - If the answer requires forbidden access, say it is outside the readonly mentor scope.
6069
+ - If sensitive data appears accidentally, do not quote it. State that sensitive output was omitted.`,
6070
+ });
6071
+ break;
6072
+ }
6073
+
5748
6074
  case 'web_search': {
5749
6075
  tools.push(webSearchTool());
5750
6076
  break;
@@ -6033,8 +6359,8 @@ const load_ai_agent_doc = async function (app_id, agent_id) {
6033
6359
  ai_agent_doc.reference_doc = reference_doc;
6034
6360
  } else if (ai_agent_doc.studio_meta.installed_from_app_uid) {
6035
6361
  const reference_doc = _.cloneDeep(ai_agent_doc);
6036
- await get_account_default_project_id(ai_agent_doc.studio_meta.installed_from_app_uid);
6037
- ai_agent_doc = await db_module.get_app_couch_doc_native(app_id, ai_agent_doc.studio_meta.share_item_id);
6362
+ const source_app_id = ai_agent_doc.studio_meta.installed_from_app_id || (await get_account_default_project_id(ai_agent_doc.studio_meta.installed_from_app_uid));
6363
+ ai_agent_doc = await db_module.get_app_couch_doc_native(source_app_id, ai_agent_doc.studio_meta.share_item_id || ai_agent_doc._id);
6038
6364
  ai_agent_doc.reference_doc = reference_doc;
6039
6365
  }
6040
6366
 
@@ -7406,10 +7732,10 @@ const get_plugin_tool = async function (app_id, uid, plugin_name, method, prop_d
7406
7732
  }
7407
7733
  };
7408
7734
 
7409
- const create_and_upload_image_to_drive = async function (drive_type, file_path, file_name, prompt, numGenerations, uid, app_id, job_id, headers = {}, is_system, tags = [], account_profile_info) {
7735
+ const create_and_upload_image_to_drive = async function (drive_type, file_path, file_name, prompt, numGenerations, uid, app_id, job_id, headers = {}, is_system, tags = [], account_profile_info, width = 256, height = 256) {
7410
7736
  try {
7411
7737
  // 1. Generate the images
7412
- let result = await create_image(uid, prompt, undefined, undefined, numGenerations, undefined, undefined, { drive_type, file_path, file_name }, account_profile_info);
7738
+ let result = await create_image(uid, prompt, undefined, undefined, numGenerations, width, height, { drive_type, file_path, file_name }, account_profile_info);
7413
7739
 
7414
7740
  // Normalize to array
7415
7741
  const images = Array.isArray(result) ? result : [result];
@@ -7865,6 +8191,14 @@ export const get_profile_picture = async function (uid, account_type = 'business
7865
8191
  const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
7866
8192
  const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`);
7867
8193
 
8194
+ // Normalize: visitor accounts created through the chat widget can arrive with
8195
+ // account_type missing, null, or some other value. Treat anything that isn't
8196
+ // explicitly 'business' as 'personal' so the chat flow doesn't crash before
8197
+ // ever reaching the agent reply.
8198
+ if (account_type !== 'personal' && account_type !== 'business') {
8199
+ account_type = 'personal';
8200
+ }
8201
+
7868
8202
  const { email, create_avatar } = metadata;
7869
8203
 
7870
8204
  let prompt,
@@ -8250,12 +8584,12 @@ const create_ai_agent_image = async function (req, job_id, headers) {
8250
8584
  const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`);
8251
8585
 
8252
8586
  const { app_id, uid, ai_agent_id, tags } = req;
8253
- const account_profile_info = await get_active_account_profile_info(uid);
8587
+ const account_profile_info = req.account_profile_info || (await get_active_account_profile_info(uid));
8254
8588
  const ai_agent_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, ai_agent_id);
8255
8589
  const account_doc = await db_module.get_couch_doc_native('xuda_accounts', uid);
8256
8590
 
8257
8591
  // faceless humanoid for business
8258
- if (account_doc.account_info.account_type === 'business') {
8592
+ if (account_doc.account_info.account_type === 'business' || ai_agent_doc.studio_meta?.force_faceless_agent_image) {
8259
8593
  const prompt = `
8260
8594
  Create a futuristic, faceless humanoid profile portrait with a transparent background.
8261
8595
 
@@ -8290,7 +8624,7 @@ const create_ai_agent_image = async function (req, job_id, headers) {
8290
8624
 
8291
8625
  `;
8292
8626
 
8293
- const images_arr = await create_and_upload_image_to_drive('studio', 'Progs Thumbnails', ai_agent_id, prompt, 1, uid, app_id, job_id, headers, false, tags, account_profile_info);
8627
+ const images_arr = await create_and_upload_image_to_drive('studio', 'Progs Thumbnails', ai_agent_id, prompt, 1, uid, app_id, job_id, headers, false, tags, account_profile_info, 1024, 1024);
8294
8628
  return { code: 1, data: images_arr[0] };
8295
8629
  }
8296
8630
 
@@ -13538,8 +13872,8 @@ export const get_chat_suggestions = async function (req) {
13538
13872
  ai_agent_doc.reference_doc = reference_doc;
13539
13873
  } else if (ai_agent_doc.studio_meta.installed_from_app_uid) {
13540
13874
  const reference_doc = _.cloneDeep(ai_agent_doc);
13541
- await get_account_default_project_id(ai_agent_doc.studio_meta.installed_from_app_uid);
13542
- ai_agent_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, ai_agent_doc.studio_meta.share_item_id);
13875
+ const source_app_id = ai_agent_doc.studio_meta.installed_from_app_id || (await get_account_default_project_id(ai_agent_doc.studio_meta.installed_from_app_uid));
13876
+ ai_agent_doc = await db_module.get_app_couch_doc_native(source_app_id, ai_agent_doc.studio_meta.share_item_id || ai_agent_doc._id);
13543
13877
  ai_agent_doc.reference_doc = reference_doc;
13544
13878
  }
13545
13879
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/ai_module",
3
- "version": "1.1.5611",
3
+ "version": "1.1.5613",
4
4
  "description": "Xuda AI Module",
5
5
  "main": "index.mjs",
6
6
  "type": "module",