@opentiny/next-sdk 0.2.3 → 0.2.4

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.ts CHANGED
@@ -38,3 +38,17 @@ export { getAISDKTools } from './agent/utils/getAISDKTools'
38
38
  // 方便的二维码类
39
39
  export { QrCode, type QrCodeOption } from './remoter/QrCode'
40
40
  export type * from './agent/type'
41
+
42
+ // Web 端 Skill 公共能力:解析 skill 文档、生成 systemPrompt、内置 list_skills / get_skill_content 工具
43
+ export {
44
+ getSkillOverviews,
45
+ formatSkillsForSystemPrompt,
46
+ getSkillMdPaths,
47
+ getSkillMdContent,
48
+ getMainSkillPaths,
49
+ getMainSkillPathByName,
50
+ parseSkillFrontMatter,
51
+ createSkillTools,
52
+ type SkillMeta,
53
+ type SkillToolsSet
54
+ } from './skills/index'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentiny/next-sdk",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.opentiny.design/next-sdk/guide/",
6
6
  "repository": {
@@ -26,14 +26,14 @@
26
26
  "dependencies": {
27
27
  "@modelcontextprotocol/sdk": "~1.25.2",
28
28
  "@opentiny/next": "^0.3.2",
29
- "@ai-sdk/openai": "^2.0.76",
29
+ "@ai-sdk/openai": "^3.0.0",
30
30
  "@ai-sdk/deepseek": "1.0.30",
31
- "@ai-sdk/provider": "^2.0.0",
32
- "@ai-sdk/mcp": "^0.0.11",
31
+ "@ai-sdk/provider": "^3.0.0",
32
+ "@ai-sdk/mcp": "^1.0.0",
33
33
  "qrcode": "^1.5.4",
34
34
  "zod": "^3.25.76",
35
35
  "ajv": "^8.17.1",
36
- "ai": "5.0.106"
36
+ "ai": "^6.0.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "typescript": "~5.9.3",
@@ -6,7 +6,7 @@ import link from './svgs/link.svg?url'
6
6
  import qrCode from './svgs/qrcode.svg?url'
7
7
  import iconCopy from './svgs/icon-copy.svg?url'
8
8
 
9
- const DEFAULT_REMOTE_URL = 'https://agent.opentiny.design/tiny-robot'
9
+ const DEFAULT_REMOTE_URL = 'https://chat.opentiny.design'
10
10
  const DEFAULT_QR_CODE_URL = 'https://ai.opentiny.design/next-remoter'
11
11
  const DEFAULT_LOGO_URL = 'https://ai.opentiny.design/next-remoter/svgs/logo-next-no-bg-left.svg'
12
12
 
@@ -43,7 +43,7 @@ export interface FloatingBlockOptions {
43
43
  sessionId: string
44
44
  /** 菜单项配置 */
45
45
  menuItems?: MenuItemConfig[]
46
- /** 遥控端页面地址,默认为: https://agent.opentiny.design/tiny-robot */
46
+ /** 遥控端页面地址,默认为: https://chat.opentiny.design */
47
47
  remoteUrl?: string
48
48
  /** 悬浮Logo的url地址,默认为: https://ai.opentiny.design/next-remoter/svgs/logo-next-no-bg-left.svg */
49
49
  logoUrl?: string
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Web 端 Skill 公共能力模块(next-sdk)
3
+ * - 提供解析、概况、systemPrompt 拼接、按路径/名称查文档
4
+ * - 提供 createSkillTools:供 remoter 注入 get_skill_content 工具,大模型可按需加载技能文档
5
+ */
6
+
7
+ import { tool } from 'ai'
8
+ import { z } from 'zod'
9
+
10
+ /** 主 SKILL.md 路径格式:仅匹配一级子目录下的 SKILL.md,如 ./calculator/SKILL.md */
11
+ const MAIN_SKILL_PATH_REG = /^\.\/[^/]+\/SKILL\.md$/
12
+
13
+ /** 从 front matter 中提取 name 和 description 的正则(--- 与 --- 之间) */
14
+ const FRONT_MATTER_BLOCK_REG = /^---\s*\n([\s\S]+?)\s*\n---/
15
+
16
+ /** 单个技能的概况信息(从主 SKILL.md 的 front matter 提取) */
17
+ export interface SkillMeta {
18
+ /** 技能名称,与 skill 目录名一致 */
19
+ name: string
20
+ /** 技能描述,用于 systemPrompt */
21
+ description: string
22
+ /** 主 SKILL.md 相对路径,如 ./calculator/SKILL.md */
23
+ path: string
24
+ }
25
+
26
+ /**
27
+ * 从主 SKILL.md 的 YAML front matter 中用正则提取 name、description
28
+ */
29
+ export function parseSkillFrontMatter(content: string): { name: string; description: string } | null {
30
+ // 先提取 --- 之间的文本块
31
+ const blockMatch = content.match(FRONT_MATTER_BLOCK_REG)
32
+ if (!blockMatch?.[1]) return null
33
+ const block = blockMatch[1]
34
+
35
+ // 分别匹配 name 和 description 字段(支持任意顺序)
36
+ const nameMatch = block.match(/^name:\s*(.+)$/m)
37
+ const descMatch = block.match(/^description:\s*(.+)$/m)
38
+
39
+ const name = nameMatch?.[1]?.trim()
40
+ const description = descMatch?.[1]?.trim()
41
+
42
+ return name && description ? { name, description } : null
43
+ }
44
+
45
+ /**
46
+ * 获取所有「主 SKILL.md」的路径(一级子目录下的 SKILL.md)
47
+ */
48
+ export function getMainSkillPaths(modules: Record<string, string>): string[] {
49
+ return Object.keys(modules).filter((path) => MAIN_SKILL_PATH_REG.test(path))
50
+ }
51
+
52
+ /**
53
+ * 获取所有技能的概况列表(name、description、path),用于 systemPrompt 或列表展示
54
+ */
55
+ export function getSkillOverviews(modules: Record<string, string>): SkillMeta[] {
56
+ const mainPaths = getMainSkillPaths(modules)
57
+ const list: SkillMeta[] = []
58
+ for (const path of mainPaths) {
59
+ const content = modules[path]
60
+ if (!content) continue
61
+ const parsed = parseSkillFrontMatter(content)
62
+ if (!parsed) continue
63
+ list.push({
64
+ name: parsed.name,
65
+ description: parsed.description,
66
+ path
67
+ })
68
+ }
69
+ return list
70
+ }
71
+
72
+ /**
73
+ * 格式化为大模型 systemPrompt 可用的技能说明文本
74
+ * @param skills 不传则需由调用方传入从 getSkillOverviews 得到的结果
75
+ */
76
+ export function formatSkillsForSystemPrompt(skills: SkillMeta[]): string {
77
+ if (skills.length === 0) return ''
78
+ const lines = skills.map((s) => `- **${s.name}**: ${s.description}`)
79
+ return `## 可用技能\n\n${lines.join('\n')}\n\n当需要用到某技能时,请使用 get_skill_content 工具获取该技能的完整文档内容。`
80
+ }
81
+
82
+ /**
83
+ * 获取所有已加载的 md 文件路径(含主 SKILL.md 与 reference 等)
84
+ */
85
+ export function getSkillMdPaths(modules: Record<string, string>): string[] {
86
+ return Object.keys(modules)
87
+ }
88
+
89
+ /**
90
+ * 根据相对路径获取某个 md 文档的原始内容
91
+ */
92
+ export function getSkillMdContent(modules: Record<string, string>, path: string): string | undefined {
93
+ return modules[path]
94
+ }
95
+
96
+ /**
97
+ * 根据技能 name 查找其主 SKILL.md 的路径(name 与目录名一致)
98
+ */
99
+ export function getMainSkillPathByName(modules: Record<string, string>, name: string): string | undefined {
100
+ return getMainSkillPaths(modules).find((p) => p.startsWith(`./${name}/SKILL.md`))
101
+ }
102
+
103
+ // ============ 内置工具:供 remoter 注入,替代业界 skill 中「读取文档」的操作 ============
104
+
105
+ /** AI SDK Tool 类型,用于 extraTools 合并,不写死泛型避免与 ai 包版本强绑定 */
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ export type SkillToolsSet = Record<string, any>
108
+
109
+ /**
110
+ * 根据 skillMdModules 创建供 AI 调用的工具集
111
+ * - get_skill_content: 按技能名或路径获取完整文档内容,便于大模型自动识别并加载技能
112
+ * remoter 可将返回的 tools 合并进 extraTools 注入 agent
113
+ */
114
+ export function createSkillTools(modules: Record<string, string>): SkillToolsSet {
115
+ const getSkillContent = tool({
116
+ description:
117
+ '根据技能名称或文档路径获取该技能的完整 Markdown 文档内容。传入 skillName(如 calculator)或 path(如 ./calculator/SKILL.md)',
118
+ inputSchema: z.object({
119
+ skillName: z.string().optional().describe('技能名称,与目录名一致,如 calculator'),
120
+ path: z.string().optional().describe('文档相对路径,如 ./calculator/SKILL.md 或 ./product-guide/reference/xxx.md')
121
+ }),
122
+ execute: (args: { skillName?: string; path?: string }) => {
123
+ const { skillName, path: pathArg } = args
124
+ let content: string | undefined
125
+ if (pathArg) {
126
+ content = getSkillMdContent(modules, pathArg)
127
+ } else if (skillName) {
128
+ const mainPath = getMainSkillPathByName(modules, skillName)
129
+ content = mainPath ? getSkillMdContent(modules, mainPath) : undefined
130
+ }
131
+ if (content === undefined) {
132
+ return { error: '未找到对应技能文档', skillName: skillName ?? pathArg }
133
+ }
134
+ return { content, path: pathArg ?? getMainSkillPathByName(modules, skillName!) }
135
+ }
136
+ })
137
+
138
+ return {
139
+ get_skill_content: getSkillContent
140
+ }
141
+ }