@xuda.io/ai_module 1.1.5638 → 1.1.5640

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
@@ -413,7 +413,6 @@ const get_studio_table_info_tool = tool({
413
413
  async execute(e, RunContext) {
414
414
  const { table } = e;
415
415
  const { app_id, uid } = RunContext.context;
416
- debugger;
417
416
  if (!table) throw new Error('got null in the table parameter');
418
417
 
419
418
  try {
@@ -558,7 +557,6 @@ const fetch_data_tool = tool({
558
557
  }
559
558
 
560
559
  const validateQuery_result = validateQueryAgainstTable(query_obj, table_doc.tableFields);
561
- debugger;
562
560
  if (!validateQuery_result.isValid) {
563
561
  // console.error('Validation failed:', validateQuery_result.errors);
564
562
  throw new Error(`Validation failed: ${JSON.stringify(validateQuery_result.errors)}`);
@@ -795,7 +793,6 @@ const create_index_tool = tool({
795
793
  const { index } = e;
796
794
  const { app_id, uid } = RunContext.context;
797
795
  try {
798
- debugger;
799
796
  const index_obj = JSON5.parse(index);
800
797
  console.log('Create Index', index_obj);
801
798
  const project_db = await get_project_db(app_id);
@@ -3993,7 +3990,6 @@ export const update_ai_agent_properties = async function (doc, app_id, uid, fiel
3993
3990
  db_doc.agentConfig.agent_industry = await get_agent_industry();
3994
3991
  db_doc.studio_meta.agent_industry = db_doc.agentConfig.agent_industry;
3995
3992
  }
3996
- debugger;
3997
3993
  if (db_doc.agentConfig.agent_user_guide && (fields_changed.agent_user_guide_changed || fields_changed.all)) {
3998
3994
  db_doc.agentConfig.agent_user_guide_fields = await get_agent_user_guide_fields();
3999
3995
  db_doc.studio_meta.agent_user_guide_fields = db_doc.agentConfig.agent_user_guide_fields;
@@ -4122,7 +4118,6 @@ export const update_thumbnail = async function (type, doc, app_id, uid, job_id,
4122
4118
  }
4123
4119
  return save_ret;
4124
4120
  } catch (error) {
4125
- debugger;
4126
4121
  }
4127
4122
  // }, 500);
4128
4123
  };
@@ -4738,6 +4733,154 @@ export const diagnose_vps_snapshot = async function (req) {
4738
4733
  }
4739
4734
  };
4740
4735
 
4736
+ // Map a studio program's menuType to a friendly, user-facing noun so the
4737
+ // release-notes prompt talks about "screens" and "workflows" rather than the
4738
+ // internal type ids. Unknown types fall back to a de-underscored label.
4739
+ const _human_menu_type = function (t) {
4740
+ const map = {
4741
+ component: 'screen',
4742
+ workflow: 'workflow',
4743
+ table: 'data table',
4744
+ javascript: 'logic',
4745
+ js: 'logic',
4746
+ ai_agent: 'AI agent',
4747
+ api: 'API',
4748
+ style: 'style',
4749
+ function: 'function',
4750
+ query: 'query',
4751
+ report: 'report',
4752
+ };
4753
+ return map[t] || (t ? String(t).replace(/_/g, ' ') : 'item');
4754
+ };
4755
+
4756
+ // Generate user-facing release notes for a build the developer is about to
4757
+ // create. READ-ONLY: reads the project's PUBLISHED studio programs, diffs them
4758
+ // against the previous release's timestamp to see what changed since the last
4759
+ // build, and asks the model for a concise, plain-language changelog. Never
4760
+ // writes the release or mutates a program — the notes are returned for the
4761
+ // developer to review/edit before they hit Build. Credits are metered to the
4762
+ // caller via submit_chat_gpt_prompt -> record_ai_usage.
4763
+ // req: { app_id, version?, build_type?(major|minor|patch), instructions? }
4764
+ // -> { code:1, data:{ notes, changed_count, since_build, version } }
4765
+ // { code:-6, insufficient_credits:true } when the account is out of credits.
4766
+ export const generate_release_notes = async function (req) {
4767
+ const uid = req.uid || req.token_ret?.data?.uid;
4768
+ const app_id_in = req.app_id;
4769
+ const version = String(req.version || '').trim();
4770
+ const build_type = String(req.build_type || '').trim(); // major | minor | patch
4771
+ const steer = String(req.instructions || '')
4772
+ .trim()
4773
+ .slice(0, 400); // optional free-text hint from the developer
4774
+
4775
+ if (!uid) return { code: -1, data: 'not authorized' };
4776
+ if (!app_id_in) return { code: -1, data: 'app_id required' };
4777
+
4778
+ // Credit gate — usage is metered, but block up-front if the account is
4779
+ // already out (validate_credits_limit RETURNS the error, never throws).
4780
+ const credit_err = await validate_credits_limit(uid, null, _conf.release_notes?.model || _conf.default_ai_model, 'release notes');
4781
+ if (credit_err) return { code: -6, data: credit_err.message || 'insufficient credits', insufficient_credits: true };
4782
+
4783
+ // Resolve to the project (master) app — build/release data lives there.
4784
+ let app_obj;
4785
+ let app_ref;
4786
+ try {
4787
+ app_ref = await _common.get_project_app_id(app_id_in, true);
4788
+ const app_ret = await db_module.get_app_obj(app_ref);
4789
+ if (app_ret.code < 0 || !app_ret.data) return { code: -1, data: 'app not found' };
4790
+ app_obj = app_ret.data;
4791
+ } catch (e) {
4792
+ return { code: -1, data: 'app not found' };
4793
+ }
4794
+
4795
+ const app_name = app_obj.app_name || 'the app';
4796
+ const prev_version = app_obj.app_publish_info?.version || '';
4797
+ const last_build = app_obj.app_publish_info?.app_lastBuild || 0;
4798
+
4799
+ // Cutoff = the previous release's timestamp. Any program whose `ts`
4800
+ // (last-saved time) is newer than this changed since that build.
4801
+ let since_ts = 0;
4802
+ try {
4803
+ const rel = await db_module.find_app_couch_query(app_ref, {
4804
+ selector: { docType: 'release', stat: { $lt: 4 } },
4805
+ sort: [{ build_id: 'desc' }],
4806
+ limit: 1,
4807
+ });
4808
+ since_ts = rel.docs?.[0]?.date || 0;
4809
+ } catch (e) {
4810
+ since_ts = 0;
4811
+ }
4812
+
4813
+ // Published programs, projected to a tiny summary (bounds prompt tokens).
4814
+ let progs = [];
4815
+ try {
4816
+ const res = await db_module.find_app_couch_query(app_ref, {
4817
+ selector: { docType: 'studio', stat: 3 },
4818
+ fields: ['ts', 'properties.menuType', 'properties.menuName', 'properties.menuTitle'],
4819
+ limit: 99999,
4820
+ });
4821
+ progs = res.docs || [];
4822
+ } catch (e) {
4823
+ progs = [];
4824
+ }
4825
+
4826
+ const first_build = !since_ts || last_build === 0;
4827
+ const changed = progs.filter((p) => p && (first_build || (p.ts && p.ts > since_ts))).sort((a, b) => (b.ts || 0) - (a.ts || 0));
4828
+
4829
+ // Compact, deduped change list for the prompt (name + type; capped at 80).
4830
+ const seen = new Set();
4831
+ const items = [];
4832
+ for (const p of changed) {
4833
+ const name = p.properties?.menuTitle || p.properties?.menuName || 'Untitled';
4834
+ const type = _human_menu_type(p.properties?.menuType);
4835
+ const key = `${type}:${name}`;
4836
+ if (seen.has(key)) continue;
4837
+ seen.add(key);
4838
+ items.push({ name, type });
4839
+ if (items.length >= 80) break;
4840
+ }
4841
+
4842
+ const by_type = {};
4843
+ for (const it of items) by_type[it.type] = (by_type[it.type] || 0) + 1;
4844
+ const type_summary = Object.entries(by_type)
4845
+ .map(([t, n]) => `${n} ${t}${n > 1 ? 's' : ''}`)
4846
+ .join(', ');
4847
+
4848
+ const parts = [
4849
+ `You are writing end-user release notes for a no-code app called "${app_name}" built on the Xuda platform.`,
4850
+ `The developer is publishing a new build to every deployment of this app. Write short, friendly, user-facing release notes that tell the people who USE the app what is new — in plain language, not technical jargon.`,
4851
+ '',
4852
+ 'RULES:',
4853
+ '- Output ONLY 2 to 5 concise bullet points, each a single short line. No title, no preamble, no sign-off — bullets only.',
4854
+ '- Describe outcomes and improvements ("New School Info screen", "Faster class enrollment"), never internal file names or program types.',
4855
+ '- Group related changes into one bullet instead of listing every item.',
4856
+ '- Neutral, professional tone. Never invent features that the change list does not imply.',
4857
+ '- Write in the SAME language as the program names below (e.g. Hebrew names -> Hebrew notes).',
4858
+ steer ? `- The developer added this guidance — follow it: ${steer}` : '',
4859
+ '',
4860
+ `Version: ${prev_version ? prev_version + ' -> ' : ''}${version || '(new)'}${build_type ? ` (${build_type} release)` : ''}`,
4861
+ first_build ? 'This is the FIRST published build. Write a brief "initial release" style note highlighting the main areas of the app.' : `Programs changed since the last build (${type_summary || 'general updates'}):`,
4862
+ items.length ? items.map((it) => `- ${it.name} (${it.type})`).join('\n') : '(No specific program changes were detected — write a brief note about minor improvements and stability fixes.)',
4863
+ ].filter(Boolean);
4864
+
4865
+ // account_profile_info is REQUIRED for usage to be metered: record_ai_usage
4866
+ // throws (and skips broadcast_credits, so the nav meter never moves) without
4867
+ // it. Fetch it like classify_external_app_scan / the chat flows do.
4868
+ const account_profile_info = await get_active_account_profile_info(uid);
4869
+
4870
+ const ret = await submit_chat_gpt_prompt({
4871
+ uid,
4872
+ prompt: parts.join('\n'),
4873
+ model: _conf.release_notes?.model || _conf.default_ai_model,
4874
+ metadata: { func: 'generate_release_notes', app_id: app_ref, version },
4875
+ account_profile_info,
4876
+ });
4877
+ if (ret.code < 0) return { code: -1, data: ret.data };
4878
+
4879
+ const notes = String(ret.data || '').trim();
4880
+ if (!notes) return { code: -1, data: 'no notes generated' };
4881
+ return { code: 1, data: { notes, changed_count: changed.length, since_build: last_build, version } };
4882
+ };
4883
+
4741
4884
  function getFirstNWords(text, n = 10) {
4742
4885
  // Split on whitespace, keep punctuation attached to words
4743
4886
  const words = text.match(/\S+/g) || [];
@@ -4919,7 +5062,6 @@ export const create_conversation = async function (req, job_id, headers) {
4919
5062
 
4920
5063
  return save_ret;
4921
5064
  } catch (err) {
4922
- debugger;
4923
5065
 
4924
5066
  return { code: -5, data: err.message || String(err) };
4925
5067
  }
@@ -5176,7 +5318,6 @@ const update_conversation_mood_level = async function (uid, target_contacts = []
5176
5318
  }
5177
5319
  }
5178
5320
  } catch (err) {
5179
- debugger;
5180
5321
  }
5181
5322
  };
5182
5323
 
@@ -7144,7 +7285,6 @@ Hard restrictions:
7144
7285
  }
7145
7286
 
7146
7287
  try {
7147
- debugger;
7148
7288
  const api_allow_methods = Object.keys(_conf.cpi_methods);
7149
7289
  for (const method_name of api_allow_methods || []) {
7150
7290
  const method_prop = _conf.cpi_methods[method_name];
@@ -7199,7 +7339,6 @@ Hard restrictions:
7199
7339
  inputSchema[key] = get_z_item(key, val);
7200
7340
  }
7201
7341
  } catch (error) {
7202
- debugger;
7203
7342
  }
7204
7343
  }
7205
7344
  try {
@@ -7231,11 +7370,9 @@ Hard restrictions:
7231
7370
  }),
7232
7371
  );
7233
7372
  } catch (error) {
7234
- debugger;
7235
7373
  }
7236
7374
  }
7237
7375
  } catch (error) {
7238
- debugger;
7239
7376
  }
7240
7377
  break;
7241
7378
  }
@@ -7306,7 +7443,6 @@ Hard restrictions:
7306
7443
  numGenerations: z.number().int().describe('Number generations to image create.').default(3),
7307
7444
  }),
7308
7445
  async execute(e, RunContext) {
7309
- debugger;
7310
7446
  const { prompt, numGenerations } = e;
7311
7447
 
7312
7448
  try {
@@ -7690,7 +7826,6 @@ const ai_chat_conversation = async function (req, job_id, headers) {
7690
7826
  });
7691
7827
 
7692
7828
  agent.on('agent_tool_start', (context, tool, details) => {
7693
- debugger;
7694
7829
  // emitToDashboard('agent_tool_start', tool.name);
7695
7830
  emitToDashboard('stream_phase', `Starting ${tool?.name?.replaceAll('_', ' ')} tool`, { update: true });
7696
7831
  });
@@ -7849,13 +7984,11 @@ const ai_chat_conversation = async function (req, job_id, headers) {
7849
7984
  if (!prompt_suggestion_activated) {
7850
7985
  // init_agent_hooks(agent);
7851
7986
  agent.on('onToolStart', (context, tool, details) => {
7852
- debugger;
7853
7987
  // emitToDashboard('agent_tool_start', tool.name);
7854
7988
  // emitToDashboard('stream_phase', `Starting ${tool.name} tool`);
7855
7989
  });
7856
7990
 
7857
7991
  agent.on('onToolEnd', (context, tool, details) => {
7858
- debugger;
7859
7992
  // emitToDashboard('agent_tool_start', tool.name);
7860
7993
  // emitToDashboard('stream_phase', `Starting ${tool.name} tool`);
7861
7994
  });
@@ -8000,7 +8133,6 @@ const ai_chat_conversation = async function (req, job_id, headers) {
8000
8133
  emitToDashboard('stream_phase', 'Submitting chat', { update: true });
8001
8134
  init_agent_hooks(_agent);
8002
8135
  // const output = await runner.run(_agent, prompt, opt);
8003
- debugger;
8004
8136
  const output = await run_agent(_agent, prompt, opt);
8005
8137
  const done = async function (output) {
8006
8138
  try {
@@ -8048,7 +8180,6 @@ const ai_chat_conversation = async function (req, job_id, headers) {
8048
8180
  // }
8049
8181
  const save_ret = await db_module.save_app_couch_doc_native(account_profile_info.app_id, in_conversation_item_obj);
8050
8182
 
8051
- debugger;
8052
8183
 
8053
8184
  let conversation_items = await client.conversations.items.list(conversation_doc.reference_conversation_id, { order: 'asc', after: last_conversation_item });
8054
8185
 
@@ -8070,7 +8201,6 @@ const ai_chat_conversation = async function (req, job_id, headers) {
8070
8201
 
8071
8202
  return save_ret;
8072
8203
  } catch (error) {
8073
- debugger;
8074
8204
  }
8075
8205
  // }
8076
8206
 
@@ -8135,7 +8265,6 @@ const ai_chat_conversation = async function (req, job_id, headers) {
8135
8265
  };
8136
8266
  // }
8137
8267
  } catch (err) {
8138
- debugger;
8139
8268
 
8140
8269
  conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_id);
8141
8270
  conversation_doc.ts = Date.now();
@@ -8239,6 +8368,11 @@ const auto_response = async function (uid, profile_id, contact_id, conversation_
8239
8368
 
8240
8369
  let account_doc = await db_module.get_couch_doc_native('xuda_accounts', uid);
8241
8370
 
8371
+ // The main profile is the account's OWN identity: never auto-respond on it
8372
+ // (you don't auto-reply to yourself). Authoritative check via the account's
8373
+ // account_profile_id, independent of the stored auto_respond flag.
8374
+ if (account_doc?.account_profile_id && profile_id === account_doc.account_profile_id) return;
8375
+
8242
8376
  if (account_profile_doc.auto_respond_mode === 'when_offline' && account_doc.socket_id) return;
8243
8377
  if (!['always', 'when_offline'].includes(account_profile_doc.auto_respond_mode)) return;
8244
8378
 
package/index_ms.mjs CHANGED
@@ -133,6 +133,10 @@ export const diagnose_vps_snapshot = async function (...args) {
133
133
  return await broker.send_to_queue("diagnose_vps_snapshot", ...args);
134
134
  };
135
135
 
136
+ export const generate_release_notes = async function (...args) {
137
+ return await broker.send_to_queue("generate_release_notes", ...args);
138
+ };
139
+
136
140
  export const create_openai_conversation = async function (...args) {
137
141
  return await broker.send_to_queue("create_openai_conversation", ...args);
138
142
  };
package/index_msa.mjs CHANGED
@@ -133,6 +133,10 @@ export const diagnose_vps_snapshot = function (...args) {
133
133
  broker.send_to_queue_async("diagnose_vps_snapshot", ...args);
134
134
  };
135
135
 
136
+ export const generate_release_notes = function (...args) {
137
+ broker.send_to_queue_async("generate_release_notes", ...args);
138
+ };
139
+
136
140
  export const create_openai_conversation = function (...args) {
137
141
  broker.send_to_queue_async("create_openai_conversation", ...args);
138
142
  };
package/old.mjs CHANGED
@@ -580,7 +580,6 @@ async function testTool(app_id, uid) {
580
580
 
581
581
  console.log('Input:', JSON.stringify(input, null, 2));
582
582
  console.log('RunContext:', JSON.stringify(runContext, null, 2));
583
- debugger;
584
583
  // CORRECT ORDER: input first, then runContext
585
584
  const result = await get_studio_table_info_by_table_id_tool.invoke(
586
585
  runContext, // 1st: context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/ai_module",
3
- "version": "1.1.5638",
3
+ "version": "1.1.5640",
4
4
  "description": "Xuda AI Module",
5
5
  "main": "index.mjs",
6
6
  "type": "module",