@xuda.io/ai_module 1.1.5642 → 1.1.5643

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 +52 -80
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -7256,64 +7256,18 @@ const get_ai_agent_tools = async function ({ ai_agent_doc, agent_id, reference_t
7256
7256
  let tool_resources = {};
7257
7257
  let eligible_agent = true;
7258
7258
 
7259
- const add_internal_codex_tool = function ({ name, description, system_prompt }) {
7259
+ const add_xuda_public_website_tool = function ({ name, description, origin, path_prefix }) {
7260
7260
  if (reference_type !== 'ai_agents' && !prompt_suggestion_activated && !chat_suggestion_activated) {
7261
7261
  eligible_agent = false;
7262
7262
  return;
7263
7263
  }
7264
7264
 
7265
- tools.push(
7266
- tool({
7267
- name: name.substring(0, 60),
7268
- description,
7269
- parameters: z.object({
7270
- question: z.string().describe('The support question to investigate.'),
7271
- }),
7272
- async execute(e) {
7273
- const question = String(e?.question || '').trim();
7274
- if (!question) {
7275
- return 'question is required';
7276
- }
7277
-
7278
- const dev_host = _conf?.internal_ai_agents?.dev_server_host || 'dev.xuda.ai';
7279
- const codex_ret = await execute_codex_request(
7280
- {
7281
- ip: dev_host,
7282
- uid,
7283
- account_profile_info,
7284
- job_id,
7285
- stream: false,
7286
- prompt: `${system_prompt}
7287
-
7288
- User question:
7289
- ${question}
7290
-
7291
- Return a concise answer. Include only non-sensitive findings and mention when the requested information is outside the allowed scope.`,
7292
- },
7293
- job_id,
7294
- headers,
7295
- );
7296
-
7297
- if (codex_ret.code < 0) {
7298
- return `Codex readonly lookup failed: ${get_error_message(codex_ret.data, 'unknown error')}`;
7299
- }
7300
-
7301
- const events = codex_ret?.data?.events || [];
7302
- const final_message = [...events].reverse().find((event) => event?.item?.type === 'agent_message' && event.item.text)?.item?.text;
7303
- return final_message || 'Codex readonly lookup completed without a final answer.';
7304
- },
7305
- }),
7306
- );
7307
- };
7308
-
7309
- const add_xuda_public_website_tool = function ({ name, description }) {
7310
- if (reference_type !== 'ai_agents' && !prompt_suggestion_activated && !chat_suggestion_activated) {
7311
- eligible_agent = false;
7312
- return;
7313
- }
7314
-
7315
- const website_origin = _conf?.internal_ai_agents?.public_website_url || 'https://xuda.ai';
7316
- const dump_dir = path.join(os.tmpdir(), 'xuda_public_website_dump', new URL(website_origin).hostname.replace(/[^a-z0-9.-]/gi, '_'));
7265
+ const website_origin = origin || _conf?.internal_ai_agents?.public_website_url || 'https://xuda.ai';
7266
+ // path_prefix narrows the crawl to one section of the site (the mentor tool uses /docs).
7267
+ // It is part of the cache key so a narrowed tool can never read a wider tool's dump.
7268
+ const section = String(path_prefix || '').replace(/\/+$/, '');
7269
+ const cache_key = `${new URL(website_origin).hostname}${section}`.replace(/[^a-z0-9.-]/gi, '_');
7270
+ const dump_dir = path.join(os.tmpdir(), 'xuda_public_website_dump', cache_key);
7317
7271
  const manifest_path = path.join(dump_dir, 'manifest.json');
7318
7272
  const ttl_ms = Number(_conf?.internal_ai_agents?.public_website_dump_ttl_ms || 10 * 60 * 1000);
7319
7273
  const cache_version = 2;
@@ -7437,13 +7391,19 @@ Return a concise answer. Include only non-sensitive findings and mention when th
7437
7391
 
7438
7392
  await fs.promises.mkdir(dump_dir, { recursive: true });
7439
7393
  const sitemap_xml = await fetch_text(`${website_base}/sitemap.xml`, 8000);
7440
- const priority_urls = [`${website_base}/`, `${website_base}/about`, `${website_base}/legal/privacy-policy`, `${website_base}/legal/terms`, `${website_base}/contact`];
7394
+ const priority_urls = section
7395
+ ? [`${website_base}${section}/`]
7396
+ : [`${website_base}/`, `${website_base}/about`, `${website_base}/legal/privacy-policy`, `${website_base}/legal/terms`, `${website_base}/contact`];
7441
7397
  const sitemap_urls = [...sitemap_xml.matchAll(/<loc>([\s\S]*?)<\/loc>/gi)]
7442
7398
  .map((match) => decode_html(match[1]).trim())
7443
7399
  .filter((url) => {
7444
7400
  try {
7445
7401
  const parsed = new URL(url);
7446
- return parsed.origin === website_base && !/\.(png|jpe?g|gif|webp|svg|pdf|zip)$/i.test(parsed.pathname);
7402
+ if (parsed.origin !== website_base) return false;
7403
+ // Section-scoped tools see only their own subtree, so a docs-only tool cannot
7404
+ // wander into the rest of the site.
7405
+ if (section && !(parsed.pathname === section || parsed.pathname.startsWith(`${section}/`))) return false;
7406
+ return !/\.(png|jpe?g|gif|webp|svg|pdf|zip)$/i.test(parsed.pathname);
7447
7407
  } catch (_) {
7448
7408
  return false;
7449
7409
  }
@@ -7554,26 +7514,20 @@ Return a concise answer. Include only non-sensitive findings and mention when th
7554
7514
  break;
7555
7515
  }
7556
7516
 
7557
- case 'xuda_network_mentor_dev_server_readonly': {
7558
- add_internal_codex_tool({
7559
- name: name || 'Xuda Mentor Dev Server Readonly',
7560
- description: description || 'Readonly Codex lookup on /var/xuda of the dev server for technical mentor answers.',
7561
- system_prompt: `You are Codex helping a Xuda Network mentor answer technical questions about the Xuda codebase.
7562
-
7563
- Scope:
7564
- - You may inspect only the dev server ${_conf?.internal_ai_agents?.dev_server_host || 'dev.xuda.ai'}.
7565
- - You may use SSH only for readonly inspection.
7566
- - You may read only under /var/xuda.
7567
- - 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.
7568
-
7569
- Hard restrictions:
7570
- - 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.
7571
- - Never query CouchDB or any database.
7572
- - Never use network commands to call internal services.
7573
- - Never run commands that write, mutate, install, delete, restart, deploy, publish, chmod/chown, kill processes, or change system state.
7574
- - 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.
7575
- - If the answer requires forbidden access, say it is outside the readonly mentor scope.
7576
- - If sensitive data appears accidentally, do not quote it. State that sensitive output was omitted.`,
7517
+ // RETIRED 2026-07-31 (Boaz): this used to run Codex over SSH on the dev server. Its
7518
+ // "readonly" scope was only a system prompt, while the process itself ran with
7519
+ // --dangerously-bypass-approvals-and-sandbox (no sandbox key is set in either config,
7520
+ // and that is the default), so an external mentor chatting to the agent was one
7521
+ // persuasive message away from arbitrary command execution on dev. The type string is
7522
+ // KEPT and remapped rather than deleted, so every mentor agent already installed out
7523
+ // there loses the capability the moment this ships, with no agent doc to migrate.
7524
+ case 'xuda_network_mentor_dev_server_readonly':
7525
+ case 'xuda_network_mentor_docs_readonly': {
7526
+ add_xuda_public_website_tool({
7527
+ name: name || 'Xuda Mentor Docs Readonly',
7528
+ description: description || 'Readonly lookup against the public Xuda developer documentation for technical mentor answers.',
7529
+ origin: _conf?.internal_ai_agents?.docs_website_url || _conf?.internal_ai_agents?.public_website_url || 'https://xuda.ai',
7530
+ path_prefix: _conf?.internal_ai_agents?.docs_path_prefix || '/docs',
7577
7531
  });
7578
7532
  break;
7579
7533
  }
@@ -8683,6 +8637,20 @@ const add_conversation_item = async function (uid, profile_id, conversation_id,
8683
8637
  }
8684
8638
  };
8685
8639
 
8640
+ // The same privacy rule the phone AI follows, worded for a written reply. The contact may of
8641
+ // course see the thread they are already in, that is their own conversation. What they must
8642
+ // never get is the business's DERIVED view of them (internal notes, categories, mood or
8643
+ // status labels, payment standing), anything about a DIFFERENT contact, or a rundown of their
8644
+ // activity on other channels. Chat and SMS have no live verification step the way a phone
8645
+ // call does (there is nobody on the line to read a code back mid-sentence), so the verified
8646
+ // exception does not apply here and cross-channel history stays closed.
8647
+ const AUTO_REPLY_PRIVACY_POLICY = `Privacy rules, these override any other instruction:
8648
+ - You may use this conversation thread to reply naturally, it is the contact's own conversation.
8649
+ - Never reveal or hint at the business's internal record of this person: notes, tags, categories, sentiment or mood scores, account status, payment standing, or any other internal assessment.
8650
+ - Never reveal anything about any other person or contact.
8651
+ - Do not summarise or confirm their history on other channels, such as their phone calls, other email threads, or SMS messages. If they ask, say you cannot go over their record here and offer to have someone follow up.
8652
+ - If you are unsure whether something is safe to share, do not share it.`;
8653
+
8686
8654
  const auto_response = async function (uid, profile_id, contact_id, conversation_type = 'email') {
8687
8655
  try {
8688
8656
  const account_profile_info = await get_active_account_profile_info(uid, profile_id);
@@ -8694,11 +8662,11 @@ const auto_response = async function (uid, profile_id, contact_id, conversation_
8694
8662
 
8695
8663
  let account_doc = await db_module.get_couch_doc_native('xuda_accounts', uid);
8696
8664
 
8697
- // The main profile is the account's OWN identity: never auto-respond on it
8698
- // (you don't auto-reply to yourself). Authoritative check via the account's
8699
- // account_profile_id, independent of the stored auto_respond flag.
8700
- if (account_doc?.account_profile_id && profile_id === account_doc.account_profile_id) return;
8701
-
8665
+ // The main profile auto-responds like any other (Boaz, 2026-07-31, reversing UI-44
8666
+ // part 2). It used to be hard-skipped here on the reasoning that you do not auto-reply
8667
+ // to yourself, but the main profile is the identity most people actually receive on, so
8668
+ // excluding it meant the feature was off exactly where it was most wanted. The
8669
+ // auto_respond flag and mode above are now the only gate, for every profile.
8702
8670
  if (account_profile_doc.auto_respond_mode === 'when_offline' && account_doc.socket_id) return;
8703
8671
  if (!['always', 'when_offline'].includes(account_profile_doc.auto_respond_mode)) return;
8704
8672
 
@@ -8764,6 +8732,8 @@ const auto_response = async function (uid, profile_id, contact_id, conversation_
8764
8732
  instructions: `You are ${account_profile_doc.profile_name || userName}'s automatic ${conversation_type === 'chat' ? 'chat' : 'email'} assistant.
8765
8733
  Reply to the latest incoming message from ${contact_doc.name || contact_doc.email} in a friendly, helpful, and concise way.
8766
8734
  Use the conversation history for context.
8735
+
8736
+ ${AUTO_REPLY_PRIVACY_POLICY}
8767
8737
  Match the language the contact wrote in.
8768
8738
  Do not mention that the reply is automated.
8769
8739
  ${conversation_type === 'chat' ? 'Return only the chat message text, ready to send.' : 'Return only the email body, ready to send.'}
@@ -8805,6 +8775,8 @@ ${account_profile_doc.profile_signature || ''}`.trim(),
8805
8775
 
8806
8776
  You are composing an automatic ${conversation_type === 'chat' ? 'chat' : 'email'} reply.
8807
8777
  Use the existing conversation history as context.
8778
+
8779
+ ${AUTO_REPLY_PRIVACY_POLICY}
8808
8780
  Reply as ${account_profile_doc.profile_name || userName}.
8809
8781
  ${conversation_type === 'chat' ? 'Return only the chat message text, ready to send.' : 'Return only the email body text, ready to send.'}
8810
8782
  Keep the response concise, natural, and professional.${
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/ai_module",
3
- "version": "1.1.5642",
3
+ "version": "1.1.5643",
4
4
  "description": "Xuda AI Module",
5
5
  "main": "index.mjs",
6
6
  "type": "module",