chatlab-cli 0.24.1 → 0.25.0

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 (60) hide show
  1. package/dist/{chunk-N5DGZQB4.mjs → chunk-4OMV4VNH.mjs} +567 -97
  2. package/dist/chunk-4OMV4VNH.mjs.map +1 -0
  3. package/dist/{chunk-CIIQ3S2X.mjs → chunk-CIBTUWAE.mjs} +7 -7
  4. package/dist/{chunk-43XWYC3M.mjs → chunk-E65ABM3C.mjs} +168 -72
  5. package/dist/chunk-E65ABM3C.mjs.map +1 -0
  6. package/dist/{chunk-ZL322NVL.mjs → chunk-FSHVDWI2.mjs} +109 -44
  7. package/dist/chunk-FSHVDWI2.mjs.map +1 -0
  8. package/dist/{chunk-QY6P2YS7.mjs → chunk-GNT5G74Z.mjs} +2 -2
  9. package/dist/{chunk-QJI6AOAT.mjs → chunk-TCPXAX3F.mjs} +2 -2
  10. package/dist/cli.mjs +2 -2
  11. package/dist/{http-TPX74BLK.mjs → http-PMNZ42EN.mjs} +6 -6
  12. package/dist/{import-B745DRHD.mjs → import-OP7R4CYY.mjs} +4 -4
  13. package/dist/index.mjs +6 -6
  14. package/dist/{mcp-YWAVG5XW.mjs → mcp-ABXNFUJO.mjs} +3 -3
  15. package/dist/{stream-import-OPALPVWF.mjs → stream-import-L6RJHH4L.mjs} +3 -3
  16. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BUwpIerl.js +161 -0
  17. package/dist-web/assets/{EChartWordcloud-VpNJDurs.js → EChartWordcloud-CzhfrEF_.js} +1 -1
  18. package/dist-web/assets/{MessageExportModal-CSfaY25B.js → MessageExportModal-BAJKWX4y.js} +1 -1
  19. package/dist-web/assets/{electron-BUeXfvxT.js → electron-CZt36wW5.js} +1 -1
  20. package/dist-web/assets/electron-CwERrTWp.js +1 -0
  21. package/dist-web/assets/{fetch-DzNuFdFM.js → fetch-BxFAkm7O.js} +1 -1
  22. package/dist-web/assets/{fetch-BohUj3CQ.js → fetch-CgBIWyAo.js} +1 -1
  23. package/dist-web/assets/{fetch-B2q6973s.js → fetch-D4p9vj1A.js} +1 -1
  24. package/dist-web/assets/{fetch-Ck3tjoLa.js → fetch-DD1EGRKV.js} +1 -1
  25. package/dist-web/assets/{fetch-BtHwWs8A.js → fetch-DO8lty7_.js} +1 -1
  26. package/dist-web/assets/{fetch-BGMRmQx0.js → fetch-DXfZysDf.js} +1 -1
  27. package/dist-web/assets/{fetch-DypPduyQ.js → fetch-GMkMBzBS.js} +1 -1
  28. package/dist-web/assets/{fetch-RDFt3hmp.js → fetch-JzVEwamn.js} +1 -1
  29. package/dist-web/assets/{fetch-CARsBVl0.js → fetch-qoGEVjNg.js} +1 -1
  30. package/dist-web/assets/{fetch-BneHn5qk.js → fetch-yWwp067l.js} +1 -1
  31. package/dist-web/assets/{index-Bvlj3dMn.js → index-BrS_VrIY.js} +1 -1
  32. package/dist-web/assets/index-C1t-kzfo.css +1 -0
  33. package/dist-web/assets/{index-DCWvD-4L.js → index-Cvlw0RLu.js} +2 -2
  34. package/dist-web/assets/index-DABpUm3J.js +9 -0
  35. package/dist-web/assets/{index-C5LrQG3N.js → index-DvWI6Yqa.js} +4 -4
  36. package/dist-web/assets/{index-BvaWRM6_.js → index-O0PG26_v.js} +1 -1
  37. package/dist-web/assets/index-OsjX5hXw.js +173 -0
  38. package/dist-web/assets/vendor-echarts-CITQeskI.js +11 -0
  39. package/dist-web/assets/{vendor-echarts-wordcloud-CZ9gsX8l.js → vendor-echarts-wordcloud-B1kQMklB.js} +1 -1
  40. package/dist-web/assets/{vendor-nuxt-ui-BYU7dz8t.js → vendor-nuxt-ui-C_8CsH6e.js} +1 -1
  41. package/dist-web/assets/{vendor-reka-ui-CnB0mMNX.js → vendor-reka-ui-CIMXwvEp.js} +1 -1
  42. package/dist-web/assets/{web-CXPkmhis.js → web-4rtrAcLu.js} +1 -1
  43. package/dist-web/index.html +4 -4
  44. package/package.json +1 -1
  45. package/dist/chunk-43XWYC3M.mjs.map +0 -1
  46. package/dist/chunk-N5DGZQB4.mjs.map +0 -1
  47. package/dist/chunk-ZL322NVL.mjs.map +0 -1
  48. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BBDrsJfe.js +0 -161
  49. package/dist-web/assets/electron-BynNUuEA.js +0 -1
  50. package/dist-web/assets/index-BRwEubUe.js +0 -173
  51. package/dist-web/assets/index-DB-iqXzV.css +0 -1
  52. package/dist-web/assets/index-DS1rzk1I.js +0 -8
  53. package/dist-web/assets/vendor-echarts-C5u62NnM.js +0 -11
  54. /package/dist/{chunk-CIIQ3S2X.mjs.map → chunk-CIBTUWAE.mjs.map} +0 -0
  55. /package/dist/{chunk-QY6P2YS7.mjs.map → chunk-GNT5G74Z.mjs.map} +0 -0
  56. /package/dist/{chunk-QJI6AOAT.mjs.map → chunk-TCPXAX3F.mjs.map} +0 -0
  57. /package/dist/{http-TPX74BLK.mjs.map → http-PMNZ42EN.mjs.map} +0 -0
  58. /package/dist/{import-B745DRHD.mjs.map → import-OP7R4CYY.mjs.map} +0 -0
  59. /package/dist/{mcp-YWAVG5XW.mjs.map → mcp-ABXNFUJO.mjs.map} +0 -0
  60. /package/dist/{stream-import-OPALPVWF.mjs.map → stream-import-L6RJHH4L.mjs.map} +0 -0
@@ -4410,6 +4410,218 @@ function isNewerStableVersion(latest, current) {
4410
4410
  return currentVersion.prerelease;
4411
4411
  }
4412
4412
 
4413
+ // ../../packages/core/src/chart/index.ts
4414
+ var ChartValidationError = class extends Error {
4415
+ constructor(message) {
4416
+ super(message);
4417
+ this.name = "ChartValidationError";
4418
+ }
4419
+ };
4420
+ var CHART_TYPES = /* @__PURE__ */ new Set(["bar", "line", "pie", "heatmap"]);
4421
+ function isRecord(value) {
4422
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4423
+ }
4424
+ function asFieldName(value, label) {
4425
+ if (typeof value !== "string" || value.trim().length === 0) {
4426
+ throw new ChartValidationError(`${label} must be a non-empty field name`);
4427
+ }
4428
+ return value;
4429
+ }
4430
+ function assertFieldExists(rows, field) {
4431
+ if (rows.length === 0) return;
4432
+ if (!Object.prototype.hasOwnProperty.call(rows[0], field)) {
4433
+ throw new ChartValidationError(`Field "${field}" does not exist in SQL result`);
4434
+ }
4435
+ }
4436
+ function toLabel(value) {
4437
+ if (value === null || value === void 0 || value === "") return "(empty)";
4438
+ return String(value);
4439
+ }
4440
+ function toNumber(value, field) {
4441
+ const num = typeof value === "number" ? value : Number(value);
4442
+ if (!Number.isFinite(num)) {
4443
+ throw new ChartValidationError(`Field "${field}" contains non-numeric value "${String(value)}"`);
4444
+ }
4445
+ return num;
4446
+ }
4447
+ function inferFieldType(values) {
4448
+ const present = values.filter((v) => v !== null && v !== void 0);
4449
+ if (present.length === 0) return "string";
4450
+ if (present.every((v) => typeof v === "number" && Number.isInteger(v))) return "integer";
4451
+ if (present.every(
4452
+ (v) => typeof v === "number" || typeof v === "string" && v.trim() !== "" && !Number.isNaN(Number(v))
4453
+ )) {
4454
+ return "number";
4455
+ }
4456
+ if (present.every((v) => typeof v === "boolean")) return "boolean";
4457
+ return "category";
4458
+ }
4459
+ function normalizeChartSpec(raw) {
4460
+ if (!isRecord(raw)) throw new ChartValidationError("chartSpec must be an object");
4461
+ if (raw.version !== 1) throw new ChartValidationError("chartSpec.version must be 1");
4462
+ if (typeof raw.type !== "string" || !CHART_TYPES.has(raw.type)) {
4463
+ throw new ChartValidationError("chartSpec.type must be one of: bar, line, pie, heatmap");
4464
+ }
4465
+ if (typeof raw.title !== "string" || raw.title.trim().length === 0) {
4466
+ throw new ChartValidationError("chartSpec.title must be a non-empty string");
4467
+ }
4468
+ if (!isRecord(raw.encoding)) throw new ChartValidationError("chartSpec.encoding must be an object");
4469
+ const spec = raw;
4470
+ switch (spec.type) {
4471
+ case "bar":
4472
+ case "line":
4473
+ asFieldName(spec.encoding.x, "encoding.x");
4474
+ asFieldName(spec.encoding.y, "encoding.y");
4475
+ break;
4476
+ case "pie":
4477
+ asFieldName(spec.encoding.label, "encoding.label");
4478
+ asFieldName(spec.encoding.value, "encoding.value");
4479
+ break;
4480
+ case "heatmap":
4481
+ asFieldName(spec.encoding.x, "encoding.x");
4482
+ asFieldName(spec.encoding.y, "encoding.y");
4483
+ asFieldName(spec.encoding.value, "encoding.value");
4484
+ break;
4485
+ }
4486
+ return {
4487
+ ...spec,
4488
+ title: spec.title.trim()
4489
+ };
4490
+ }
4491
+ function inferChartDataset(rows, declaredFields) {
4492
+ const declared = new Map((declaredFields ?? []).map((field) => [field.name, field]));
4493
+ const columnNames = rows.length > 0 ? Object.keys(rows[0]) : [...declared.keys()];
4494
+ const columns = columnNames.map((name) => {
4495
+ const declaredField = declared.get(name);
4496
+ if (declaredField) return declaredField;
4497
+ return {
4498
+ name,
4499
+ type: inferFieldType(rows.map((row) => row[name]))
4500
+ };
4501
+ });
4502
+ return { columns, rows };
4503
+ }
4504
+ function aggregateByLabel(rows, labelField, valueField) {
4505
+ const labels = [];
4506
+ const indexByLabel = /* @__PURE__ */ new Map();
4507
+ const values = [];
4508
+ for (const row of rows) {
4509
+ const label = toLabel(row[labelField]);
4510
+ let index = indexByLabel.get(label);
4511
+ if (index === void 0) {
4512
+ index = labels.length;
4513
+ indexByLabel.set(label, index);
4514
+ labels.push(label);
4515
+ values.push(0);
4516
+ }
4517
+ values[index] += toNumber(row[valueField], valueField);
4518
+ }
4519
+ return { labels, values };
4520
+ }
4521
+ function buildLineData(rows, spec) {
4522
+ const xField = spec.encoding.x;
4523
+ const yField = spec.encoding.y;
4524
+ const seriesField = spec.encoding.series;
4525
+ if (!seriesField) return aggregateByLabel(rows, xField, yField);
4526
+ assertFieldExists(rows, seriesField);
4527
+ const labels = [];
4528
+ const labelIndex = /* @__PURE__ */ new Map();
4529
+ const seriesNames = [];
4530
+ const seriesIndex = /* @__PURE__ */ new Map();
4531
+ const matrix = [];
4532
+ for (const row of rows) {
4533
+ const label = toLabel(row[xField]);
4534
+ const seriesName = toLabel(row[seriesField]);
4535
+ let xIndex = labelIndex.get(label);
4536
+ if (xIndex === void 0) {
4537
+ xIndex = labels.length;
4538
+ labelIndex.set(label, xIndex);
4539
+ labels.push(label);
4540
+ for (const values of matrix) values.push(0);
4541
+ }
4542
+ let sIndex = seriesIndex.get(seriesName);
4543
+ if (sIndex === void 0) {
4544
+ sIndex = seriesNames.length;
4545
+ seriesIndex.set(seriesName, sIndex);
4546
+ seriesNames.push(seriesName);
4547
+ matrix.push(Array(labels.length).fill(0));
4548
+ }
4549
+ matrix[sIndex][xIndex] += toNumber(row[yField], yField);
4550
+ }
4551
+ const series = seriesNames.map((name, index) => ({ name, values: matrix[index] }));
4552
+ return {
4553
+ labels,
4554
+ values: series[0]?.values ?? [],
4555
+ series
4556
+ };
4557
+ }
4558
+ function buildHeatmapData(rows, spec) {
4559
+ const xField = spec.encoding.x;
4560
+ const yField = spec.encoding.y;
4561
+ const valueField = spec.encoding.value;
4562
+ const xLabels = [];
4563
+ const yLabels = [];
4564
+ const xIndex = /* @__PURE__ */ new Map();
4565
+ const yIndex = /* @__PURE__ */ new Map();
4566
+ const values = /* @__PURE__ */ new Map();
4567
+ for (const row of rows) {
4568
+ const xLabel = toLabel(row[xField]);
4569
+ const yLabel = toLabel(row[yField]);
4570
+ let xi = xIndex.get(xLabel);
4571
+ if (xi === void 0) {
4572
+ xi = xLabels.length;
4573
+ xIndex.set(xLabel, xi);
4574
+ xLabels.push(xLabel);
4575
+ }
4576
+ let yi = yIndex.get(yLabel);
4577
+ if (yi === void 0) {
4578
+ yi = yLabels.length;
4579
+ yIndex.set(yLabel, yi);
4580
+ yLabels.push(yLabel);
4581
+ }
4582
+ const key = `${xi}:${yi}`;
4583
+ values.set(key, (values.get(key) ?? 0) + toNumber(row[valueField], valueField));
4584
+ }
4585
+ const data = [];
4586
+ for (const [key, value] of values.entries()) {
4587
+ const [x, y] = key.split(":").map(Number);
4588
+ data.push([x, y, value]);
4589
+ }
4590
+ return { xLabels, yLabels, data };
4591
+ }
4592
+ function buildChartPayload(rows, rawSpec, options) {
4593
+ const spec = normalizeChartSpec(rawSpec);
4594
+ const dataset = inferChartDataset(rows, spec.fields);
4595
+ const requiredFields = /* @__PURE__ */ new Set();
4596
+ for (const field of Object.values(spec.encoding)) {
4597
+ if (typeof field === "string" && field) requiredFields.add(field);
4598
+ }
4599
+ for (const field of requiredFields) assertFieldExists(rows, field);
4600
+ let data;
4601
+ switch (spec.type) {
4602
+ case "bar":
4603
+ data = aggregateByLabel(rows, spec.encoding.x, spec.encoding.y);
4604
+ break;
4605
+ case "line":
4606
+ data = buildLineData(rows, spec);
4607
+ break;
4608
+ case "pie":
4609
+ data = aggregateByLabel(rows, spec.encoding.label, spec.encoding.value);
4610
+ break;
4611
+ case "heatmap":
4612
+ data = buildHeatmapData(rows, spec);
4613
+ break;
4614
+ }
4615
+ return {
4616
+ version: 1,
4617
+ spec,
4618
+ dataset,
4619
+ data,
4620
+ rowCount: rows.length,
4621
+ truncated: options?.truncated
4622
+ };
4623
+ }
4624
+
4413
4625
  // ../../packages/core/src/nlp/pos-tags.ts
4414
4626
  var POS_TAG_DEFINITIONS = [
4415
4627
  { tag: "n", name: "\u540D\u8BCD", description: "\u666E\u901A\u540D\u8BCD", meaningful: true },
@@ -5248,6 +5460,7 @@ var BUILTIN_TOOL_CATALOG = [
5248
5460
  { name: "search_sessions", category: "core" },
5249
5461
  { name: "get_session_messages", category: "core" },
5250
5462
  { name: "get_members", category: "core" },
5463
+ { name: "get_schema", category: "core" },
5251
5464
  // Analysis 工具
5252
5465
  { name: "get_member_stats", category: "analysis" },
5253
5466
  { name: "get_time_stats", category: "analysis" },
@@ -5256,6 +5469,8 @@ var BUILTIN_TOOL_CATALOG = [
5256
5469
  { name: "get_session_summaries", category: "analysis" },
5257
5470
  { name: "response_time_analysis", category: "analysis" },
5258
5471
  { name: "keyword_frequency", category: "analysis" },
5472
+ { name: "render_chart", category: "analysis" },
5473
+ { name: "execute_sql", category: "analysis" },
5259
5474
  // SQL Analysis 工具
5260
5475
  { name: "message_type_breakdown", category: "analysis" },
5261
5476
  { name: "peak_chat_hours_by_member", category: "analysis" },
@@ -5270,6 +5485,9 @@ var BUILTIN_TOOL_CATALOG = [
5270
5485
  { name: "unanswered_messages", category: "analysis" }
5271
5486
  ];
5272
5487
 
5488
+ // ../../packages/core/src/ai/chart-capability.ts
5489
+ var CHART_CAPABILITY_SKILL_ID = "chart_runtime";
5490
+
5273
5491
  // ../../packages/core/src/ai/provider-registry.ts
5274
5492
  var OPENAI = {
5275
5493
  id: "openai",
@@ -13394,93 +13612,43 @@ function countMessagesTokens(messages, systemPrompt) {
13394
13612
  // ../../packages/node-runtime/src/ai/skill-manager.ts
13395
13613
  import * as fs30 from "fs";
13396
13614
  import * as path27 from "path";
13397
- var SKILLS_DIR_NAME = "skills";
13615
+
13616
+ // ../../packages/node-runtime/src/ai/skill-menu.ts
13398
13617
  var MAX_SKILL_MENU_ITEMS = 15;
13399
- var defaultLogger2 = {
13400
- info: () => {
13401
- },
13402
- warn: () => {
13403
- }
13404
- };
13405
- var SkillManager = class {
13406
- skills = /* @__PURE__ */ new Map();
13407
- initialized = false;
13408
- skillsDir;
13409
- logger;
13410
- constructor(aiDataDir, logger) {
13411
- this.skillsDir = path27.join(aiDataDir, SKILLS_DIR_NAME);
13412
- this.logger = logger ?? defaultLogger2;
13413
- }
13414
- init() {
13415
- this.loadAll();
13416
- this.initialized = true;
13417
- this.logger.info(`SkillManager initialized: ${this.skills.size} skills`);
13418
- return { total: this.skills.size };
13419
- }
13420
- getSkillConfig(id) {
13421
- this.ensureInitialized();
13422
- return this.skills.get(id) ?? null;
13423
- }
13424
- getAllSkills() {
13425
- this.ensureInitialized();
13426
- return Array.from(this.skills.values()).map((def) => ({
13427
- id: def.id,
13428
- name: def.name,
13429
- description: def.description,
13430
- tags: def.tags,
13431
- chatScope: def.chatScope,
13432
- tools: def.tools,
13433
- builtinId: def.builtinId
13434
- }));
13435
- }
13436
- /**
13437
- * 构建 AI 自选技能菜单文本
13438
- * 只包含与当前 chatType + 助手工具权限兼容的技能
13439
- */
13440
- getSkillMenu(chatType, allowedTools) {
13441
- this.ensureInitialized();
13442
- const compatible = Array.from(this.skills.values()).filter((skill) => {
13443
- if (skill.chatScope !== "all" && skill.chatScope !== chatType) return false;
13444
- if (skill.tools.length > 0 && allowedTools && allowedTools.length > 0) {
13445
- const allCovered = skill.tools.every((t2) => allowedTools.includes(t2));
13446
- if (!allCovered) return false;
13447
- }
13448
- return true;
13449
- });
13450
- if (compatible.length === 0) return null;
13451
- const items = compatible.slice(0, MAX_SKILL_MENU_ITEMS);
13452
- const lines = items.map((s) => `- ${s.id}: ${s.name} \u2014 ${s.description}`);
13453
- return `## \u53EF\u7528\u6280\u80FD
13618
+ var SKILL_MENU_HEADER = `## \u53EF\u7528\u6280\u80FD
13454
13619
  \u4EE5\u4E0B\u662F\u4F60\u53EF\u4EE5\u4F7F\u7528\u7684\u5206\u6790\u6280\u80FD\u3002\u5F53\u4F60\u5224\u65AD\u7528\u6237\u7684\u95EE\u9898\u9002\u5408\u4F7F\u7528\u67D0\u4E2A\u6280\u80FD\u65F6\uFF0C
13455
- \u8BF7\u8C03\u7528 activate_skill \u5DE5\u5177\u6FC0\u6D3B\u5B83\uFF0C\u7136\u540E\u6309\u7167\u8FD4\u56DE\u7684\u6307\u5BFC\u5B8C\u6210\u4EFB\u52A1\u3002
13620
+ \u8BF7\u8C03\u7528 activate_skill \u5DE5\u5177\u6FC0\u6D3B\u5B83\uFF0C\u7136\u540E\u6309\u7167\u8FD4\u56DE\u7684\u6307\u5BFC\u5B8C\u6210\u4EFB\u52A1\u3002`;
13621
+ var SKILL_MENU_CLOSING = "\u5982\u679C\u7528\u6237\u7684\u95EE\u9898\u4E0D\u9700\u8981\u4F7F\u7528\u6280\u80FD\uFF0C\u76F4\u63A5\u56DE\u7B54\u5373\u53EF\u3002";
13622
+ var SKILL_MENU_CLOSING_BLOCK = `
13456
13623
 
13457
- ${lines.join("\n")}
13624
+ ${SKILL_MENU_CLOSING}`;
13625
+ function formatSkillMenuLine(item) {
13626
+ const guidanceSuffix = item.guidance ? `. ${item.guidance}` : "";
13627
+ return `- ${item.id}: ${item.name} \u2014 ${item.description}${guidanceSuffix}`;
13628
+ }
13629
+ function buildSkillMenuText(lines) {
13630
+ const visibleLines = normalizeSkillMenuLines(lines);
13631
+ if (visibleLines.length === 0) return null;
13632
+ return `${SKILL_MENU_HEADER}
13458
13633
 
13459
- \u5982\u679C\u7528\u6237\u7684\u95EE\u9898\u4E0D\u9700\u8981\u4F7F\u7528\u6280\u80FD\uFF0C\u76F4\u63A5\u56DE\u7B54\u5373\u53EF\u3002`;
13460
- }
13461
- ensureInitialized() {
13462
- if (!this.initialized) {
13463
- this.init();
13464
- }
13465
- }
13466
- loadAll() {
13467
- this.skills.clear();
13468
- if (!fs30.existsSync(this.skillsDir)) return;
13469
- const files = fs30.readdirSync(this.skillsDir).filter((f) => f.endsWith(".md"));
13470
- for (const file of files) {
13471
- try {
13472
- const filePath = path27.join(this.skillsDir, file);
13473
- const content = fs30.readFileSync(filePath, "utf-8");
13474
- const def = parseSkillFile(content, filePath);
13475
- if (def) {
13476
- this.skills.set(def.id, def);
13477
- }
13478
- } catch (error) {
13479
- this.logger.warn(`Failed to load skill: ${file}`, { error: String(error) });
13480
- }
13481
- }
13634
+ ${visibleLines.join("\n")}
13635
+
13636
+ ${SKILL_MENU_CLOSING}`;
13637
+ }
13638
+ function appendSkillMenuLines(baseMenu, lines) {
13639
+ const visibleLines = normalizeSkillMenuLines(lines);
13640
+ if (visibleLines.length === 0) return baseMenu ?? null;
13641
+ if (!baseMenu) return buildSkillMenuText(visibleLines);
13642
+ const insertion = `
13643
+ ${visibleLines.join("\n")}`;
13644
+ if (!baseMenu.includes(SKILL_MENU_CLOSING_BLOCK)) {
13645
+ return `${baseMenu}${insertion}`;
13482
13646
  }
13483
- };
13647
+ return baseMenu.replace(SKILL_MENU_CLOSING_BLOCK, `${insertion}${SKILL_MENU_CLOSING_BLOCK}`);
13648
+ }
13649
+ function normalizeSkillMenuLines(lines) {
13650
+ return lines.map((line) => line.trim()).filter(Boolean);
13651
+ }
13484
13652
 
13485
13653
  // ../../packages/node-runtime/src/ai/skill-manager-core.ts
13486
13654
  function toSummary2(def) {
@@ -13511,7 +13679,6 @@ function simpleHash(content) {
13511
13679
  }
13512
13680
  return hash.toString(36);
13513
13681
  }
13514
- var MAX_SKILL_MENU_ITEMS2 = 15;
13515
13682
  var SkillManagerCore = class {
13516
13683
  deps;
13517
13684
  builtinDefs = /* @__PURE__ */ new Map();
@@ -13665,21 +13832,14 @@ var SkillManagerCore = class {
13665
13832
  this.ensureInitialized();
13666
13833
  const compatible = Array.from(this.cache.values()).filter((skill) => {
13667
13834
  if (skill.chatScope !== "all" && skill.chatScope !== chatType) return false;
13668
- if (skill.tools.length > 0 && allowedTools && allowedTools.length > 0) {
13835
+ if (skill.tools.length > 0 && allowedTools !== void 0) {
13669
13836
  if (!skill.tools.every((t2) => allowedTools.includes(t2))) return false;
13670
13837
  }
13671
13838
  return true;
13672
13839
  });
13673
13840
  if (compatible.length === 0) return null;
13674
- const items = compatible.slice(0, MAX_SKILL_MENU_ITEMS2);
13675
- const lines = items.map((s) => `- ${s.id}: ${s.name} \u2014 ${s.description}`);
13676
- return `## \u53EF\u7528\u6280\u80FD
13677
- \u4EE5\u4E0B\u662F\u4F60\u53EF\u4EE5\u4F7F\u7528\u7684\u5206\u6790\u6280\u80FD\u3002\u5F53\u4F60\u5224\u65AD\u7528\u6237\u7684\u95EE\u9898\u9002\u5408\u4F7F\u7528\u67D0\u4E2A\u6280\u80FD\u65F6\uFF0C
13678
- \u8BF7\u8C03\u7528 activate_skill \u5DE5\u5177\u6FC0\u6D3B\u5B83\uFF0C\u7136\u540E\u6309\u7167\u8FD4\u56DE\u7684\u6307\u5BFC\u5B8C\u6210\u4EFB\u52A1\u3002
13679
-
13680
- ${lines.join("\n")}
13681
-
13682
- \u5982\u679C\u7528\u6237\u7684\u95EE\u9898\u4E0D\u9700\u8981\u4F7F\u7528\u6280\u80FD\uFF0C\u76F4\u63A5\u56DE\u7B54\u5373\u53EF\u3002`;
13841
+ const items = compatible.slice(0, MAX_SKILL_MENU_ITEMS);
13842
+ return buildSkillMenuText(items.map(formatSkillMenuLine));
13683
13843
  }
13684
13844
  // ==================== Internal ====================
13685
13845
  findByBuiltinId(builtinId) {
@@ -13709,6 +13869,80 @@ ${lines.join("\n")}
13709
13869
  }
13710
13870
  };
13711
13871
 
13872
+ // ../../packages/node-runtime/src/ai/skill-manager.ts
13873
+ var SKILLS_DIR_NAME = "skills";
13874
+ var defaultLogger2 = {
13875
+ info: () => {
13876
+ },
13877
+ warn: () => {
13878
+ }
13879
+ };
13880
+ var nodeFs = {
13881
+ ensureDir(dir) {
13882
+ fs30.mkdirSync(dir, { recursive: true });
13883
+ },
13884
+ listFiles(dir, ext) {
13885
+ if (!fs30.existsSync(dir)) return [];
13886
+ return fs30.readdirSync(dir).filter((file) => file.endsWith(ext));
13887
+ },
13888
+ readFile(filePath) {
13889
+ return fs30.readFileSync(filePath, "utf-8");
13890
+ },
13891
+ writeFile(filePath, content) {
13892
+ fs30.writeFileSync(filePath, content, "utf-8");
13893
+ },
13894
+ deleteFile(filePath) {
13895
+ fs30.unlinkSync(filePath);
13896
+ },
13897
+ fileExists(filePath) {
13898
+ return fs30.existsSync(filePath);
13899
+ },
13900
+ joinPath(...parts) {
13901
+ return path27.join(...parts);
13902
+ }
13903
+ };
13904
+ var SkillManager = class {
13905
+ core;
13906
+ logger;
13907
+ constructor(aiDataDir, logger) {
13908
+ this.logger = logger ?? defaultLogger2;
13909
+ this.core = new SkillManagerCore({
13910
+ fs: nodeFs,
13911
+ skillsDir: path27.join(aiDataDir, SKILLS_DIR_NAME),
13912
+ builtinRawSkills: [],
13913
+ logger: {
13914
+ info: () => {
13915
+ },
13916
+ warn: (_category, message, data) => this.logger.warn(message, toLoggerExtra(data)),
13917
+ error: (_category, message, data) => this.logger.warn(message, toLoggerExtra(data))
13918
+ }
13919
+ });
13920
+ }
13921
+ init() {
13922
+ const result = this.core.init();
13923
+ this.logger.info(`SkillManager initialized: ${result.total} skills`);
13924
+ return result;
13925
+ }
13926
+ getSkillConfig(id) {
13927
+ return this.core.getSkillConfig(id);
13928
+ }
13929
+ getAllSkills() {
13930
+ return this.core.getAllSkills();
13931
+ }
13932
+ /**
13933
+ * 构建 AI 自选技能菜单文本
13934
+ * 只包含与当前 chatType + 助手工具权限兼容的技能
13935
+ */
13936
+ getSkillMenu(chatType, allowedTools) {
13937
+ return this.core.getSkillMenu(chatType, allowedTools);
13938
+ }
13939
+ };
13940
+ function toLoggerExtra(data) {
13941
+ if (data === void 0) return void 0;
13942
+ if (data && typeof data === "object" && !Array.isArray(data)) return data;
13943
+ return { data };
13944
+ }
13945
+
13712
13946
  // ../../packages/node-runtime/src/ai/activate-skill-tool.ts
13713
13947
  function createActivateSkillTool(options) {
13714
13948
  const { chatType, allowedTools, coreToolNames, getSkillConfig, locale = "zh-CN" } = options;
@@ -13744,7 +13978,7 @@ function createActivateSkillTool(options) {
13744
13978
  details: { skillId, found: true, applicable: false }
13745
13979
  };
13746
13980
  }
13747
- if (skill.tools.length > 0 && allowedTools && allowedTools.length > 0) {
13981
+ if (skill.tools.length > 0 && allowedTools !== void 0) {
13748
13982
  const missing = skill.tools.filter((t2) => !(coreToolNames?.has(t2) ?? false) && !allowedTools.includes(t2));
13749
13983
  if (missing.length > 0) {
13750
13984
  const msg = isZh ? `\u5F53\u524D\u52A9\u624B\u7F3A\u5C11\u8BE5\u6280\u80FD\u6240\u9700\u7684\u5DE5\u5177\uFF1A${missing.join(", ")}` : `Current assistant lacks tools required by this skill: ${missing.join(", ")}`;
@@ -13763,6 +13997,175 @@ function createActivateSkillTool(options) {
13763
13997
  };
13764
13998
  }
13765
13999
 
14000
+ // ../../packages/node-runtime/src/ai/chart-runtime.ts
14001
+ var CHART_CAPABILITY_CORE_TOOLS = ["get_schema"];
14002
+ var CHART_CAPABILITY_ANALYSIS_TOOLS = ["render_chart"];
14003
+ var RAW_SQL_TOOL_NAMES = /* @__PURE__ */ new Set(["execute_sql"]);
14004
+ function getChartCapabilityAllowedBuiltinTools(allowedTools) {
14005
+ const baseTools = (allowedTools ?? []).filter((toolName) => !RAW_SQL_TOOL_NAMES.has(toolName));
14006
+ return Array.from(/* @__PURE__ */ new Set([...baseTools, ...CHART_CAPABILITY_ANALYSIS_TOOLS]));
14007
+ }
14008
+ function getAllowedBuiltinToolsForChartAutoSkill(allowedTools) {
14009
+ if (!allowedTools) return void 0;
14010
+ if (allowedTools.length === 0) return [];
14011
+ return Array.from(/* @__PURE__ */ new Set([...allowedTools, ...CHART_CAPABILITY_ANALYSIS_TOOLS]));
14012
+ }
14013
+ function shouldUseChartCapabilityForMessage(message) {
14014
+ const normalized = message.trim().toLowerCase();
14015
+ if (!normalized) return false;
14016
+ return /(?:画|绘制|生成|做|出|展示).{0,8}(?:图|图表)|(?:图表|可视化|饼图|柱状图|条形图|折线图|热力图|趋势图|占比图|分布图|chart|charts|plot|visuali[sz]e|visuali[sz]ation|pie chart|bar chart|line chart|heatmap)/i.test(
14017
+ normalized
14018
+ );
14019
+ }
14020
+ function resolveChartRuntimeForRequest(options) {
14021
+ const locale = options.locale ?? "zh-CN";
14022
+ const isExplicitChartSkill = options.skillId === CHART_CAPABILITY_SKILL_ID;
14023
+ const isAutoChartSkill = options.enableAutoDetection === true && !options.skillId && shouldUseChartCapabilityForMessage(options.userMessage ?? "");
14024
+ if (!isExplicitChartSkill && !isAutoChartSkill) {
14025
+ return { isChartCapability: false };
14026
+ }
14027
+ return {
14028
+ isChartCapability: true,
14029
+ skillDef: { ...getChartCapabilitySkill(locale), chatScope: "all" },
14030
+ allowedBuiltinTools: getChartCapabilityAllowedBuiltinTools(options.assistantAllowedTools)
14031
+ };
14032
+ }
14033
+ function getChartCapabilitySkill(locale = "zh-CN") {
14034
+ const isZh = locale.startsWith("zh");
14035
+ return {
14036
+ id: CHART_CAPABILITY_SKILL_ID,
14037
+ name: isZh ? "\u7ED8\u56FE\u52A9\u624B" : "Chart Assistant",
14038
+ description: isZh ? "\u6309\u672C\u8F6E\u95EE\u9898\u751F\u6210\u7075\u6D3B\u7684\u804A\u5929\u6570\u636E\u56FE\u8868" : "Generate flexible charts for this chat question",
14039
+ tags: [isZh ? "\u56FE\u8868" : "chart"],
14040
+ chatScope: "all",
14041
+ tools: ["render_chart", "get_schema"],
14042
+ prompt: isZh ? ZH_PROMPT : EN_PROMPT,
14043
+ builtinId: CHART_CAPABILITY_SKILL_ID
14044
+ };
14045
+ }
14046
+ function getChartMenuLine(locale) {
14047
+ const skill = getChartCapabilitySkill(locale);
14048
+ const isZh = locale.startsWith("zh");
14049
+ const guidance = isZh ? "\u7528\u6237\u660E\u786E\u8981\u6C42\u56FE\u8868\u3001\u753B\u56FE\u3001\u5360\u6BD4\u3001\u8D8B\u52BF\u3001\u5206\u5E03\u3001\u997C\u56FE\u3001\u67F1\u72B6\u56FE\u3001\u6298\u7EBF\u56FE\u6216\u70ED\u529B\u56FE\u65F6\u4F18\u5148\u6FC0\u6D3B\uFF1B\u4E0D\u8981\u8F93\u51FA Python/JS \u7ED8\u56FE\u4EE3\u7801" : "Activate first when the user explicitly asks for charts, visualization, ratios, trends, distributions, pie, bar, line, or heatmap charts; do not output Python/JS chart code";
14050
+ return formatSkillMenuLine({
14051
+ id: skill.id,
14052
+ name: skill.name,
14053
+ description: skill.description,
14054
+ guidance
14055
+ });
14056
+ }
14057
+ function buildSkillMenuWithBuiltinChart(baseMenu, locale = "zh-CN", allowedTools) {
14058
+ if (allowedTools && !allowedTools.includes(CHART_CAPABILITY_ANALYSIS_TOOLS[0])) return baseMenu ?? null;
14059
+ if (baseMenu?.includes(CHART_CAPABILITY_SKILL_ID)) return baseMenu;
14060
+ const chartLine = getChartMenuLine(locale);
14061
+ return appendSkillMenuLines(baseMenu, [chartLine]) ?? "";
14062
+ }
14063
+ function getSkillConfigWithBuiltinChart(id, locale = "zh-CN", getSkillConfig) {
14064
+ if (id === CHART_CAPABILITY_SKILL_ID) return getChartCapabilitySkill(locale);
14065
+ return getSkillConfig(id);
14066
+ }
14067
+ var ZH_PROMPT = `\u4F60\u662F ChatLab \u7ED8\u56FE\u52A9\u624B\u3002\u672C\u8F6E\u7528\u6237\u5E0C\u671B\u4F60\u5728\u56DE\u7B54\u91CC\u81EA\u7136\u5D4C\u5165\u4E00\u5F20\u6216\u591A\u5F20\u56FE\u8868\u3002
14068
+
14069
+ \u4F60\u5FC5\u987B\u901A\u8FC7 render_chart \u5DE5\u5177\u751F\u6210\u56FE\u8868\u3002\u4E0D\u8981\u8F93\u51FA HTML\u3001JavaScript\u3001SVG\u3001Canvas\u3001ECharts option \u6216\u4EFB\u4F55\u6E32\u67D3\u4EE3\u7801\u3002
14070
+
14071
+ \u786C\u6027\u89C4\u5219\uFF1A
14072
+ 1. \u7B2C\u4E00\u6B21\u5199 SQL \u524D\u5FC5\u987B\u5148\u8C03\u7528 get_schema\u3002\u7981\u6B62\u731C\u6D4B\u8868\u540D\u3001\u5B57\u6BB5\u540D\u6216\u65F6\u95F4\u5B57\u6BB5\u3002
14073
+ 2. \u201C\u6700\u8FD1\u201D\u201C\u5230\u73B0\u5728\u201D\u201C\u5230\u6700\u8FD1\u201D\u5FC5\u987B\u7528\u6570\u636E\u4E2D\u7684 MAX(ts) \u6216\u7B49\u4EF7\u5B57\u6BB5\u786E\u5B9A\u7ED3\u675F\u65E5\u671F\uFF1B\u4E0D\u8981\u624B\u7B97\u6216\u81C6\u6D4B\u65E5\u671F\u3002
14074
+ 3. ChatLab \u7684 message.ts \u662F\u79D2\u7EA7 Unix \u65F6\u95F4\u6233\uFF0C\u4F7F\u7528 date(ts, 'unixepoch', 'localtime')\uFF1B\u7981\u6B62\u5199 ts/1000\u3002
14075
+ 4. \u201C\u6700\u8FD1\u7684 N \u4EBA/\u6210\u5458\u201D\u5FC5\u987B\u5148\u7528\u6570\u636E\u5B9A\u4E49\u5E76\u7B5B\u9009\u51FA N \u4E2A\u6210\u5458\uFF0C\u518D\u53EA\u7EDF\u8BA1\u8FD9\u4E9B\u6210\u5458\u3002
14076
+ 5. \u6298\u7EBF\u56FE\u6709\u591A\u6761\u7EBF\u65F6\uFF0C\u6BCF\u6761\u7EBF\u5FC5\u987B\u6709\u660E\u786E series \u5B57\u6BB5\u542B\u4E49\uFF1B\u6309\u65E5\u671F\u7EDF\u8BA1\u65F6\u5E94\u8865\u9F50\u65E5\u671F \xD7 series \u7684 0 \u503C\uFF0C\u907F\u514D\u7F3A\u5931\u65E5\u671F\u5BFC\u81F4\u6298\u7EBF\u65AD\u88C2\u6216\u8BEF\u8FDE\u3002
14077
+ 6. \u5DE5\u5177\u5931\u8D25\u3001SQL \u4FEE\u6B63\u3001schema \u63A2\u7D22\u8FC7\u7A0B\u4E0D\u8981\u5199\u8FDB\u6700\u7EC8\u56DE\u7B54\uFF1B\u6700\u7EC8\u53EA\u4FDD\u7559\u56FE\u8868\u548C\u7B80\u77ED\u7ED3\u8BBA\u3002
14078
+ 7. \u56FE\u8868\u89E3\u91CA\u53EA\u80FD\u63CF\u8FF0\u6570\u636E\u76F4\u63A5\u652F\u6301\u7684\u8D8B\u52BF\u3001\u5CF0\u503C\u3001\u4F4E\u8C37\u3001\u5DEE\u5F02\u548C\u540C\u6B65\u6027\uFF0C\u7981\u6B62\u5938\u5F20\u62DF\u4EBA\u5316\u6216\u65E0\u6CD5\u4ECE\u56FE\u4E2D\u9A8C\u8BC1\u7684\u5173\u7CFB\u5224\u65AD\u3002
14079
+
14080
+ \u5DE5\u4F5C\u6D41\u7A0B\uFF1A
14081
+ 1. \u8C03\u7528 get_schema\u3002
14082
+ 2. \u6839\u636E schema \u548C\u7528\u6237\u8981\u6C42\u786E\u5B9A\u7EDF\u8BA1\u5BF9\u8C61\u3001\u65F6\u95F4\u8303\u56F4\u3001\u7EF4\u5EA6\u3001\u6307\u6807\u548C\u56FE\u8868\u7C7B\u578B\u3002
14083
+ 3. \u5FC5\u8981\u65F6\u7528\u53EA\u8BFB SQL \u5728 render_chart \u4E2D\u5B8C\u6210\u6210\u5458\u7B5B\u9009\u3001\u65F6\u95F4\u8303\u56F4\u786E\u8BA4\u3001\u805A\u5408\u548C\u8865\u96F6\u3002
14084
+ 4. \u8C03\u7528 render_chart\uFF0C\u63D0\u4EA4\u53EA\u8BFB SQL \u548C ChartSpec v1\u3002
14085
+ 5. \u56FE\u8868\u751F\u6210\u540E\uFF0C\u7528 1-3 \u53E5\u7B80\u6D01\u6587\u5B57\u89E3\u91CA\u7ED3\u8BBA\u3002\u7528\u6237\u8981\u6C42\u591A\u5F20\u56FE\u65F6\uFF0C\u53EF\u4EE5\u591A\u6B21\u8C03\u7528 render_chart\u3002
14086
+
14087
+ \u5E38\u7528\u8BED\u3001\u53E3\u5934\u7985\u3001\u9AD8\u9891\u77ED\u53E5\u7C7B\u56FE\u8868\u5FC5\u987B\u6392\u9664\u975E\u771F\u4EBA\u6587\u672C\uFF1A\u53D1\u9001\u8005\u4E3A\u7CFB\u7EDF\u6D88\u606F\u3001\u5A92\u4F53\u5360\u4F4D\uFF08\u5982 [\u8868\u60C5\u5305]\u3001[\u56FE\u7247]\u3001[\u8BED\u97F3]\u3001[\u89C6\u9891]\u3001[\u6587\u4EF6]\u3001[\u94FE\u63A5]\u3001[\u7EA2\u5305]\u3001[\u8F6C\u8D26]\uFF09\u4EE5\u53CA\u64A4\u56DE/\u5220\u9664\u63D0\u793A\u90FD\u4E0D\u80FD\u8FDB\u5165\u7EDF\u8BA1\u3002
14088
+
14089
+ ChartSpec v1 \u652F\u6301\uFF1A
14090
+ - bar: encoding.x + encoding.y
14091
+ - line: encoding.x + encoding.y\uFF0C\u53EF\u9009 encoding.series \u8868\u793A\u6BCF\u6761\u7EBF\u7684\u542B\u4E49
14092
+ - pie: encoding.label + encoding.value
14093
+ - heatmap: encoding.x + encoding.y + encoding.value
14094
+
14095
+ ChartSpec \u793A\u4F8B\uFF1A
14096
+ {
14097
+ "version": 1,
14098
+ "type": "line",
14099
+ "title": "\u6700\u8FD1 30 \u5929\u6210\u5458\u53D1\u8A00\u8D8B\u52BF",
14100
+ "encoding": { "x": "day", "y": "msg_count", "series": "member_name" },
14101
+ "unit": "\u6761"
14102
+ }
14103
+
14104
+ \u7528\u6237\u660E\u786E\u8981\u6C42\u56FE\u8868\u65F6\u5FC5\u987B\u5C1D\u8BD5\u751F\u6210\u3002\u7528\u6237\u6CA1\u6709\u660E\u786E\u8981\u6C42\u65F6\uFF0C\u53EA\u6709\u6392\u540D\u3001\u8D8B\u52BF\u3001\u5206\u5E03\u3001\u5360\u6BD4\u6216\u4E8C\u7EF4\u5BC6\u5EA6\u660E\u663E\u66F4\u6E05\u695A\u65F6\u624D\u4E3B\u52A8\u751F\u6210\uFF0C\u4E14\u9ED8\u8BA4\u6700\u591A\u4E00\u5F20\u3002`;
14105
+ var EN_PROMPT = `You are the ChatLab Chart Assistant. In this turn, the user wants one or more charts embedded naturally in the answer.
14106
+
14107
+ You must generate charts through the render_chart tool. Do not output HTML, JavaScript, SVG, Canvas, ECharts options, or rendering code.
14108
+
14109
+ Hard rules:
14110
+ 1. Always call get_schema before writing the first SQL query. Do not guess table names, field names, or timestamp fields.
14111
+ 2. For "latest", "until now", or "through the latest date", derive the end date from MAX(ts) or the equivalent timestamp field in the data. Do not hand-calculate or assume dates.
14112
+ 3. ChatLab message.ts is a Unix timestamp in seconds. Use date(ts, 'unixepoch', 'localtime'); never write ts/1000.
14113
+ 4. For "latest N members" or "recent N people", first define and select those N members from the data, then count only those members.
14114
+ 5. For multi-series line charts, every line must have an explicit series field. For daily counts, fill the date x series grid with zero values so missing days do not break or mislead the line.
14115
+ 6. Do not include failed SQL attempts, schema exploration, or retry reasoning in the final answer. Final answer should contain only the chart and a short conclusion.
14116
+ 7. Explain only trends, peaks, lows, differences, and synchrony directly supported by chart data. Do not make exaggerated personality or relationship claims.
14117
+
14118
+ Workflow:
14119
+ 1. Call get_schema.
14120
+ 2. Derive the target members, time range, dimensions, metrics, and chart type from schema and the user request.
14121
+ 3. When needed, use read-only SQL inside render_chart to select members, determine time range, aggregate data, and fill zero rows.
14122
+ 4. Call render_chart with read-only SQL and ChartSpec v1.
14123
+ 5. After the chart is generated, explain the key finding in 1-3 concise sentences. If the user asks for multiple charts, call render_chart multiple times.
14124
+
14125
+ For catchphrase, common phrase, or frequent short-text charts, exclude non-human text from the SQL: system-message senders, media placeholders such as [Sticker], [Image], [Photo], [Voice], [Video], [File], [Link], and recall/deletion notices must not be counted.
14126
+
14127
+ ChartSpec v1 supports:
14128
+ - bar: encoding.x + encoding.y
14129
+ - line: encoding.x + encoding.y, optional encoding.series for the meaning of each line
14130
+ - pie: encoding.label + encoding.value
14131
+ - heatmap: encoding.x + encoding.y + encoding.value
14132
+
14133
+ ChartSpec example:
14134
+ {
14135
+ "version": 1,
14136
+ "type": "line",
14137
+ "title": "Member message trend in the last 30 days",
14138
+ "encoding": { "x": "day", "y": "msg_count", "series": "member_name" },
14139
+ "unit": "messages"
14140
+ }
14141
+
14142
+ When the user explicitly asks for charts, you must try to generate them. If the user does not explicitly ask, add at most one chart only when ranking, trend, distribution, ratio, or two-dimensional density is clearly easier to understand visually.`;
14143
+
14144
+ // ../../packages/node-runtime/src/ai/chart-schema-gate.ts
14145
+ var CHART_SCHEMA_REQUIRED_MESSAGE = "Error: Call get_schema before render_chart. Do not guess table names, fields, or timestamp units.";
14146
+ function createChartSchemaGateState() {
14147
+ return { schemaSeen: false };
14148
+ }
14149
+ function wrapWithChartSchemaGate(tool, state) {
14150
+ const originalExecute = tool.execute;
14151
+ return {
14152
+ ...tool,
14153
+ execute: async (toolCallId, params, signal, onUpdate) => {
14154
+ if (tool.name === "render_chart" && !state.schemaSeen) {
14155
+ return {
14156
+ content: [{ type: "text", text: CHART_SCHEMA_REQUIRED_MESSAGE }],
14157
+ details: null
14158
+ };
14159
+ }
14160
+ const result = await originalExecute(toolCallId, params, signal, onUpdate);
14161
+ if (tool.name === "get_schema") {
14162
+ state.schemaSeen = true;
14163
+ }
14164
+ return result;
14165
+ }
14166
+ };
14167
+ }
14168
+
13766
14169
  // ../../packages/node-runtime/src/ai/compression/compressor.ts
13767
14170
  var DEFAULT_CONTEXT_WINDOW = 128e3;
13768
14171
  var INITIAL_COMPRESSION_PROMPT = `You are a context compression assistant. Compress the conversation below into a structured summary.
@@ -14542,6 +14945,11 @@ import {
14542
14945
  streamSimple as defaultStreamSimple,
14543
14946
  clampThinkingLevel
14544
14947
  } from "@earendil-works/pi-ai";
14948
+
14949
+ // ../../packages/node-runtime/src/ai/agent/constants.ts
14950
+ var DEFAULT_MAX_TOOL_ROUNDS = 10;
14951
+
14952
+ // ../../packages/node-runtime/src/ai/agent/core.ts
14545
14953
  function createEmptyPiUsage() {
14546
14954
  return {
14547
14955
  input: 0,
@@ -14584,7 +14992,7 @@ async function runAgentCore(options) {
14584
14992
  tools,
14585
14993
  history,
14586
14994
  userMessage,
14587
- maxToolRounds = 5,
14995
+ maxToolRounds = DEFAULT_MAX_TOOL_ROUNDS,
14588
14996
  abortSignal,
14589
14997
  steerMessage = "Please provide your final answer based on the information gathered.",
14590
14998
  onEvent,
@@ -15222,6 +15630,19 @@ var zh_CN_default = {
15222
15630
  days: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E",
15223
15631
  top_n: "\u8FD4\u56DE\u524D\u591A\u5C11\u4E2A\u5173\u952E\u8BCD"
15224
15632
  }
15633
+ },
15634
+ get_schema: {
15635
+ desc: "\u67E5\u770B\u804A\u5929\u6570\u636E\u5E93\u8868\u7ED3\u6784\u3002\u7ED8\u56FE\u6216\u81EA\u5B9A\u4E49 SQL \u524D\u5E94\u5148\u7528\u5B83\u786E\u8BA4\u8868\u540D\u548C\u5B57\u6BB5\u540D\u3002",
15636
+ params: {}
15637
+ },
15638
+ render_chart: {
15639
+ desc: "\u6839\u636E\u53EA\u8BFB SQL \u548C ChartSpec v1 \u751F\u6210 ChatLab \u539F\u751F\u56FE\u8868\u3002\u652F\u6301 bar\u3001line\u3001pie\u3001heatmap\u3002\u7981\u6B62\u8F93\u51FA HTML\u3001JavaScript\u3001SVG\u3001ECharts option \u6216\u6E32\u67D3\u4EE3\u7801\u3002",
15640
+ params: {
15641
+ sql: "\u53EA\u8BFB SELECT \u6216 WITH SELECT SQL\u3002\u5FC5\u987B\u8FD4\u56DE ChartSpec encoding \u4E2D\u5F15\u7528\u7684\u5B57\u6BB5\u3002",
15642
+ params: "SQL \u547D\u540D\u53C2\u6570\u5BF9\u8C61\uFF1B\u4E0D\u9700\u8981\u53C2\u6570\u65F6\u4F20\u7A7A\u5BF9\u8C61\u3002",
15643
+ chartSpec: "ChartSpec v1\uFF0C\u5305\u542B version\u3001type\u3001title\u3001encoding\u3002",
15644
+ maxRows: "\u56FE\u8868\u67E5\u8BE2\u6700\u5927\u884C\u6570\uFF0C\u9ED8\u8BA4 1000\u3002"
15645
+ }
15225
15646
  }
15226
15647
  },
15227
15648
  agent: {
@@ -15544,6 +15965,19 @@ Returned summaries are brief descriptions of each session, helping quickly locat
15544
15965
  days: "Number of recent days to analyze",
15545
15966
  top_n: "Number of top keywords to return"
15546
15967
  }
15968
+ },
15969
+ get_schema: {
15970
+ desc: "Inspect the chat database schema. Use it before charting or custom SQL when table or field names are uncertain.",
15971
+ params: {}
15972
+ },
15973
+ render_chart: {
15974
+ desc: "Generate a native ChatLab chart from read-only SQL and ChartSpec v1. Supports bar, line, pie, and heatmap. Do not output HTML, JavaScript, SVG, ECharts options, or rendering code.",
15975
+ params: {
15976
+ sql: "Read-only SELECT or WITH SELECT SQL. It must return fields referenced by ChartSpec encoding.",
15977
+ params: "Named SQL parameters. Use an empty object when no parameters are needed.",
15978
+ chartSpec: "ChartSpec v1 with version, type, title, and encoding.",
15979
+ maxRows: "Maximum chart query rows. Default 1000."
15980
+ }
15547
15981
  }
15548
15982
  },
15549
15983
  // ===== AI Agent system prompts =====
@@ -15862,6 +16296,19 @@ var ja_JP_default = {
15862
16296
  days: "\u76F4\u8FD1\u4F55\u65E5\u9593\u306E\u30C7\u30FC\u30BF\u3092\u96C6\u8A08\u3059\u308B\u304B",
15863
16297
  top_n: "\u4E0A\u4F4D\u4F55\u500B\u306E\u30AD\u30FC\u30EF\u30FC\u30C9\u3092\u8FD4\u5374\u3059\u308B\u304B"
15864
16298
  }
16299
+ },
16300
+ get_schema: {
16301
+ desc: "\u30C1\u30E3\u30C3\u30C8\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u30B9\u30AD\u30FC\u30DE\u3092\u78BA\u8A8D\u3057\u307E\u3059\u3002\u30C1\u30E3\u30FC\u30C8\u4F5C\u6210\u3084\u30AB\u30B9\u30BF\u30E0 SQL \u306E\u524D\u306B\u30C6\u30FC\u30D6\u30EB\u540D\u3068\u30D5\u30A3\u30FC\u30EB\u30C9\u540D\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
16302
+ params: {}
16303
+ },
16304
+ render_chart: {
16305
+ desc: "\u8AAD\u307F\u53D6\u308A\u5C02\u7528 SQL \u3068 ChartSpec v1 \u304B\u3089 ChatLab \u30CD\u30A4\u30C6\u30A3\u30D6\u30C1\u30E3\u30FC\u30C8\u3092\u751F\u6210\u3057\u307E\u3059\u3002bar\u3001line\u3001pie\u3001heatmap \u3092\u30B5\u30DD\u30FC\u30C8\u3057\u307E\u3059\u3002HTML\u3001JavaScript\u3001SVG\u3001ECharts option\u3001\u63CF\u753B\u30B3\u30FC\u30C9\u306F\u51FA\u529B\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002",
16306
+ params: {
16307
+ sql: "\u8AAD\u307F\u53D6\u308A\u5C02\u7528\u306E SELECT \u307E\u305F\u306F WITH SELECT SQL\u3002ChartSpec encoding \u3067\u53C2\u7167\u3059\u308B\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002",
16308
+ params: "SQL \u306E\u540D\u524D\u4ED8\u304D\u30D1\u30E9\u30E1\u30FC\u30BF\u3002\u4E0D\u8981\u306A\u5834\u5408\u306F\u7A7A\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u6E21\u3057\u307E\u3059\u3002",
16309
+ chartSpec: "version\u3001type\u3001title\u3001encoding \u3092\u542B\u3080 ChartSpec v1\u3002",
16310
+ maxRows: "\u30C1\u30E3\u30FC\u30C8\u30AF\u30A8\u30EA\u306E\u6700\u5927\u884C\u6570\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F 1000\u3002"
16311
+ }
15865
16312
  }
15866
16313
  },
15867
16314
  agent: {
@@ -16181,6 +16628,19 @@ var zh_TW_default = {
16181
16628
  days: "\u7D71\u8A08\u6700\u8FD1\u591A\u5C11\u5929\u7684\u8CC7\u6599",
16182
16629
  top_n: "\u56DE\u50B3\u524D\u591A\u5C11\u500B\u95DC\u9375\u8A5E"
16183
16630
  }
16631
+ },
16632
+ get_schema: {
16633
+ desc: "\u67E5\u770B\u804A\u5929\u8CC7\u6599\u5EAB\u8868\u7D50\u69CB\u3002\u7E6A\u5716\u6216\u81EA\u8A02 SQL \u524D\u61C9\u5148\u7528\u5B83\u78BA\u8A8D\u8868\u540D\u548C\u6B04\u4F4D\u540D\u3002",
16634
+ params: {}
16635
+ },
16636
+ render_chart: {
16637
+ desc: "\u6839\u64DA\u552F\u8B80 SQL \u548C ChartSpec v1 \u751F\u6210 ChatLab \u539F\u751F\u5716\u8868\u3002\u652F\u63F4 bar\u3001line\u3001pie\u3001heatmap\u3002\u7981\u6B62\u8F38\u51FA HTML\u3001JavaScript\u3001SVG\u3001ECharts option \u6216\u6E32\u67D3\u7A0B\u5F0F\u78BC\u3002",
16638
+ params: {
16639
+ sql: "\u552F\u8B80 SELECT \u6216 WITH SELECT SQL\u3002\u5FC5\u9808\u8FD4\u56DE ChartSpec encoding \u4E2D\u5F15\u7528\u7684\u6B04\u4F4D\u3002",
16640
+ params: "SQL \u547D\u540D\u53C3\u6578\u7269\u4EF6\uFF1B\u4E0D\u9700\u8981\u53C3\u6578\u6642\u50B3\u7A7A\u7269\u4EF6\u3002",
16641
+ chartSpec: "ChartSpec v1\uFF0C\u5305\u542B version\u3001type\u3001title\u3001encoding\u3002",
16642
+ maxRows: "\u5716\u8868\u67E5\u8A62\u6700\u5927\u884C\u6578\uFF0C\u9810\u8A2D 1000\u3002"
16643
+ }
16184
16644
  }
16185
16645
  },
16186
16646
  agent: {
@@ -18925,6 +19385,7 @@ export {
18925
19385
  getRelationshipStats,
18926
19386
  getLanguagePreferenceAnalysis,
18927
19387
  isNewerStableVersion,
19388
+ buildChartPayload,
18928
19389
  POS_TAG_DEFINITIONS,
18929
19390
  BUILTIN_TOOL_CATALOG,
18930
19391
  BUILTIN_PROVIDERS,
@@ -18963,14 +19424,23 @@ export {
18963
19424
  AIConversationManager,
18964
19425
  initTokenizer,
18965
19426
  countMessagesTokens,
18966
- SkillManager,
18967
19427
  SkillManagerCore,
19428
+ SkillManager,
18968
19429
  createActivateSkillTool,
19430
+ CHART_CAPABILITY_CORE_TOOLS,
19431
+ getAllowedBuiltinToolsForChartAutoSkill,
19432
+ resolveChartRuntimeForRequest,
19433
+ getChartCapabilitySkill,
19434
+ buildSkillMenuWithBuiltinChart,
19435
+ getSkillConfigWithBuiltinChart,
19436
+ createChartSchemaGateState,
19437
+ wrapWithChartSchemaGate,
18969
19438
  checkAndCompress,
18970
19439
  createCompressionLlmAdapter,
18971
19440
  getDefaultRulesForLocale,
18972
19441
  mergeRulesForLocale,
18973
19442
  applyPreprocessingPipeline,
19443
+ DEFAULT_MAX_TOOL_ROUNDS,
18974
19444
  runAgentCore,
18975
19445
  AgentEventHandler,
18976
19446
  buildSystemPrompt,
@@ -19005,4 +19475,4 @@ export {
19005
19475
  createDatabaseManagerAdapter,
19006
19476
  MergeSessionCache
19007
19477
  };
19008
- //# sourceMappingURL=chunk-N5DGZQB4.mjs.map
19478
+ //# sourceMappingURL=chunk-4OMV4VNH.mjs.map