@xuda.io/ai_module 1.1.5642 → 1.1.5644
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/index.mjs +74 -82
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -253,7 +253,19 @@ const get_openai_codex_exec_args = function (codex_model, opts = {}) {
|
|
|
253
253
|
// end-user prompts can't run Codex with full host access. The studio/vibe flow
|
|
254
254
|
// passes nothing and keeps the global setting.
|
|
255
255
|
const sandbox = opts.sandbox || _conf.openai_codex_sandbox;
|
|
256
|
-
|
|
256
|
+
// Precedence: an explicit per-call sandbox always confines the run; opts.bypass_sandbox is
|
|
257
|
+
// the one deliberate escape hatch (the customer-VPS vibe build, which runs on the
|
|
258
|
+
// customer's OWN box and needs to install and restart things there); otherwise the config
|
|
259
|
+
// decides. The config default is now false, so a new call site that forgets to say
|
|
260
|
+
// anything gets a CONFINED run instead of full host access, which is how the mentor Codex
|
|
261
|
+
// tool ended up unsandboxed.
|
|
262
|
+
const bypass_approvals_and_sandbox = opts.sandbox
|
|
263
|
+
? false
|
|
264
|
+
: opts.bypass_sandbox === true
|
|
265
|
+
? true
|
|
266
|
+
: typeof _conf.openai_codex_bypass_approvals_and_sandbox === 'undefined'
|
|
267
|
+
? true
|
|
268
|
+
: _conf.openai_codex_bypass_approvals_and_sandbox;
|
|
257
269
|
|
|
258
270
|
if (codex_model) {
|
|
259
271
|
// codex_model is a catalog code (user pick or cheapest default); the Codex CLI
|
|
@@ -1386,6 +1398,9 @@ export const execute_codex_request = async function (req_or_ip, prompt_arg, atta
|
|
|
1386
1398
|
// Optional per-call hardening (used by the AI site generator for untrusted
|
|
1387
1399
|
// prompts): a confined sandbox + a hard wall-clock timeout on the codex run.
|
|
1388
1400
|
const sandbox = is_req_call ? req_or_ip.sandbox : undefined;
|
|
1401
|
+
// Explicit opt-out of the confined default, for runs that legitimately need full access
|
|
1402
|
+
// on a box the customer owns. Never set this for anything running on Xuda infrastructure.
|
|
1403
|
+
const bypass_sandbox = is_req_call ? req_or_ip.bypass_sandbox === true : false;
|
|
1389
1404
|
const codex_timeout = is_req_call ? req_or_ip.codex_timeout : undefined;
|
|
1390
1405
|
// Optional: a scoped MCP server exposed to codex (the in-app vibe's Xuda
|
|
1391
1406
|
// platform actions). { url, bearer_env_var, bearer_value } — the bearer is
|
|
@@ -1594,7 +1609,7 @@ ${prompt}`;
|
|
|
1594
1609
|
}
|
|
1595
1610
|
|
|
1596
1611
|
const openai_codex_command = get_openai_codex_cli_command();
|
|
1597
|
-
let codex_args = get_openai_codex_exec_args(codex_model, { sandbox });
|
|
1612
|
+
let codex_args = get_openai_codex_exec_args(codex_model, { sandbox, bypass_sandbox });
|
|
1598
1613
|
if (agent_mcp && agent_mcp.url) {
|
|
1599
1614
|
// Register the Xuda MCP server for this run. Global `-c` overrides must
|
|
1600
1615
|
// precede the `exec` subcommand (args[0]), so prepend them. The bearer
|
|
@@ -7037,6 +7052,11 @@ ${conversation_history || `User (dashboard): ${prompt}`}
|
|
|
7037
7052
|
prompt: codex_prompt,
|
|
7038
7053
|
attachments,
|
|
7039
7054
|
stream: false,
|
|
7055
|
+
// Deliberate: this build runs on the CUSTOMER'S OWN VPS over SSH and has to install
|
|
7056
|
+
// packages, write outside a workspace and restart services there, so it keeps full
|
|
7057
|
+
// access. Stated explicitly now that the global default is confined, so the choice
|
|
7058
|
+
// is visible instead of inherited. Nothing on Xuda infrastructure may set this.
|
|
7059
|
+
bypass_sandbox: true,
|
|
7040
7060
|
...(agent_key ? { agent_mcp: { url: 'http://localhost:3012/mcp', bearer_env_var: 'XUDA_AGENT_KEY', bearer_value: agent_key } } : {}),
|
|
7041
7061
|
},
|
|
7042
7062
|
job_id,
|
|
@@ -7256,64 +7276,18 @@ const get_ai_agent_tools = async function ({ ai_agent_doc, agent_id, reference_t
|
|
|
7256
7276
|
let tool_resources = {};
|
|
7257
7277
|
let eligible_agent = true;
|
|
7258
7278
|
|
|
7259
|
-
const
|
|
7279
|
+
const add_xuda_public_website_tool = function ({ name, description, origin, path_prefix }) {
|
|
7260
7280
|
if (reference_type !== 'ai_agents' && !prompt_suggestion_activated && !chat_suggestion_activated) {
|
|
7261
7281
|
eligible_agent = false;
|
|
7262
7282
|
return;
|
|
7263
7283
|
}
|
|
7264
7284
|
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
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, '_'));
|
|
7285
|
+
const website_origin = origin || _conf?.internal_ai_agents?.public_website_url || 'https://xuda.ai';
|
|
7286
|
+
// path_prefix narrows the crawl to one section of the site (the mentor tool uses /docs).
|
|
7287
|
+
// It is part of the cache key so a narrowed tool can never read a wider tool's dump.
|
|
7288
|
+
const section = String(path_prefix || '').replace(/\/+$/, '');
|
|
7289
|
+
const cache_key = `${new URL(website_origin).hostname}${section}`.replace(/[^a-z0-9.-]/gi, '_');
|
|
7290
|
+
const dump_dir = path.join(os.tmpdir(), 'xuda_public_website_dump', cache_key);
|
|
7317
7291
|
const manifest_path = path.join(dump_dir, 'manifest.json');
|
|
7318
7292
|
const ttl_ms = Number(_conf?.internal_ai_agents?.public_website_dump_ttl_ms || 10 * 60 * 1000);
|
|
7319
7293
|
const cache_version = 2;
|
|
@@ -7437,13 +7411,19 @@ Return a concise answer. Include only non-sensitive findings and mention when th
|
|
|
7437
7411
|
|
|
7438
7412
|
await fs.promises.mkdir(dump_dir, { recursive: true });
|
|
7439
7413
|
const sitemap_xml = await fetch_text(`${website_base}/sitemap.xml`, 8000);
|
|
7440
|
-
const priority_urls =
|
|
7414
|
+
const priority_urls = section
|
|
7415
|
+
? [`${website_base}${section}/`]
|
|
7416
|
+
: [`${website_base}/`, `${website_base}/about`, `${website_base}/legal/privacy-policy`, `${website_base}/legal/terms`, `${website_base}/contact`];
|
|
7441
7417
|
const sitemap_urls = [...sitemap_xml.matchAll(/<loc>([\s\S]*?)<\/loc>/gi)]
|
|
7442
7418
|
.map((match) => decode_html(match[1]).trim())
|
|
7443
7419
|
.filter((url) => {
|
|
7444
7420
|
try {
|
|
7445
7421
|
const parsed = new URL(url);
|
|
7446
|
-
|
|
7422
|
+
if (parsed.origin !== website_base) return false;
|
|
7423
|
+
// Section-scoped tools see only their own subtree, so a docs-only tool cannot
|
|
7424
|
+
// wander into the rest of the site.
|
|
7425
|
+
if (section && !(parsed.pathname === section || parsed.pathname.startsWith(`${section}/`))) return false;
|
|
7426
|
+
return !/\.(png|jpe?g|gif|webp|svg|pdf|zip)$/i.test(parsed.pathname);
|
|
7447
7427
|
} catch (_) {
|
|
7448
7428
|
return false;
|
|
7449
7429
|
}
|
|
@@ -7554,26 +7534,20 @@ Return a concise answer. Include only non-sensitive findings and mention when th
|
|
|
7554
7534
|
break;
|
|
7555
7535
|
}
|
|
7556
7536
|
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
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.`,
|
|
7537
|
+
// RETIRED 2026-07-31 (Boaz): this used to run Codex over SSH on the dev server. Its
|
|
7538
|
+
// "readonly" scope was only a system prompt, while the process itself ran with
|
|
7539
|
+
// --dangerously-bypass-approvals-and-sandbox (no sandbox key is set in either config,
|
|
7540
|
+
// and that is the default), so an external mentor chatting to the agent was one
|
|
7541
|
+
// persuasive message away from arbitrary command execution on dev. The type string is
|
|
7542
|
+
// KEPT and remapped rather than deleted, so every mentor agent already installed out
|
|
7543
|
+
// there loses the capability the moment this ships, with no agent doc to migrate.
|
|
7544
|
+
case 'xuda_network_mentor_dev_server_readonly':
|
|
7545
|
+
case 'xuda_network_mentor_docs_readonly': {
|
|
7546
|
+
add_xuda_public_website_tool({
|
|
7547
|
+
name: name || 'Xuda Mentor Docs Readonly',
|
|
7548
|
+
description: description || 'Readonly lookup against the public Xuda developer documentation for technical mentor answers.',
|
|
7549
|
+
origin: _conf?.internal_ai_agents?.docs_website_url || _conf?.internal_ai_agents?.public_website_url || 'https://xuda.ai',
|
|
7550
|
+
path_prefix: _conf?.internal_ai_agents?.docs_path_prefix || '/docs',
|
|
7577
7551
|
});
|
|
7578
7552
|
break;
|
|
7579
7553
|
}
|
|
@@ -8683,6 +8657,20 @@ const add_conversation_item = async function (uid, profile_id, conversation_id,
|
|
|
8683
8657
|
}
|
|
8684
8658
|
};
|
|
8685
8659
|
|
|
8660
|
+
// The same privacy rule the phone AI follows, worded for a written reply. The contact may of
|
|
8661
|
+
// course see the thread they are already in, that is their own conversation. What they must
|
|
8662
|
+
// never get is the business's DERIVED view of them (internal notes, categories, mood or
|
|
8663
|
+
// status labels, payment standing), anything about a DIFFERENT contact, or a rundown of their
|
|
8664
|
+
// activity on other channels. Chat and SMS have no live verification step the way a phone
|
|
8665
|
+
// call does (there is nobody on the line to read a code back mid-sentence), so the verified
|
|
8666
|
+
// exception does not apply here and cross-channel history stays closed.
|
|
8667
|
+
const AUTO_REPLY_PRIVACY_POLICY = `Privacy rules, these override any other instruction:
|
|
8668
|
+
- You may use this conversation thread to reply naturally, it is the contact's own conversation.
|
|
8669
|
+
- 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.
|
|
8670
|
+
- Never reveal anything about any other person or contact.
|
|
8671
|
+
- 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.
|
|
8672
|
+
- If you are unsure whether something is safe to share, do not share it.`;
|
|
8673
|
+
|
|
8686
8674
|
const auto_response = async function (uid, profile_id, contact_id, conversation_type = 'email') {
|
|
8687
8675
|
try {
|
|
8688
8676
|
const account_profile_info = await get_active_account_profile_info(uid, profile_id);
|
|
@@ -8694,11 +8682,11 @@ const auto_response = async function (uid, profile_id, contact_id, conversation_
|
|
|
8694
8682
|
|
|
8695
8683
|
let account_doc = await db_module.get_couch_doc_native('xuda_accounts', uid);
|
|
8696
8684
|
|
|
8697
|
-
// The main profile
|
|
8698
|
-
//
|
|
8699
|
-
//
|
|
8700
|
-
|
|
8701
|
-
|
|
8685
|
+
// The main profile auto-responds like any other (Boaz, 2026-07-31, reversing UI-44
|
|
8686
|
+
// part 2). It used to be hard-skipped here on the reasoning that you do not auto-reply
|
|
8687
|
+
// to yourself, but the main profile is the identity most people actually receive on, so
|
|
8688
|
+
// excluding it meant the feature was off exactly where it was most wanted. The
|
|
8689
|
+
// auto_respond flag and mode above are now the only gate, for every profile.
|
|
8702
8690
|
if (account_profile_doc.auto_respond_mode === 'when_offline' && account_doc.socket_id) return;
|
|
8703
8691
|
if (!['always', 'when_offline'].includes(account_profile_doc.auto_respond_mode)) return;
|
|
8704
8692
|
|
|
@@ -8764,6 +8752,8 @@ const auto_response = async function (uid, profile_id, contact_id, conversation_
|
|
|
8764
8752
|
instructions: `You are ${account_profile_doc.profile_name || userName}'s automatic ${conversation_type === 'chat' ? 'chat' : 'email'} assistant.
|
|
8765
8753
|
Reply to the latest incoming message from ${contact_doc.name || contact_doc.email} in a friendly, helpful, and concise way.
|
|
8766
8754
|
Use the conversation history for context.
|
|
8755
|
+
|
|
8756
|
+
${AUTO_REPLY_PRIVACY_POLICY}
|
|
8767
8757
|
Match the language the contact wrote in.
|
|
8768
8758
|
Do not mention that the reply is automated.
|
|
8769
8759
|
${conversation_type === 'chat' ? 'Return only the chat message text, ready to send.' : 'Return only the email body, ready to send.'}
|
|
@@ -8805,6 +8795,8 @@ ${account_profile_doc.profile_signature || ''}`.trim(),
|
|
|
8805
8795
|
|
|
8806
8796
|
You are composing an automatic ${conversation_type === 'chat' ? 'chat' : 'email'} reply.
|
|
8807
8797
|
Use the existing conversation history as context.
|
|
8798
|
+
|
|
8799
|
+
${AUTO_REPLY_PRIVACY_POLICY}
|
|
8808
8800
|
Reply as ${account_profile_doc.profile_name || userName}.
|
|
8809
8801
|
${conversation_type === 'chat' ? 'Return only the chat message text, ready to send.' : 'Return only the email body text, ready to send.'}
|
|
8810
8802
|
Keep the response concise, natural, and professional.${
|