@xuda.io/ai_module 1.1.5613 → 1.1.5614

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 CHANGED
@@ -9178,6 +9178,78 @@ export const is_spam_email = async function (uid, email, subject, account_profil
9178
9178
  } catch (error) {}
9179
9179
  };
9180
9180
 
9181
+ // AI screening for a marketplace publish. Receives plain-JSON context from
9182
+ // marketplace_module (zod schemas cannot cross the broker, so the prompt +
9183
+ // structured schema must live here). Returns a structured verdict across the
9184
+ // approval rule dimensions. The caller (marketplace_approval_review) merges
9185
+ // this with deterministic account-standing checks and decides the final stat.
9186
+ export const marketplace_review_item = async function (req) {
9187
+ const { uid, item_type, item = {}, account_context = {}, account_name = '', model = _conf.default_ai_model, account_profile_info } = req || {};
9188
+
9189
+ const check_schema = z.object({
9190
+ flag: z.boolean().describe('true if this concern is present'),
9191
+ severity: z.enum(['none', 'low', 'medium', 'high']).describe('how serious the concern is'),
9192
+ note: z.string().describe('one short sentence of evidence; empty string if no concern'),
9193
+ });
9194
+
9195
+ const response_format = z.object({
9196
+ decision: z.enum(['approve', 'reject', 'manual_review']).describe('approve = safe to publish; reject = clear violation; manual_review = uncertain, needs a human'),
9197
+ confidence: z.number().min(0).max(100).describe('confidence in the decision, 0-100'),
9198
+ checks: z.object({
9199
+ ai_bot_activity: check_schema.describe('automated/bot publishing: high velocity, near-duplicate items, low-effort generated bulk'),
9200
+ commercial_undisclosed: check_schema.describe('undisclosed commercial intent: external paid links, lead-gen, off-platform payment, advertising inconsistent with declared price/category'),
9201
+ harmful_code: check_schema.describe('destructive ops, data exfiltration, crypto-miners, obfuscation, malicious network calls in the submitted code'),
9202
+ suspicious_activity: check_schema.describe('mismatched metadata, hidden payloads, evasion patterns'),
9203
+ malformed_account_name: check_schema.describe('gibberish, homoglyphs, embedded URLs, or impersonation in the account name'),
9204
+ spam_low_quality: check_schema.describe('spam, filler, or low-quality content with no real utility'),
9205
+ impersonation_trademark: check_schema.describe('impersonates a brand/person or abuses a trademark'),
9206
+ prohibited_content: check_schema.describe('adult, illegal, hateful, or otherwise disallowed content'),
9207
+ plagiarism_duplicate: check_schema.describe('appears copied from or duplicates an existing item'),
9208
+ secrets_pii_leak: check_schema.describe('hardcoded API keys, tokens, credentials, or exposed PII in the code'),
9209
+ }),
9210
+ reasons: z.array(z.string()).describe('short user-facing lines explaining the decision (empty if approved cleanly)'),
9211
+ });
9212
+
9213
+ const prompt = `You are the xuda marketplace moderation reviewer. Decide whether the following ${item_type} should be published to the public marketplace.
9214
+
9215
+ Account name: ${account_name}
9216
+ Account context (signals): ${JSON.stringify(account_context)}
9217
+
9218
+ Item:
9219
+ - name: ${item.name || ''}
9220
+ - description: ${item.description || ''}
9221
+ - category: ${item.category || ''}
9222
+ - tags: ${JSON.stringify(item.tags || [])}
9223
+ - price: ${item.price ?? 0}
9224
+ - code/content summary: ${item.code_summary || '(none provided)'}
9225
+
9226
+ Evaluate every check honestly. Set decision to:
9227
+ - "approve" when no medium/high concerns and the item is genuine and useful.
9228
+ - "reject" when there is a clear high-severity violation (harmful code, leaked secrets, prohibited content, obvious impersonation/spam, or clear bot abuse).
9229
+ - "manual_review" when concerns are present but ambiguous, or you are not confident.
9230
+ Keep "reasons" concise, factual, and user-facing (no internal jargon).`;
9231
+
9232
+ const ret = await submit_chat_gpt_prompt({
9233
+ uid,
9234
+ prompt,
9235
+ model,
9236
+ response_format,
9237
+ metadata: { func: 'marketplace_review_item', item_type },
9238
+ account_profile_info,
9239
+ });
9240
+
9241
+ if (ret.code < 0) {
9242
+ return { code: ret.code, data: ret.data };
9243
+ }
9244
+
9245
+ try {
9246
+ const verdict = JSON.parse(ret.data);
9247
+ return { code: 1, data: { ...verdict, model } };
9248
+ } catch (err) {
9249
+ return { code: -1, data: `marketplace_review_item parse error: ${err.message}` };
9250
+ }
9251
+ };
9252
+
9181
9253
  export const is_business_contact = async function (uid, email, name, subject, body, account_profile_info) {
9182
9254
  let prompt = `detect if the email "${email}", subject "${subject}", name "${name}", or body "${body}" is business or personal. Return true if it is a business , false otherwise.
9183
9255
 
@@ -14256,7 +14328,7 @@ export const get_widget_embed_snippet = async function (req, job_id, headers) {
14256
14328
  };
14257
14329
 
14258
14330
  const _widget_signup_core = async function (req, job_id, headers, opts) {
14259
- const { profile_id, email, name, widget_origin, google_sub, signup_method } = req;
14331
+ const { profile_id, email, name, widget_origin, google_sub, signup_method, visitor_lang } = req;
14260
14332
  const [owner_uid_part, pid_part] = String(profile_id || '').split('.');
14261
14333
  if (!owner_uid_part || !pid_part) return { code: -404, data: 'invalid_profile' };
14262
14334
 
@@ -14268,19 +14340,19 @@ const _widget_signup_core = async function (req, job_id, headers, opts) {
14268
14340
  if (ap_doc.widget_enabled === false) return { code: -404, data: 'widget_disabled' };
14269
14341
 
14270
14342
  const visitor_uid = await _widget_find_or_create_visitor(email, name, google_sub);
14271
- return await _widget_signup_finalize(visitor_uid, email, name, widget_origin, signup_method, account_profile_info, canonical_owner_uid, canonical_profile_id, ap_doc, job_id, headers);
14343
+ return await _widget_signup_finalize(visitor_uid, email, name, widget_origin, signup_method, account_profile_info, canonical_owner_uid, canonical_profile_id, ap_doc, job_id, headers, visitor_lang);
14272
14344
  };
14273
14345
 
14274
14346
  export const widget_signup = async function (req, job_id, headers) {
14275
14347
  try {
14276
- let { profile_id, email, name, widget_origin } = req;
14348
+ let { profile_id, email, name, widget_origin, visitor_lang } = req;
14277
14349
  if (typeof email !== 'string' || typeof name !== 'string') return { code: -1, data: 'invalid_input' };
14278
14350
  email = email.toLowerCase().trim();
14279
14351
  name = name.trim();
14280
14352
  if (!email || !name) return { code: -1, data: 'invalid_input' };
14281
14353
  if (!WIDGET_EMAIL_RE.test(email)) return { code: -1, data: 'invalid_email' };
14282
14354
 
14283
- return await _widget_signup_core({ profile_id, email, name, widget_origin, signup_method: 'email' }, job_id, headers);
14355
+ return await _widget_signup_core({ profile_id, email, name, widget_origin, visitor_lang, signup_method: 'email' }, job_id, headers);
14284
14356
  } catch (err) {
14285
14357
  return { code: -1, data: err.message || String(err) };
14286
14358
  }
@@ -14288,7 +14360,7 @@ export const widget_signup = async function (req, job_id, headers) {
14288
14360
 
14289
14361
  export const widget_signup_google = async function (req, job_id, headers) {
14290
14362
  try {
14291
- const { profile_id, id_token, widget_origin } = req;
14363
+ const { profile_id, id_token, widget_origin, visitor_lang } = req;
14292
14364
  if (!id_token || typeof id_token !== 'string') return { code: -1, data: 'missing_id_token' };
14293
14365
 
14294
14366
  let payload;
@@ -14304,13 +14376,13 @@ export const widget_signup_google = async function (req, job_id, headers) {
14304
14376
  const name = String(payload.name || payload.email?.split('@')[0] || 'Friend').trim();
14305
14377
  if (!email) return { code: -1, data: 'no_email_in_token' };
14306
14378
 
14307
- return await _widget_signup_core({ profile_id, email, name, widget_origin, google_sub: payload.sub, signup_method: 'google' }, job_id, headers);
14379
+ return await _widget_signup_core({ profile_id, email, name, widget_origin, visitor_lang, google_sub: payload.sub, signup_method: 'google' }, job_id, headers);
14308
14380
  } catch (err) {
14309
14381
  return { code: -1, data: err.message || String(err) };
14310
14382
  }
14311
14383
  };
14312
14384
 
14313
- const _widget_signup_finalize = async function (visitor_uid, email, name, widget_origin, signup_method, account_profile_info, canonical_owner_uid, canonical_profile_id, ap_doc, job_id, headers) {
14385
+ const _widget_signup_finalize = async function (visitor_uid, email, name, widget_origin, signup_method, account_profile_info, canonical_owner_uid, canonical_profile_id, ap_doc, job_id, headers, visitor_lang) {
14314
14386
  try {
14315
14387
  let team_req_doc = await _widget_find_existing_team_req(visitor_uid, canonical_profile_id);
14316
14388
  let assigned_uid;
@@ -14374,12 +14446,20 @@ const _widget_signup_finalize = async function (visitor_uid, email, name, widget
14374
14446
 
14375
14447
  const widget_token = `wds_${crypto.randomUUID().replace(/-/g, '')}`;
14376
14448
  const now = Date.now();
14449
+ // Normalize visitor_lang to a short tag (e.g. "ja-JP" → "ja"). The
14450
+ // auto-reply agent reads this from the session to respond in the
14451
+ // visitor's language. Empty / missing → English fallback at reply time.
14452
+ const visitor_lang_base = String(visitor_lang || '')
14453
+ .toLowerCase()
14454
+ .split('-')[0]
14455
+ .slice(0, 8);
14377
14456
  const session_doc = {
14378
14457
  _id: widget_token,
14379
14458
  docType: 'widget_session',
14380
14459
  stat: 2,
14381
14460
  state,
14382
14461
  visitor_uid,
14462
+ visitor_lang: visitor_lang_base || '',
14383
14463
  team_req_id: team_req_doc._id,
14384
14464
  assigned_uid,
14385
14465
  owner_uid: canonical_owner_uid,
@@ -14398,6 +14478,12 @@ const _widget_signup_finalize = async function (visitor_uid, email, name, widget
14398
14478
  const assigned_avatar = assigned_info_ret?.data?.profile_picture || assigned_info_ret?.data?.profile_avatar || '';
14399
14479
  const greeting = ap_doc?.widget_config?.greeting || WIDGET_DEFAULT_GREETING;
14400
14480
 
14481
+ // Surface the visitor's first name so the chat widget can personalize the
14482
+ // greeting on the page ("Hi {visitor_first_name}, I'm Valentina..."). The
14483
+ // widget reads this from the persisted session and replaces the
14484
+ // {visitor_name} placeholder client-side.
14485
+ const visitor_first_name = _widget_split_name(name).first_name || '';
14486
+
14401
14487
  return {
14402
14488
  code: 1,
14403
14489
  data: {
@@ -14407,6 +14493,7 @@ const _widget_signup_finalize = async function (visitor_uid, email, name, widget
14407
14493
  assigned_avatar,
14408
14494
  greeting,
14409
14495
  conversation_id,
14496
+ visitor_first_name,
14410
14497
  signup_method: signup_method || 'email',
14411
14498
  account_active: signup_method === 'google',
14412
14499
  signin_url: signup_method === 'google' ? `https://${_conf.domain || 'xuda.ai'}/dashboard/login` : undefined,
package/index_ms.mjs CHANGED
@@ -260,3 +260,7 @@ export const widget_send_message = async function (...args) {
260
260
  export const widget_get_messages = async function (...args) {
261
261
  return await broker.send_to_queue("widget_get_messages", ...args);
262
262
  };
263
+
264
+ export const marketplace_review_item = async function (...args) {
265
+ return await broker.send_to_queue("marketplace_review_item", ...args);
266
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/ai_module",
3
- "version": "1.1.5613",
3
+ "version": "1.1.5614",
4
4
  "description": "Xuda AI Module",
5
5
  "main": "index.mjs",
6
6
  "type": "module",