@opentiny/next-sdk 0.2.7 → 0.2.8

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.
@@ -27762,6 +27762,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
27762
27762
  this.dropdownMenu.parentNode.removeChild(this.dropdownMenu);
27763
27763
  }
27764
27764
  }
27765
+ // 隐藏组件
27766
+ hide() {
27767
+ if (this.floatingBlock) {
27768
+ this.floatingBlock.style.display = "none";
27769
+ }
27770
+ this.closeDropdown();
27771
+ }
27772
+ // 显示组件
27773
+ show() {
27774
+ if (this.floatingBlock) {
27775
+ this.floatingBlock.style.display = "flex";
27776
+ }
27777
+ }
27765
27778
  }
27766
27779
  const createRemoter = (options = {}) => {
27767
27780
  return new FloatingBlock(options);
@@ -50165,15 +50178,24 @@ Thought: 用户想要获取今天的日期,我需要调用日期相关的工
50165
50178
  }
50166
50179
  this.onUpdatedTools?.();
50167
50180
  }
50168
- /** 创建临时允许调用的tools集合 */
50169
- _tempMergeTools(extraTool = {}) {
50170
- const toolsResult = Object.values(this.mcpTools).reduce((acc, curr) => ({ ...acc, ...curr }), {});
50181
+ /** 创建临时允许调用的 tools 集合,合并 mcpTools 与 extraTool */
50182
+ _tempMergeTools(extraTool = {}, deleteIgnored = true) {
50183
+ const toolsResult = Object.values(this.mcpTools).reduce(
50184
+ (acc, curr) => ({ ...acc, ...curr }),
50185
+ {}
50186
+ );
50171
50187
  Object.assign(toolsResult, extraTool);
50172
- this.ignoreToolnames.forEach((name16) => {
50173
- delete toolsResult[name16];
50174
- });
50188
+ if (deleteIgnored) {
50189
+ this.ignoreToolnames.forEach((name16) => {
50190
+ delete toolsResult[name16];
50191
+ });
50192
+ }
50175
50193
  return toolsResult;
50176
50194
  }
50195
+ /** 获取当前激活的 tools 名称列表(过滤 ignoreToolnames) */
50196
+ _getActiveToolNames(tools) {
50197
+ return Object.keys(tools).filter((name16) => !this.ignoreToolnames.includes(name16));
50198
+ }
50177
50199
  /** 生成 ReAct 模式的系统提示词(包含工具描述) */
50178
50200
  _generateReActSystemPrompt(tools, modelName, baseSystemPrompt) {
50179
50201
  const toolsPrompt = generateReActToolsPrompt(tools);
@@ -50517,12 +50539,14 @@ ${observationText}
50517
50539
  throw new Error("LLM is not initialized");
50518
50540
  }
50519
50541
  await this.initClientsAndTools();
50542
+ const allTools = this._tempMergeTools(options.tools, false);
50520
50543
  const chatOptions = {
50521
50544
  // @ts-ignore ProviderV2 是所有llm的父类, 在每一个具体的llm 类都有一个选择model的函数用法
50522
50545
  model: this.llm(model),
50523
50546
  stopWhen: stepCountIs(maxSteps),
50524
50547
  ...options,
50525
- tools: this._tempMergeTools(options.tools)
50548
+ tools: allTools,
50549
+ activeTools: this._getActiveToolNames(allTools)
50526
50550
  };
50527
50551
  let lastUserMessage = null;
50528
50552
  if (options.message && !options.messages) {
@@ -50552,6 +50576,282 @@ ${observationText}
50552
50576
  return this._chat(streamText, options);
50553
50577
  }
50554
50578
  }
50579
+ let overlayElement = null;
50580
+ let labelElement = null;
50581
+ let styleElement = null;
50582
+ let activeCount = 0;
50583
+ const BODY_GLOW_CLASS = "next-sdk-tool-body-glow";
50584
+ function ensureDomReady() {
50585
+ return typeof window !== "undefined" && typeof document !== "undefined";
50586
+ }
50587
+ function ensureStyleElement() {
50588
+ if (!ensureDomReady()) return;
50589
+ if (styleElement) return;
50590
+ const style = document.createElement("style");
50591
+ style.textContent = `
50592
+ .${BODY_GLOW_CLASS} {
50593
+ position: relative;
50594
+ }
50595
+
50596
+ .next-sdk-tool-overlay {
50597
+ position: fixed;
50598
+ inset: 0;
50599
+ z-index: 999999;
50600
+ pointer-events: none;
50601
+ display: flex;
50602
+ align-items: flex-end;
50603
+ justify-content: flex-start;
50604
+ padding: 0 0 18px 18px;
50605
+ background: transparent;
50606
+ animation: next-sdk-overlay-fade-in 260ms ease-out;
50607
+ }
50608
+
50609
+ .next-sdk-tool-overlay--exit {
50610
+ animation: next-sdk-overlay-fade-out 220ms ease-in forwards;
50611
+ }
50612
+
50613
+ .next-sdk-tool-overlay__glow-ring {
50614
+ display: none;
50615
+ }
50616
+
50617
+ .next-sdk-tool-overlay__panel {
50618
+ position: relative;
50619
+ min-width: min(320px, 78vw);
50620
+ max-width: min(420px, 82vw);
50621
+ padding: 10px 14px;
50622
+ border-radius: 999px;
50623
+ background:
50624
+ linear-gradient(135deg, rgba(15, 23, 42, 0.9), rgba(17, 24, 39, 0.9)),
50625
+ radial-gradient(circle at top left, rgba(96, 165, 250, 0.25), transparent 55%),
50626
+ radial-gradient(circle at bottom right, rgba(45, 212, 191, 0.22), transparent 60%);
50627
+ box-shadow:
50628
+ 0 12px 28px rgba(15, 23, 42, 0.78),
50629
+ 0 0 0 1px rgba(148, 163, 184, 0.26);
50630
+ display: flex;
50631
+ align-items: center;
50632
+ gap: 10px;
50633
+ pointer-events: none;
50634
+ transform-origin: center;
50635
+ animation: next-sdk-panel-pop-in 260ms cubic-bezier(0.18, 0.89, 0.32, 1.28);
50636
+ color: #e5e7eb;
50637
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
50638
+ }
50639
+
50640
+ .next-sdk-tool-overlay__indicator {
50641
+ width: 26px;
50642
+ height: 26px;
50643
+ border-radius: 999px;
50644
+ background: radial-gradient(circle at 30% 10%, #f9fafb, #93c5fd);
50645
+ box-shadow:
50646
+ 0 0 0 1px rgba(191, 219, 254, 0.6),
50647
+ 0 8px 18px rgba(37, 99, 235, 0.8),
50648
+ 0 0 28px rgba(56, 189, 248, 0.9);
50649
+ position: relative;
50650
+ flex-shrink: 0;
50651
+ display: flex;
50652
+ align-items: center;
50653
+ justify-content: center;
50654
+ overflow: hidden;
50655
+ }
50656
+
50657
+ .next-sdk-tool-overlay__indicator-orbit {
50658
+ position: absolute;
50659
+ inset: 2px;
50660
+ border-radius: inherit;
50661
+ border: 1px solid rgba(248, 250, 252, 0.6);
50662
+ box-sizing: border-box;
50663
+ opacity: 0.9;
50664
+ }
50665
+
50666
+ .next-sdk-tool-overlay__indicator-orbit::before {
50667
+ content: '';
50668
+ position: absolute;
50669
+ width: 6px;
50670
+ height: 6px;
50671
+ border-radius: 999px;
50672
+ background: #f9fafb;
50673
+ box-shadow:
50674
+ 0 0 12px rgba(248, 250, 252, 0.9),
50675
+ 0 0 24px rgba(250, 249, 246, 0.9);
50676
+ top: 0;
50677
+ left: 50%;
50678
+ transform: translate(-50%, -50%);
50679
+ transform-origin: 50% 18px;
50680
+ animation: next-sdk-indicator-orbit 1.4s linear infinite;
50681
+ }
50682
+
50683
+ .next-sdk-tool-overlay__indicator-core {
50684
+ width: 14px;
50685
+ height: 14px;
50686
+ border-radius: inherit;
50687
+ background: radial-gradient(circle at 30% 20%, #f9fafb, #bfdbfe);
50688
+ box-shadow:
50689
+ 0 0 12px rgba(248, 250, 252, 0.9),
50690
+ 0 0 32px rgba(191, 219, 254, 0.8);
50691
+ opacity: 0.96;
50692
+ }
50693
+
50694
+ .next-sdk-tool-overlay__content {
50695
+ display: flex;
50696
+ flex-direction: column;
50697
+ gap: 1px;
50698
+ min-width: 0;
50699
+ }
50700
+
50701
+ .next-sdk-tool-overlay__title {
50702
+ font-size: 11px;
50703
+ letter-spacing: 0.08em;
50704
+ text-transform: uppercase;
50705
+ color: rgba(156, 163, 175, 0.96);
50706
+ display: flex;
50707
+ align-items: center;
50708
+ gap: 6px;
50709
+ }
50710
+
50711
+ .next-sdk-tool-overlay__title-dot {
50712
+ width: 6px;
50713
+ height: 6px;
50714
+ border-radius: 999px;
50715
+ background: #22c55e;
50716
+ box-shadow:
50717
+ 0 0 8px rgba(34, 197, 94, 0.9),
50718
+ 0 0 14px rgba(22, 163, 74, 0.9);
50719
+ }
50720
+
50721
+ .next-sdk-tool-overlay__label {
50722
+ font-size: 12px;
50723
+ font-weight: 500;
50724
+ color: #e5e7eb;
50725
+ white-space: nowrap;
50726
+ text-overflow: ellipsis;
50727
+ overflow: hidden;
50728
+ }
50729
+
50730
+ @keyframes next-sdk-overlay-fade-in {
50731
+ from { opacity: 0; }
50732
+ to { opacity: 1; }
50733
+ }
50734
+
50735
+ @keyframes next-sdk-overlay-fade-out {
50736
+ from { opacity: 1; }
50737
+ to { opacity: 0; }
50738
+ }
50739
+
50740
+ @keyframes next-sdk-indicator-orbit {
50741
+ from {
50742
+ transform: translate(-50%, -50%) rotate(0deg) translateY(0);
50743
+ }
50744
+ to {
50745
+ transform: translate(-50%, -50%) rotate(360deg) translateY(0);
50746
+ }
50747
+ }
50748
+
50749
+ @keyframes next-sdk-panel-pop-in {
50750
+ 0% {
50751
+ opacity: 0;
50752
+ transform: scale(0.92) translateY(10px);
50753
+ }
50754
+ 100% {
50755
+ opacity: 1;
50756
+ transform: scale(1) translateY(0);
50757
+ }
50758
+ }
50759
+ `;
50760
+ document.head.appendChild(style);
50761
+ styleElement = style;
50762
+ }
50763
+ function ensureOverlayElement() {
50764
+ if (!ensureDomReady()) return;
50765
+ if (overlayElement) return;
50766
+ ensureStyleElement();
50767
+ const overlay = document.createElement("div");
50768
+ overlay.className = "next-sdk-tool-overlay";
50769
+ const glowRing = document.createElement("div");
50770
+ glowRing.className = "next-sdk-tool-overlay__glow-ring";
50771
+ const panel = document.createElement("div");
50772
+ panel.className = "next-sdk-tool-overlay__panel";
50773
+ const indicator = document.createElement("div");
50774
+ indicator.className = "next-sdk-tool-overlay__indicator";
50775
+ const indicatorOrbit = document.createElement("div");
50776
+ indicatorOrbit.className = "next-sdk-tool-overlay__indicator-orbit";
50777
+ const indicatorCore = document.createElement("div");
50778
+ indicatorCore.className = "next-sdk-tool-overlay__indicator-core";
50779
+ const content = document.createElement("div");
50780
+ content.className = "next-sdk-tool-overlay__content";
50781
+ const titleRow = document.createElement("div");
50782
+ titleRow.className = "next-sdk-tool-overlay__title";
50783
+ titleRow.textContent = "AI 正在调用页面工具";
50784
+ const titleDot = document.createElement("span");
50785
+ titleDot.className = "next-sdk-tool-overlay__title-dot";
50786
+ const label = document.createElement("div");
50787
+ label.className = "next-sdk-tool-overlay__label";
50788
+ titleRow.prepend(titleDot);
50789
+ content.appendChild(titleRow);
50790
+ content.appendChild(label);
50791
+ indicator.appendChild(indicatorOrbit);
50792
+ indicator.appendChild(indicatorCore);
50793
+ panel.appendChild(indicator);
50794
+ panel.appendChild(content);
50795
+ overlay.appendChild(glowRing);
50796
+ overlay.appendChild(panel);
50797
+ document.body.appendChild(overlay);
50798
+ overlayElement = overlay;
50799
+ labelElement = label;
50800
+ }
50801
+ function updateOverlay(config2) {
50802
+ if (!ensureDomReady()) return;
50803
+ ensureOverlayElement();
50804
+ if (!overlayElement || !labelElement) return;
50805
+ overlayElement.classList.remove("next-sdk-tool-overlay--exit");
50806
+ labelElement.textContent = config2.label;
50807
+ }
50808
+ function removeOverlayWithAnimation() {
50809
+ if (!overlayElement) return;
50810
+ overlayElement.classList.add("next-sdk-tool-overlay--exit");
50811
+ const localOverlay = overlayElement;
50812
+ let handled = false;
50813
+ let timerId;
50814
+ const handle = () => {
50815
+ if (handled) return;
50816
+ handled = true;
50817
+ if (timerId !== void 0) {
50818
+ clearTimeout(timerId);
50819
+ timerId = void 0;
50820
+ }
50821
+ if (localOverlay.parentNode) {
50822
+ localOverlay.parentNode.removeChild(localOverlay);
50823
+ }
50824
+ if (overlayElement === localOverlay) {
50825
+ overlayElement = null;
50826
+ labelElement = null;
50827
+ }
50828
+ localOverlay.removeEventListener("animationend", handle);
50829
+ };
50830
+ localOverlay.addEventListener("animationend", handle);
50831
+ timerId = setTimeout(handle, 500);
50832
+ }
50833
+ function showToolInvokeEffect(config2) {
50834
+ if (!ensureDomReady()) return;
50835
+ activeCount += 1;
50836
+ updateOverlay(config2);
50837
+ }
50838
+ function hideToolInvokeEffect() {
50839
+ if (!ensureDomReady() || activeCount <= 0) return;
50840
+ activeCount -= 1;
50841
+ if (activeCount === 0) {
50842
+ removeOverlayWithAnimation();
50843
+ }
50844
+ }
50845
+ function resolveRuntimeEffectConfig(toolName, toolTitle, value) {
50846
+ if (!value) return void 0;
50847
+ const baseLabel = toolTitle || toolName;
50848
+ if (typeof value === "boolean") {
50849
+ return value ? { label: baseLabel } : void 0;
50850
+ }
50851
+ return {
50852
+ label: value.label || baseLabel
50853
+ };
50854
+ }
50555
50855
  const MSG_TOOL_CALL = "next-sdk:tool-call";
50556
50856
  const MSG_TOOL_RESPONSE = "next-sdk:tool-response";
50557
50857
  const MSG_PAGE_READY = "next-sdk:page-ready";
@@ -50605,7 +50905,7 @@ ${observationText}
50605
50905
  function setNavigator(fn) {
50606
50906
  _navigator = fn;
50607
50907
  }
50608
- function buildPageHandler(name16, route, timeout = 3e4) {
50908
+ function buildPageHandler(name16, route, timeout = 3e4, effectConfig) {
50609
50909
  return (input) => {
50610
50910
  const callId = randomUUID();
50611
50911
  return new Promise((resolve2, reject) => {
@@ -50617,6 +50917,9 @@ ${observationText}
50617
50917
  if (readyHandler) {
50618
50918
  window.removeEventListener("message", readyHandler);
50619
50919
  }
50920
+ if (effectConfig) {
50921
+ hideToolInvokeEffect();
50922
+ }
50620
50923
  };
50621
50924
  timer = setTimeout(() => {
50622
50925
  cleanup();
@@ -50640,6 +50943,9 @@ ${observationText}
50640
50943
  };
50641
50944
  const run = async () => {
50642
50945
  try {
50946
+ if (effectConfig) {
50947
+ showToolInvokeEffect(effectConfig);
50948
+ }
50643
50949
  if (activePages.get(route)) {
50644
50950
  sendCallOnce();
50645
50951
  return;
@@ -50676,9 +50982,10 @@ ${observationText}
50676
50982
  if (typeof handlerOrRoute === "function") {
50677
50983
  return rawRegister(name16, config2, handlerOrRoute);
50678
50984
  }
50679
- const { route, timeout } = handlerOrRoute;
50985
+ const { route, timeout, invokeEffect } = handlerOrRoute;
50680
50986
  toolRouteMap.set(name16, route);
50681
- return rawRegister(name16, config2, buildPageHandler(name16, route, timeout));
50987
+ const effectConfig = resolveRuntimeEffectConfig(name16, config2?.title, invokeEffect);
50988
+ return rawRegister(name16, config2, buildPageHandler(name16, route, timeout, effectConfig));
50682
50989
  };
50683
50990
  }
50684
50991
  return Reflect.get(target, prop, receiver);
@@ -50776,33 +51083,95 @@ ${lines.join("\n")}
50776
51083
  }
50777
51084
  function getSkillMdContent(modules, path) {
50778
51085
  const normalized = normalizeSkillModuleKeys(modules);
50779
- return normalized[path];
51086
+ const exactMatch = normalized[path];
51087
+ if (exactMatch) return exactMatch;
51088
+ const suffix = path.replace(/^\.?\//, "/");
51089
+ const matchingKey = Object.keys(normalized).find((key) => key.endsWith(suffix));
51090
+ return matchingKey ? normalized[matchingKey] : void 0;
50780
51091
  }
50781
51092
  function getMainSkillPathByName(modules, name16) {
50782
- return getMainSkillPaths(modules).find((p) => p.startsWith(`./${name16}/SKILL.md`));
51093
+ const normalizedModules = normalizeSkillModuleKeys(modules);
51094
+ const paths = getMainSkillPaths(normalizedModules);
51095
+ const dirMatch = paths.find((p) => p.startsWith(`./${name16}/SKILL.md`));
51096
+ if (dirMatch) return dirMatch;
51097
+ for (const p of paths) {
51098
+ const content = normalizedModules[p];
51099
+ if (content) {
51100
+ const parsed = parseSkillFrontMatter(content);
51101
+ if (parsed && parsed.name === name16) {
51102
+ return p;
51103
+ }
51104
+ }
51105
+ }
51106
+ return void 0;
50783
51107
  }
50784
51108
  const SKILL_INPUT_SCHEMA = objectType({
50785
- skillName: stringType().optional().describe("技能名称,与目录名一致,如 calculator"),
50786
- path: stringType().optional().describe("文档相对路径,如 ./calculator/SKILL.md 或 ./product-guide/reference/xxx.json")
51109
+ skillName: stringType().optional().describe(
51110
+ '进入某个技能的主入口名称。优先匹配技能的目录名(如 ecommerce),或者技能的中文名称(如"客户价保单创建及审核")。'
51111
+ ),
51112
+ path: stringType().optional().describe("你想查阅的文档的路径。如 ./calculator/SKILL.md 或从其他文档里看到的相对路径 ./reference/inventory.md。"),
51113
+ currentPath: stringType().optional().describe(
51114
+ "你当前正在阅读的文档路径(如果有)。比如你刚刚读取了 ./ecommerce/SKILL.md,请把这个路径原样传回来,这样系统才能根据你的相对路径准确找到下一份文件。"
51115
+ )
50787
51116
  });
50788
51117
  function createSkillTools(modules) {
50789
51118
  const normalizedModules = normalizeSkillModuleKeys(modules);
50790
51119
  const getSkillContent = tool({
50791
- description: "根据技能名称或文档路径获取该技能的完整文档内容。传入 skillName(如 calculator)或 path(如 ./calculator/SKILL.md)。支持 .md、.json、.xml 等各类文本格式文件。",
51120
+ description: "根据技能名称或文档路径获取该技能的完整文档内容。如果你想根据相对路径查阅文件,请务必同时提供你当前所在的文件路径 currentPath。",
50792
51121
  inputSchema: SKILL_INPUT_SCHEMA,
50793
51122
  execute: (args) => {
50794
- const { skillName, path: pathArg } = args;
51123
+ const { skillName, path: pathArg, currentPath: currentPathArg } = args;
50795
51124
  let content;
51125
+ let resolvedPath = "";
50796
51126
  if (pathArg) {
50797
- content = getSkillMdContent(normalizedModules, pathArg);
51127
+ let basePathContext = ".";
51128
+ if (currentPathArg) {
51129
+ const lastSlashIndex = currentPathArg.lastIndexOf("/");
51130
+ if (lastSlashIndex >= 0) {
51131
+ basePathContext = currentPathArg.slice(0, lastSlashIndex);
51132
+ }
51133
+ }
51134
+ const dummyBase = `http://localhost/${basePathContext}/`;
51135
+ const url2 = new URL(pathArg, dummyBase);
51136
+ resolvedPath = "." + url2.pathname;
51137
+ content = getSkillMdContent(normalizedModules, resolvedPath);
51138
+ if (content === void 0 && (pathArg.startsWith("./") || pathArg.startsWith("../")) && currentPathArg) {
51139
+ const baseParts = currentPathArg.split("/");
51140
+ if (baseParts.length >= 2) {
51141
+ const skillRoot = baseParts[1];
51142
+ const fallbackDummyBase = `http://localhost/${skillRoot}/`;
51143
+ const fallbackUrl = new URL(pathArg, fallbackDummyBase);
51144
+ const fallbackPath = "." + fallbackUrl.pathname;
51145
+ content = getSkillMdContent(normalizedModules, fallbackPath);
51146
+ if (content) {
51147
+ resolvedPath = fallbackPath;
51148
+ }
51149
+ }
51150
+ }
51151
+ if (content && !normalizedModules[resolvedPath]) {
51152
+ const suffix = resolvedPath.replace(/^\.?\//, "/");
51153
+ const matchingKey = Object.keys(normalizedModules).find((key) => key.endsWith(suffix));
51154
+ if (matchingKey) {
51155
+ resolvedPath = matchingKey;
51156
+ }
51157
+ }
50798
51158
  } else if (skillName) {
50799
51159
  const mainPath = getMainSkillPathByName(normalizedModules, skillName);
50800
- content = mainPath ? getSkillMdContent(normalizedModules, mainPath) : void 0;
51160
+ if (mainPath) {
51161
+ resolvedPath = mainPath;
51162
+ content = getSkillMdContent(normalizedModules, mainPath);
51163
+ }
50801
51164
  }
50802
51165
  if (content === void 0) {
50803
- return { error: "未找到对应技能文档", skillName: skillName ?? pathArg };
51166
+ return {
51167
+ error: "未找到对应技能文档",
51168
+ skillName,
51169
+ path: pathArg,
51170
+ providedCurrentPath: currentPathArg,
51171
+ attemptedPath: resolvedPath
51172
+ };
50804
51173
  }
50805
- return { content, path: pathArg ?? getMainSkillPathByName(normalizedModules, skillName) };
51174
+ return { content, path: resolvedPath };
50806
51175
  }
50807
51176
  });
50808
51177
  return {