@ray-js/t-agent 0.2.7-beta.9 → 0.2.8-beta.1

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/README-zh_CN.md CHANGED
@@ -28,14 +28,7 @@ yarn add @ray-js/t-agent @ray-js/t-agent-plugin-aistream @ray-js/t-agent-ui-ray
28
28
  }
29
29
  ```
30
30
 
31
- ### 小程序 kit 与功能对应表
32
-
33
- 部分功能对 App 的 kit 有依赖要求,需要配置 AIStreamKit 最低才能使用这些功能
34
-
35
- | 功能 | AIStreamKit 最低版本 | 相关 API |
36
- | ---------- | -------------------- | ------------------------------------- |
37
- | 对话分组 | 1.3.2 | agent.plugins.aiStream.getChatId |
38
- | 自定义参数 | 2.1.0 | agent.plugins.aiStream.onUserDataRead |
31
+ > 使用 MCP 功能时,`AIStreamKit` 需要 `2.2.1` 或以上版本。
39
32
 
40
33
  ## package.json 依赖要求
41
34
 
@@ -68,59 +61,70 @@ yarn run miniapp
68
61
  import React from 'react';
69
62
  import { View } from '@ray-js/components';
70
63
  import { createChatAgent, withDebug, withUI } from '@ray-js/t-agent';
71
- import { ChatContainer, MessageInput, MessageList, MessageActionBar } from '@ray-js/t-agent-ui-ray';
64
+ import {
65
+ ChatContainer,
66
+ defaultRenderOptions,
67
+ MessageInput,
68
+ MessageList,
69
+ MessageActionBar,
70
+ } from '@ray-js/t-agent-ui-ray';
72
71
  import { withAIStream, withBuildIn } from '@ray-js/t-agent-plugin-aistream';
73
72
 
74
73
  const createAgent = () => {
75
- try {
76
- // 应用插件是有顺序的
77
- const agent = createChatAgent(
78
- withUI(), // 第一个插件 withUI 插件提供了一些默认的 UI 行为,必选
79
- withAIStream({
80
- // withAIStream 插件对接小程序 AI 智能体平台,在小程序中必选
81
- earlyStart: true, // 是否在 onAgentStart 阶段就建立连接
82
- agentId: '', // 输入你的智能体ID
83
- }),
84
- withDebug(), // withDebug 会在 console 里打印日志
85
- withBuildIn() // withBuildIn 插件提供了一些内置的功能
86
- );
74
+ const agent = createChatAgent(
75
+ withUI(),
76
+ withAIStream({
77
+ earlyStart: true,
78
+ agentId: 'your-agent-id',
79
+ tokenOptions: {
80
+ api: 'm.life.ai.agent.token.get',
81
+ version: '1.0',
82
+ extParams: {
83
+ dialogueMode: 1,
84
+ },
85
+ },
86
+ }),
87
+ withDebug(),
88
+ withBuildIn()
89
+ );
87
90
 
88
- // 将生命周期钩子拿出来,方便注册自定义行为
89
- const { onChatStart, createMessage, onChatResume, onError, onInputBlocksPush, session } = agent;
91
+ agent.plugins.aiStream.onUserDataRead((type, data, result) => {
92
+ if (type === 'start-event') {
93
+ result.userData = {
94
+ sessionAttributes: {
95
+ 'custom.param': {
96
+ 'custom.app.scene': {
97
+ value: 'chat-page',
98
+ },
99
+ },
100
+ },
101
+ };
102
+ }
103
+ });
90
104
 
91
- // 初始化聊天时,发送一条消息
92
- onChatStart(async result => {
93
- const hello = createMessage({
94
- role: 'assistant',
95
- });
105
+ const { onChatStart, createMessage } = agent;
96
106
 
97
- hello.bubble.setText('Hello, world!');
98
- result.messages.push(hello);
99
- // 持久化消息,下次进入时会展示
100
- await hello.persist();
107
+ onChatStart(async result => {
108
+ const hello = createMessage({
109
+ role: 'assistant',
101
110
  });
102
111
 
103
- // 恢复聊天时,发送一条消息
104
- onChatResume(async result => {
105
- const welcomeBack = createMessage({
106
- role: 'assistant',
107
- });
112
+ hello.bubble.setText('Hello, world!');
113
+ result.messages.push(hello);
114
+ await hello.persist();
115
+ });
108
116
 
109
- welcomeBack.bubble.setText('Welcome back');
110
- result.messages.push(welcomeBack);
111
- await welcomeBack.persist();
112
- });
113
- return agent;
114
- } catch (error) {
115
- console.error('Agent creation failed:', error);
116
- throw error;
117
- }
117
+ return agent;
118
+ };
119
+
120
+ const renderOptions = {
121
+ ...defaultRenderOptions,
118
122
  };
119
123
 
120
124
  export default function ChatPage() {
121
125
  return (
122
126
  <View style={{ height: '100vh' }}>
123
- <ChatContainer createAgent={createAgent}>
127
+ <ChatContainer createAgent={createAgent} renderOptions={renderOptions}>
124
128
  <MessageList />
125
129
  <MessageInput />
126
130
  <MessageActionBar />
@@ -799,6 +803,8 @@ const createAgent = () => {
799
803
  - `options.sendBy` 发送者角色,默认为 'user'
800
804
  - `options.responseBy` 响应者角色,默认为 'assistant'
801
805
  - `options.userData` 可选的用户数据
806
+ - `agent.plugins.aiStream.removeMessage(message)` 删除一条历史消息
807
+ - `agent.plugins.aiStream.clearAllMessages()` 清空当前会话的所有消息和本地历史
802
808
  - `agent.plugins.aiStream.getChatId()` 获取当前会话的 chatId,返回 Promise<string>
803
809
 
804
810
  Hooks:
@@ -814,7 +820,7 @@ Hooks:
814
820
  - `status` 消息状态
815
821
  - `result.text` 文本内容,可以修改
816
822
  - `onSkillCompose` 当收到技能数据时触发,用于处理技能的渲染
817
- - `skill` 技能数据数组 (ReceivedTextSkillPacketBody[])
823
+ - `skill` 当前技能数据 (ReceivedTextSkillPacketBody)
818
824
  - `respMsg` 响应消息
819
825
  - `result.messages` 消息列表
820
826
  - `onSkillsEnd` 当所有技能处理完成时触发
@@ -835,8 +841,99 @@ Hooks:
835
841
  - `data.blocks` 当 type 为 'start-event' 时,包含本次发送的输入块
836
842
  - `result.userData` 返回的用户数据对象,会被合并后发送给 AI 平台
837
843
 
844
+ ### withBuildIn 插件
845
+
846
+ 提供了一些内置的功能,比如智能家居、知识库搜索等。
847
+
848
+ **支持的技能**:
849
+
850
+ - **智能家居**:设备控制、场景管理
851
+ - **知识库搜索**:关联文档展示
852
+
853
+ ### withMCP 插件
854
+
855
+ 提供 MCP 服务暴露能力,可以实现让 Agent 调用小程序里面提供的 tool,实现操作蓝牙设备,调用自定义 api,读取本地文件等功能,使用时先需要在智能体节点上开启“设备 MCP”,此外,MCP tool 的名字必须以 `device.` 开头。
856
+
857
+ ![custom-var-config.jpg](https://static1.tuyacn.com/static/txp-ray-TAgent/custom-var-config.jpg)
858
+
859
+ **使用前提**:
860
+
861
+ - 必须在 `withAIStream` 之后使用
862
+ - 使用 MCP 功能时,`AIStreamKit` 需要 `2.2.1` 或以上版本
863
+ - `createServer` 必须同步返回 `McpServer`
864
+
865
+ 参数:
866
+
867
+ - `createServer(agent)` 创建 MCP 服务实例
868
+ - `createContext(event, agent)` 可选,为每次 MCP 请求补充上下文
869
+
838
870
  **使用示例**:
839
871
 
872
+ ```tsx
873
+ import { createChatAgent, withUI } from '@ray-js/t-agent';
874
+ import { McpServer, withAIStream, withMCP } from '@ray-js/t-agent-plugin-aistream';
875
+
876
+ const createAgent = () => {
877
+ const server = new McpServer({
878
+ name: 'demo-mcp',
879
+ version: '1.0.0',
880
+ });
881
+
882
+ server.registerTool(
883
+ 'device.echo',
884
+ {
885
+ description: '回显输入参数',
886
+ inputSchema: {
887
+ type: 'object',
888
+ properties: {
889
+ text: { type: 'string' },
890
+ },
891
+ },
892
+ },
893
+ async (args, context) => {
894
+ return {
895
+ content: [
896
+ {
897
+ type: 'text',
898
+ text: JSON.stringify({
899
+ text: args.text,
900
+ sessionId: context.sessionId,
901
+ }),
902
+ },
903
+ ],
904
+ };
905
+ }
906
+ );
907
+
908
+ return createChatAgent(
909
+ withUI(),
910
+ withAIStream({
911
+ agentId: 'your-agent-id',
912
+ }),
913
+ withMCP({
914
+ createServer: () => server,
915
+ createContext: event => ({
916
+ traceId: event.eventId,
917
+ }),
918
+ })
919
+ );
920
+ };
921
+ ```
922
+
923
+ 启用后,插件会:
924
+
925
+ - 在创建 session 时注入 `sessionAttributes.deviceMcp`
926
+ - 在收到 `MCP_CMD` 事件时调用对应 tool
927
+ - 将 tool 执行结果按 JSON-RPC 响应格式回传给 AIStream
928
+
929
+ ### 自定义变量传入
930
+
931
+ 某些情况下,你可能需要自定义变量到 agent 作为流程中的变量,你需要在工作流里配置自定义变量的引用处,然后再在小程序里传入自定义变量:
932
+
933
+ ![custom-var-config.jpg](https://static1.tuyacn.com/static/txp-ray-TAgent/custom-var-config.jpg)
934
+
935
+ **小程序中传入自定义变量**:
936
+
840
937
  ```tsx
841
938
  const agent = createChatAgent(
842
939
  withUI(),
@@ -848,20 +945,28 @@ const agent = createChatAgent(
848
945
  agent.plugins.aiStream.onUserDataRead((type, data, result) => {
849
946
  // 在创建会话时传递用户信息
850
947
  if (type === 'create-session') {
851
- result.sessionAttributes = {
852
- 'custom.param': {
853
- userName: { value: 'John' },
854
- userLevel: { value: 'Plus' },
948
+ result.userData = {
949
+ sessionAttributes: {
950
+ 'custom.param': {
951
+ 'custom.app.test_me': {
952
+ // 此处为平台上配置接收的参数名
953
+ value: `with value test me ${type} ${Date.now()}`,
954
+ },
955
+ },
855
956
  },
856
957
  };
857
958
  return;
858
959
  }
859
960
  // 在每次发送消息时传递动态上下文
860
961
  if (type === 'start-event') {
861
- result.eventAttributes = {
862
- 'custom.param': {
863
- timestamp: { value: Date.now() },
864
- pid: { value: '123456' },
962
+ result.userData = {
963
+ sessionAttributes: {
964
+ 'custom.param': {
965
+ 'custom.app.test_me': {
966
+ // 此处为平台上配置接收的参数名
967
+ value: `with value test me ${type} ${Date.now()}`,
968
+ },
969
+ },
865
970
  },
866
971
  };
867
972
  return;
@@ -869,15 +974,6 @@ agent.plugins.aiStream.onUserDataRead((type, data, result) => {
869
974
  });
870
975
  ```
871
976
 
872
- ### withBuildIn 插件
873
-
874
- 提供了一些内置的功能,比如智能家居、知识库搜索等。
875
-
876
- **支持的技能**:
877
-
878
- - **智能家居**:设备控制、场景管理
879
- - **知识库搜索**:关联文档展示
880
-
881
977
  ## mock 机制
882
978
 
883
979
  为了方便开发,我们提供了一个 mock 机制,可以在开发时不用连接小程序 AI 智能体平台,直接使用 mock 数据进行开发。
@@ -927,6 +1023,92 @@ mock.hooks.hook('asrDetection', context => {
927
1023
  });
928
1024
  ```
929
1025
 
1026
+ ### mock MCP
1027
+
1028
+ 推荐做法是在 mock 的 `sendToAIStream` 钩子里,根据输入内容决定要调用哪个 MCP tool,再通过 `context.callMCPTool()` 直接走一遍完整的 MCP 调用链。
1029
+
1030
+ ```tsx
1031
+ import { mock } from '@ray-js/t-agent-plugin-aistream';
1032
+
1033
+ const getToolCall = (text: string) => {
1034
+ if (text.includes('报错') || text.includes('失败') || text.includes('异常')) {
1035
+ return {
1036
+ name: 'device.home.energy.summary.fail',
1037
+ arguments: {
1038
+ reason: 'mock-error-case',
1039
+ },
1040
+ };
1041
+ }
1042
+
1043
+ if (text.includes('能耗') || text.includes('电量') || text.includes('数据')) {
1044
+ return {
1045
+ name: 'device.home.energy.summary.get',
1046
+ arguments: {
1047
+ period: 'today',
1048
+ },
1049
+ };
1050
+ }
1051
+
1052
+ return {
1053
+ name: 'device.app.open',
1054
+ arguments: {
1055
+ category: 'music',
1056
+ },
1057
+ };
1058
+ };
1059
+
1060
+ export const setupMockMCP = () => {
1061
+ mock.hooks.hook('sendToAIStream', async context => {
1062
+ const text = context.data
1063
+ .filter(item => item.type === 'text')
1064
+ .map(item => item.text)
1065
+ .join(' ');
1066
+
1067
+ if (!/mcp|应用|能耗|电量|数据|报错|失败|异常/i.test(text)) {
1068
+ return;
1069
+ }
1070
+
1071
+ const toolCall = getToolCall(text);
1072
+
1073
+ await context.writeText(`MCP mock 正在调用 ${toolCall.name}...`);
1074
+
1075
+ try {
1076
+ const result = await context.callMCPTool(toolCall.name, toolCall.arguments, {
1077
+ delayMs: 80,
1078
+ });
1079
+ await context.writeText(
1080
+ `工具 ${toolCall.name} 已返回:${
1081
+ typeof result === 'string' ? result : JSON.stringify(result == null ? {} : result)
1082
+ }`
1083
+ );
1084
+ } catch (error) {
1085
+ const message = error instanceof Error ? error.message : String(error);
1086
+ await context.writeText(`MCP mock 已收到工具报错:${message}`);
1087
+ }
1088
+
1089
+ await context.end();
1090
+ });
1091
+ };
1092
+ ```
1093
+
1094
+ 使用时在创建 agent 前注册一次即可:
1095
+
1096
+ ```tsx
1097
+ const createAgent = () => {
1098
+ setupMockMCP();
1099
+
1100
+ return createChatAgent(
1101
+ withUI(),
1102
+ withAIStream({
1103
+ agentId: 'your-agent-id',
1104
+ }),
1105
+ withMCP({
1106
+ createServer: () => server,
1107
+ })
1108
+ );
1109
+ };
1110
+ ```
1111
+
930
1112
  ## 附带的 utils 工具(现在不稳定,还在开发中)
931
1113
 
932
1114
  ### AbortController
@@ -1121,17 +1303,30 @@ yarn add @ray-js/t-agent-ui-ray
1121
1303
  ## 使用
1122
1304
 
1123
1305
  ```tsx
1124
- import { ChatContainer, MessageList, MessageInput } from '@ray-js/t-agent-ui-ray';
1306
+ import React from 'react';
1307
+ import { View } from '@ray-js/components';
1308
+ import {
1309
+ ChatContainer,
1310
+ defaultRenderOptions,
1311
+ MessageActionBar,
1312
+ MessageInput,
1313
+ MessageList,
1314
+ } from '@ray-js/t-agent-ui-ray';
1125
1315
  // createAgent 实现参见 t-agent 的使用示例
1126
1316
  import { createAgent } from './createAgent';
1127
1317
 
1318
+ const renderOptions = {
1319
+ ...defaultRenderOptions,
1320
+ };
1321
+
1128
1322
  export default function ChatPage() {
1129
1323
  // createAgent 必须返回一个 ChatAgent 应用过 withUI、withAIStream 插件的实例
1130
1324
  return (
1131
1325
  <View style={{ height: '100vh' }}>
1132
- <ChatContainer createAgent={createAgent}>
1326
+ <ChatContainer createAgent={createAgent} renderOptions={renderOptions}>
1133
1327
  <MessageList />
1134
1328
  <MessageInput />
1329
+ <MessageActionBar />
1135
1330
  </ChatContainer>
1136
1331
  </View>
1137
1332
  );
@@ -1190,7 +1385,16 @@ props:
1190
1385
 
1191
1386
  - `className` 输入框的类名
1192
1387
  - `placeholder` 输入框的占位符
1388
+ - `placeholderStyle` 输入框 placeholder 的样式
1193
1389
  - `renderTop` 用于渲染输入框上方的内容
1390
+ - `style` 输入框容器样式
1391
+ - `attachment` 是否启用附件上传,或传入 `{ image, video, imageCount, videoCount }` 精细控制
1392
+ - `maxTextLength` 文本最大长度,默认 200
1393
+ - `maxAudioMs` 录音最大时长
1394
+ - `onStateChange` 输入框状态变化回调,状态值见 `MessageInputState`
1395
+ - `amplitudeCount` 语音波形振幅采样数量
1396
+
1397
+ `MessageInput` 当前默认导出的是 `MessageInputAIStream`,适用于已接入 `withAIStream` 的场景。
1194
1398
 
1195
1399
  ### MessageActionBar(0.2.x 新增)
1196
1400
 
@@ -1270,7 +1474,12 @@ const MyTile = ({ notifyHeightChanged }) => {
1270
1474
  - bubble 气泡
1271
1475
  - buttons 按钮组
1272
1476
  - card 卡片
1477
+ - divider 分隔线
1478
+ - documents 关联文档
1479
+ - executeCard 执行结果卡片
1480
+ - file 文件
1273
1481
  - image 图片
1482
+ - operateCard 操作结果卡片
1274
1483
  - recommendations 推荐行动
1275
1484
  - text 文本,包含 markdown 支持
1276
1485
  - time 时间标识
@@ -1278,10 +1487,6 @@ const MyTile = ({ notifyHeightChanged }) => {
1278
1487
  - video 视频
1279
1488
  - workflow 工作选项
1280
1489
 
1281
- ### 内置 card
1282
-
1283
- - WorkflowReplyCard 工作流回复卡片
1284
-
1285
1490
  ## React Hooks
1286
1491
 
1287
1492
  ### useChatAgent
@@ -1346,16 +1551,23 @@ const MyTilePart = () => {
1346
1551
 
1347
1552
  ### useSendAction
1348
1553
 
1349
- 发送一个 TTTAction,注意只能在 tile 组件(或 card)里使用
1554
+ 发送一个 TTTAction
1555
+
1556
+ 如果当前组件位于 tile 或 card 内部,会优先走 `emitTileEvent`。
1557
+ 如果当前组件只是 `ChatContainer` 下的普通子组件,则会退化为触发 UI 事件 `runTTTAction`。
1350
1558
 
1351
1559
  ```tsx
1352
1560
  import { useSendAction } from '@ray-js/t-agent-ui-ray';
1353
1561
 
1354
- const MyTilePart = () => {
1562
+ const ActionPanel = () => {
1355
1563
  const sendAction = useSendAction();
1356
1564
 
1357
1565
  const handleClick = () => {
1358
- sendAction({ type: 'sendMessage', blocks: [{ type: 'text', text: 'hello' }] });
1566
+ sendAction({
1567
+ type: 'sendMessage',
1568
+ blocks: [{ type: 'text', text: 'hello' }],
1569
+ sendImmediately: true,
1570
+ });
1359
1571
  };
1360
1572
 
1361
1573
  return <button onClick={handleClick}>Send Message</button>;
@@ -1762,33 +1974,3 @@ const renderOptions = {
1762
1974
  | t-agent.error.stream-exists | 错误提示 | 消息发送异常,请稍后再试 |
1763
1975
  | t-agent.error.timeout | 错误提示 | 发送超时 |
1764
1976
  | t-agent.error.asr-empty | 错误提示 | 语音识别结果为空 |
1765
-
1766
- # 更新日志
1767
-
1768
- ## 0.2.x 版本
1769
-
1770
- ### @ray-js/t-agent
1771
-
1772
- - **Hook 机制增强**:新增 `onMessageFeedback` 和 `onClearHistory` 等生命周期钩子
1773
- - **消息状态管理**:优化消息状态管理,提供更精确的消息状态控制
1774
- - **错误处理**:增强错误处理机制,支持更详细的错误信息和错误分类
1775
- - **性能优化**:优化内存管理和垃圾回收机制
1776
-
1777
- ### @ray-js/t-agent-plugin-aistream
1778
-
1779
- - **全新插件**:替代废弃的 assistant 插件,提供更强大的功能
1780
- - **连接优化**:新增 `earlyStart` 参数,支持提前建立连接,减少首次响应时间
1781
- - **Token 管理**:优化 Token 获取机制,支持自定义 `tokenOptions`
1782
- - **语音识别**:新增 AsrAgent 语音识别功能,支持实时语音转文字
1783
- - **Mock 机制**:改进的 mock 机制,支持更灵活的测试场景
1784
- - **多模态支持**:默认支持文本、图片、语音等多种输入类型
1785
-
1786
- ### @ray-js/t-agent-ui-ray
1787
-
1788
- - **消息操作栏**:新增 MessageActionBar 组件,支持多选操作和批量删除
1789
- - **虚拟滚动**:集成 LazyScrollView,提供虚拟滚动和性能优化
1790
- - **国际化系统**:完整的国际化系统,支持中文简繁体、英文、日文等 8 种语言
1791
- - **翻译 Hook**:新增 useTranslate Hook,简化多语言使用
1792
- - **自定义渲染**:扩展 renderOptions,支持更多自定义渲染选项
1793
- - **交互优化**:优化长按菜单功能,支持复制、删除、多选、点赞等操作
1794
- - **UI 完善**:新增多语言键值对,覆盖所有 UI 交互场景
package/README.md CHANGED
@@ -28,6 +28,8 @@ yarn add @ray-js/t-agent @ray-js/t-agent-plugin-aistream @ray-js/t-agent-ui-ray
28
28
  }
29
29
  ```
30
30
 
31
+ > When using MCP features, `AIStreamKit` must be version `2.2.1` or higher.
32
+
31
33
  ## package.json Dependency Requirements
32
34
 
33
35
  ```json
@@ -59,59 +61,70 @@ Implementing a chat page using ray UI
59
61
  import React from 'react';
60
62
  import { View } from '@ray-js/components';
61
63
  import { createChatAgent, withDebug, withUI } from '@ray-js/t-agent';
62
- import { ChatContainer, MessageInput, MessageList, MessageActionBar } from '@ray-js/t-agent-ui-ray';
64
+ import {
65
+ ChatContainer,
66
+ defaultRenderOptions,
67
+ MessageInput,
68
+ MessageList,
69
+ MessageActionBar,
70
+ } from '@ray-js/t-agent-ui-ray';
63
71
  import { withAIStream, withBuildIn } from '@ray-js/t-agent-plugin-aistream';
64
72
 
65
73
  const createAgent = () => {
66
- try {
67
- // The order of applying plugins matters
68
- const agent = createChatAgent(
69
- withUI(), // The withUI plugin provides default UI behavior, essential
70
- withAIStream({
71
- // Connects to the mini-program AI agent platform, essential in mini-program
72
- earlyStart: true, // Whether to establish connection during onAgentStart phase
73
- agentId: '', // Enter your agent ID
74
- }),
75
- withDebug(), // Enables logging to the console
76
- withBuildIn() // Provides built-in features
77
- );
74
+ const agent = createChatAgent(
75
+ withUI(),
76
+ withAIStream({
77
+ earlyStart: true,
78
+ agentId: 'your-agent-id',
79
+ tokenOptions: {
80
+ api: 'm.life.ai.agent.token.get',
81
+ version: '1.0',
82
+ extParams: {
83
+ dialogueMode: 1,
84
+ },
85
+ },
86
+ }),
87
+ withDebug(),
88
+ withBuildIn()
89
+ );
78
90
 
79
- // Lifecycle hooks for custom behavior registration
80
- const { onChatStart, createMessage, onChatResume, onError, onInputBlocksPush, session } = agent;
91
+ agent.plugins.aiStream.onUserDataRead((type, data, result) => {
92
+ if (type === 'start-event') {
93
+ result.userData = {
94
+ sessionAttributes: {
95
+ 'custom.param': {
96
+ 'custom.app.scene': {
97
+ value: 'chat-page',
98
+ },
99
+ },
100
+ },
101
+ };
102
+ }
103
+ });
81
104
 
82
- // Initialize chat with a message
83
- onChatStart(async result => {
84
- const hello = createMessage({
85
- role: 'assistant',
86
- });
105
+ const { onChatStart, createMessage } = agent;
87
106
 
88
- hello.bubble.setText('Hello, world!');
89
- result.messages.push(hello);
90
- // Persist message for display upon next entry
91
- await hello.persist();
107
+ onChatStart(async result => {
108
+ const hello = createMessage({
109
+ role: 'assistant',
92
110
  });
93
111
 
94
- // Message upon chat resume
95
- onChatResume(async result => {
96
- const welcomeBack = createMessage({
97
- role: 'assistant',
98
- });
112
+ hello.bubble.setText('Hello, world!');
113
+ result.messages.push(hello);
114
+ await hello.persist();
115
+ });
99
116
 
100
- welcomeBack.bubble.setText('Welcome back');
101
- result.messages.push(welcomeBack);
102
- await welcomeBack.persist();
103
- });
104
- return agent;
105
- } catch (error) {
106
- console.error('Agent creation failed:', error);
107
- throw error;
108
- }
117
+ return agent;
118
+ };
119
+
120
+ const renderOptions = {
121
+ ...defaultRenderOptions,
109
122
  };
110
123
 
111
124
  export default function ChatPage() {
112
125
  return (
113
126
  <View style={{ height: '100vh' }}>
114
- <ChatContainer createAgent={createAgent}>
127
+ <ChatContainer createAgent={createAgent} renderOptions={renderOptions}>
115
128
  <MessageList />
116
129
  <MessageInput />
117
130
  <MessageActionBar />
@@ -785,6 +798,8 @@ Methods:
785
798
  - `options.sendBy` Sender role, defaults to 'user'
786
799
  - `options.responseBy` Responder role, defaults to 'assistant'
787
800
  - `options.userData` Optional user data
801
+ - `agent.plugins.aiStream.removeMessage(message)` Delete a history message
802
+ - `agent.plugins.aiStream.clearAllMessages()` Clear all messages and local history in the current session
788
803
  - `agent.plugins.aiStream.getChatId()` Get the current session's chatId, returns Promise<string>
789
804
 
790
805
  Hooks:
@@ -800,7 +815,7 @@ Hooks:
800
815
  - `status` Message status
801
816
  - `result.text` Text content, can be modified
802
817
  - `onSkillCompose` Triggered when receiving skill data, used for handling skill rendering
803
- - `skill` Skill data array (ReceivedTextSkillPacketBody[])
818
+ - `skill` Current skill data (ReceivedTextSkillPacketBody)
804
819
  - `respMsg` Response message
805
820
  - `result.messages` Message list
806
821
  - `onSkillsEnd` Triggered when all skills processing is complete
@@ -821,8 +836,99 @@ Hooks:
821
836
  - `data.blocks` When type is 'start-event', contains the input blocks for this send
822
837
  - `result.userData` Returned user data object, will be merged and sent to the AI platform
823
838
 
839
+ ### withBuildIn Plugin
840
+
841
+ Offers built-in features including smart home device control and knowledge base search.
842
+
843
+ **Supported Skills**:
844
+
845
+ - **Smart Home**: Device control and scene management
846
+ - **Knowledge Base Search**: Related document display
847
+
848
+ ### withMCP Plugin
849
+
850
+ Provides MCP service exposure capability so the Agent can call tools implemented inside the mini-program, such as operating Bluetooth devices, calling custom APIs, or reading local files. Before using it, enable "Device MCP" on the agent node. In addition, MCP tool names must start with `device.`.
851
+
852
+ ![custom-var-config.jpg](https://static1.tuyacn.com/static/txp-ray-TAgent/custom-var-config.jpg)
853
+
854
+ **Prerequisites**:
855
+
856
+ - Must be used after `withAIStream`
857
+ - When using MCP features, `AIStreamKit` must be version `2.2.1` or higher
858
+ - `createServer` must synchronously return `McpServer`
859
+
860
+ Parameters:
861
+
862
+ - `createServer(agent)` Create the MCP service instance
863
+ - `createContext(event, agent)` Optional, append context for each MCP request
864
+
824
865
  **Usage Example**:
825
866
 
867
+ ```tsx
868
+ import { createChatAgent, withUI } from '@ray-js/t-agent';
869
+ import { McpServer, withAIStream, withMCP } from '@ray-js/t-agent-plugin-aistream';
870
+
871
+ const createAgent = () => {
872
+ const server = new McpServer({
873
+ name: 'demo-mcp',
874
+ version: '1.0.0',
875
+ });
876
+
877
+ server.registerTool(
878
+ 'device.echo',
879
+ {
880
+ description: 'Echo input arguments',
881
+ inputSchema: {
882
+ type: 'object',
883
+ properties: {
884
+ text: { type: 'string' },
885
+ },
886
+ },
887
+ },
888
+ async (args, context) => {
889
+ return {
890
+ content: [
891
+ {
892
+ type: 'text',
893
+ text: JSON.stringify({
894
+ text: args.text,
895
+ sessionId: context.sessionId,
896
+ }),
897
+ },
898
+ ],
899
+ };
900
+ }
901
+ );
902
+
903
+ return createChatAgent(
904
+ withUI(),
905
+ withAIStream({
906
+ agentId: 'your-agent-id',
907
+ }),
908
+ withMCP({
909
+ createServer: () => server,
910
+ createContext: event => ({
911
+ traceId: event.eventId,
912
+ }),
913
+ })
914
+ );
915
+ };
916
+ ```
917
+
918
+ After being enabled, the plugin will:
919
+
920
+ - Inject `sessionAttributes.deviceMcp` when creating a session
921
+ - Call the corresponding tool when receiving an `MCP_CMD` event
922
+ - Return the tool execution result to AIStream in JSON-RPC response format
923
+
924
+ ### Custom Variables
925
+
926
+ In some cases, you may need to pass custom variables to the agent for use in the workflow. You need to configure the custom variable references in the workflow first, then pass the custom variables from the mini-program:
927
+
928
+ ![custom-var-config.jpg](https://static1.tuyacn.com/static/txp-ray-TAgent/custom-var-config.jpg)
929
+
930
+ **Pass custom variables in the mini-program**:
931
+
826
932
  ```tsx
827
933
  const agent = createChatAgent(
828
934
  withUI(),
@@ -834,20 +940,28 @@ const agent = createChatAgent(
834
940
  agent.plugins.aiStream.onUserDataRead((type, data, result) => {
835
941
  // Pass user information when creating a session
836
942
  if (type === 'create-session') {
837
- result.sessionAttributes = {
838
- 'custom.param': {
839
- userName: { value: 'John' },
840
- userLevel: { value: 'Plus' },
943
+ result.userData = {
944
+ sessionAttributes: {
945
+ 'custom.param': {
946
+ 'custom.app.test_me': {
947
+ // The parameter name configured on the platform
948
+ value: `with value test me ${type} ${Date.now()}`,
949
+ },
950
+ },
841
951
  },
842
952
  };
843
953
  return;
844
954
  }
845
- // Pass dynamic context each time a message is sent
955
+ // Pass dynamic context every time a message is sent
846
956
  if (type === 'start-event') {
847
- result.eventAttributes = {
848
- 'custom.param': {
849
- timestamp: { value: Date.now() },
850
- pid: { value: '123456' },
957
+ result.userData = {
958
+ sessionAttributes: {
959
+ 'custom.param': {
960
+ 'custom.app.test_me': {
961
+ // The parameter name configured on the platform
962
+ value: `with value test me ${type} ${Date.now()}`,
963
+ },
964
+ },
851
965
  },
852
966
  };
853
967
  return;
@@ -855,15 +969,6 @@ agent.plugins.aiStream.onUserDataRead((type, data, result) => {
855
969
  });
856
970
  ```
857
971
 
858
- ### withBuildIn Plugin
859
-
860
- Offers built-in features including smart home device control and knowledge base search.
861
-
862
- **Supported Skills**:
863
-
864
- - **Smart Home**: Device control and scene management
865
- - **Knowledge Base Search**: Related document display
866
-
867
972
  ## Mock Mechanism
868
973
 
869
974
  To facilitate development, we offer a mock mechanism allowing mock data to be used without connecting to applet AI platforms.
@@ -913,6 +1018,92 @@ mock.hooks.hook('asrDetection', context => {
913
1018
  });
914
1019
  ```
915
1020
 
1021
+ ### mock MCP
1022
+
1023
+ The recommended approach is to decide which MCP tool to call in the mock `sendToAIStream` hook based on the input content, then directly run the full MCP call chain with `context.callMCPTool()`.
1024
+
1025
+ ```tsx
1026
+ import { mock } from '@ray-js/t-agent-plugin-aistream';
1027
+
1028
+ const getToolCall = (text: string) => {
1029
+ if (text.includes('error') || text.includes('failed') || text.includes('exception')) {
1030
+ return {
1031
+ name: 'device.home.energy.summary.fail',
1032
+ arguments: {
1033
+ reason: 'mock-error-case',
1034
+ },
1035
+ };
1036
+ }
1037
+
1038
+ if (text.includes('energy') || text.includes('power') || text.includes('data')) {
1039
+ return {
1040
+ name: 'device.home.energy.summary.get',
1041
+ arguments: {
1042
+ period: 'today',
1043
+ },
1044
+ };
1045
+ }
1046
+
1047
+ return {
1048
+ name: 'device.app.open',
1049
+ arguments: {
1050
+ category: 'music',
1051
+ },
1052
+ };
1053
+ };
1054
+
1055
+ export const setupMockMCP = () => {
1056
+ mock.hooks.hook('sendToAIStream', async context => {
1057
+ const text = context.data
1058
+ .filter(item => item.type === 'text')
1059
+ .map(item => item.text)
1060
+ .join(' ');
1061
+
1062
+ if (!/mcp|app|energy|power|data|error|failed|exception/i.test(text)) {
1063
+ return;
1064
+ }
1065
+
1066
+ const toolCall = getToolCall(text);
1067
+
1068
+ await context.writeText(`MCP mock is calling ${toolCall.name}...`);
1069
+
1070
+ try {
1071
+ const result = await context.callMCPTool(toolCall.name, toolCall.arguments, {
1072
+ delayMs: 80,
1073
+ });
1074
+ await context.writeText(
1075
+ `Tool ${toolCall.name} returned: ${
1076
+ typeof result === 'string' ? result : JSON.stringify(result == null ? {} : result)
1077
+ }`
1078
+ );
1079
+ } catch (error) {
1080
+ const message = error instanceof Error ? error.message : String(error);
1081
+ await context.writeText(`MCP mock received a tool error: ${message}`);
1082
+ }
1083
+
1084
+ await context.end();
1085
+ });
1086
+ };
1087
+ ```
1088
+
1089
+ Register it once before creating the agent:
1090
+
1091
+ ```tsx
1092
+ const createAgent = () => {
1093
+ setupMockMCP();
1094
+
1095
+ return createChatAgent(
1096
+ withUI(),
1097
+ withAIStream({
1098
+ agentId: 'your-agent-id',
1099
+ }),
1100
+ withMCP({
1101
+ createServer: () => server,
1102
+ })
1103
+ );
1104
+ };
1105
+ ```
1106
+
916
1107
  ## Additional Utils Tools (Currently under development)
917
1108
 
918
1109
  ### AbortController
@@ -1791,33 +1982,3 @@ Below are the built-in multilingual keys:
1791
1982
  | t-agent.error.stream-exists | Error message | Message sending error, please try again later |
1792
1983
  | t-agent.error.timeout | Error message | Send timeout |
1793
1984
  | t-agent.error.asr-empty | Error message | Speech recognition result is empty |
1794
-
1795
- # Change Log
1796
-
1797
- ## Version 0.2.x
1798
-
1799
- ### @ray-js/t-agent
1800
-
1801
- - **Hook Mechanism Enhancement**: Added new lifecycle hooks such as `onMessageFeedback` and `onClearHistory`
1802
- - **Message State Management**: Optimized message state management, providing more precise message state control
1803
- - **Error Handling**: Enhanced error handling mechanism, supporting more detailed error information and error classification
1804
- - **Performance Optimization**: Optimized memory management and garbage collection mechanisms
1805
-
1806
- ### @ray-js/t-agent-plugin-aistream
1807
-
1808
- - **New Plugin**: Replaces the deprecated assistant plugin, providing more powerful functionality
1809
- - **Connection Optimization**: Added `earlyStart` parameter, supporting early connection establishment to reduce first response time
1810
- - **Token Management**: Optimized Token acquisition mechanism, supporting custom `tokenOptions`
1811
- - **Speech Recognition**: Added AsrAgent speech recognition functionality, supporting real-time speech-to-text
1812
- - **Mock Mechanism**: Improved mock mechanism, supporting more flexible testing scenarios
1813
- - **Multi-modal Support**: Default support for various input types including text, images, and voice
1814
-
1815
- ### @ray-js/t-agent-ui-ray
1816
-
1817
- - **Message Action Bar**: Added MessageActionBar component, supporting multi-select operations and batch deletion
1818
- - **Virtual Scrolling**: Integrated LazyScrollView, providing virtual scrolling and performance optimization
1819
- - **Internationalization System**: Complete internationalization system, supporting 8 languages including Simplified Chinese, Traditional Chinese, English, Japanese, etc.
1820
- - **Translation Hook**: Added useTranslate Hook, simplifying multilingual usage
1821
- - **Custom Rendering**: Extended renderOptions, supporting more custom rendering options
1822
- - **Interaction Optimization**: Optimized long press menu functionality, supporting copy, delete, multi-select, like and other operations
1823
- - **UI Enhancement**: Added multilingual key-value pairs, covering all UI interaction scenarios
@@ -8,7 +8,8 @@ import "core-js/modules/web.dom-collections.iterator.js";
8
8
  import { ChatMessageStatus } from './types';
9
9
  import ChatTile from './ChatTile';
10
10
  import ChatBubbleTile from './ChatBubbleTile';
11
- import { deepCloneToPlainObject, deepMerge, generateId } from './utils';
11
+ import { deepCloneToPlainObject, generateId } from './utils';
12
+ import { deepmerge } from './deepmerge';
12
13
  import { getLogger } from './Logger';
13
14
  export const logger = getLogger('TAgent/ChatAgent');
14
15
  export default class ChatMessage {
@@ -146,7 +147,7 @@ export default class ChatMessage {
146
147
  if (override) {
147
148
  this.meta = meta;
148
149
  } else {
149
- this.meta = deepMerge(this.meta, meta);
150
+ this.meta = deepmerge(this.meta, meta);
150
151
  }
151
152
  return this;
152
153
  }
@@ -0,0 +1,27 @@
1
+ type PlainObject = Record<string, unknown>;
2
+ type PropertyKeyLike = string | symbol;
3
+ type KeyGetter = (value: PlainObject) => PropertyKeyLike[];
4
+ type IsMergeableObject = (value: unknown) => boolean;
5
+ type CloneFn = <T>(value: T) => T;
6
+ type DeepMergePairFn = <T, U>(target: T, source: U) => any;
7
+ type DeepMergeFn = (...args: any[]) => any;
8
+ type MergeArrayFn = (target: any[], source: any[]) => any[];
9
+ type CloneProtoObjectFn = <T extends PlainObject>(target: T) => T;
10
+ interface MergeArrayUtils {
11
+ clone: CloneFn;
12
+ deepmerge: DeepMergePairFn;
13
+ getKeys: KeyGetter;
14
+ isMergeableObject: IsMergeableObject;
15
+ }
16
+ interface DeepMergeOptions {
17
+ all?: boolean;
18
+ symbols?: boolean;
19
+ onlyDefinedProperties?: boolean;
20
+ cloneProtoObject?: CloneProtoObjectFn;
21
+ isMergeableObject?: IsMergeableObject;
22
+ mergeArray?: (utils: MergeArrayUtils) => MergeArrayFn;
23
+ }
24
+ declare function defaultIsMergeableObjectFactory(): IsMergeableObject;
25
+ export declare function deepmergeConstructor(options?: DeepMergeOptions): DeepMergeFn;
26
+ export declare const deepmerge: DeepMergeFn;
27
+ export { defaultIsMergeableObjectFactory as isMergeableObject };
@@ -0,0 +1,141 @@
1
+ import "core-js/modules/es.regexp.constructor.js";
2
+ import "core-js/modules/es.regexp.dot-all.js";
3
+ import "core-js/modules/es.regexp.exec.js";
4
+ const JSON_PROTO = Object.getPrototypeOf({});
5
+ function defaultIsMergeableObjectFactory() {
6
+ return function (value) {
7
+ return typeof value === 'object' && value !== null && !(value instanceof RegExp) && !(value instanceof Date);
8
+ };
9
+ }
10
+ export function deepmergeConstructor(options) {
11
+ function isNotPrototypeKey(value) {
12
+ return value !== 'constructor' && value !== 'prototype' && value !== '__proto__';
13
+ }
14
+ function cloneArray(value) {
15
+ let i = 0;
16
+ const il = value.length;
17
+ const result = new Array(il);
18
+ for (i; i < il; ++i) {
19
+ result[i] = clone(value[i]);
20
+ }
21
+ return result;
22
+ }
23
+ function cloneObject(target) {
24
+ const result = {};
25
+ if (cloneProtoObject && Object.getPrototypeOf(target) !== JSON_PROTO) {
26
+ return cloneProtoObject(target);
27
+ }
28
+ const targetKeys = getKeys(target);
29
+ let i;
30
+ let il;
31
+ let key;
32
+ for (i = 0, il = targetKeys.length; i < il; ++i) {
33
+ isNotPrototypeKey(key = targetKeys[i]) && (result[key] = clone(target[key]));
34
+ }
35
+ return result;
36
+ }
37
+ const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
38
+ const getKeys = options !== null && options !== void 0 && options.symbols ? function (value) {
39
+ const result = Object.keys(value);
40
+ const keys = Object.getOwnPropertySymbols(value);
41
+ for (let i = 0, il = keys.length; i < il; ++i) {
42
+ propertyIsEnumerable.call(value, keys[i]) && result.push(keys[i]);
43
+ }
44
+ return result;
45
+ } : value => Object.keys(value);
46
+ const cloneProtoObject = typeof (options === null || options === void 0 ? void 0 : options.cloneProtoObject) === 'function' ? options.cloneProtoObject : undefined;
47
+ const isMergeableObject = typeof (options === null || options === void 0 ? void 0 : options.isMergeableObject) === 'function' ? options.isMergeableObject : defaultIsMergeableObjectFactory();
48
+ const onlyDefinedProperties = (options === null || options === void 0 ? void 0 : options.onlyDefinedProperties) === true;
49
+ function isPrimitive(value) {
50
+ return typeof value !== 'object' || value === null;
51
+ }
52
+ const mergeArray = options && typeof options.mergeArray === 'function' ? options.mergeArray({
53
+ clone,
54
+ deepmerge: _deepmerge,
55
+ getKeys,
56
+ isMergeableObject
57
+ }) : function (target, source) {
58
+ const tl = target.length;
59
+ const sl = source.length;
60
+ let i = 0;
61
+ const result = new Array(tl + sl);
62
+ for (i; i < tl; ++i) {
63
+ result[i] = clone(target[i]);
64
+ }
65
+ for (i = 0; i < sl; ++i) {
66
+ result[i + tl] = clone(source[i]);
67
+ }
68
+ return result;
69
+ };
70
+ function clone(entry) {
71
+ return isMergeableObject(entry) ? Array.isArray(entry) ? cloneArray(entry) : cloneObject(entry) : entry;
72
+ }
73
+ function mergeObject(target, source) {
74
+ const result = {};
75
+ const targetKeys = getKeys(target);
76
+ const sourceKeys = getKeys(source);
77
+ let i;
78
+ let il;
79
+ let key;
80
+ for (i = 0, il = targetKeys.length; i < il; ++i) {
81
+ isNotPrototypeKey(key = targetKeys[i]) && sourceKeys.indexOf(key) === -1 && (result[key] = clone(target[key]));
82
+ }
83
+ for (i = 0, il = sourceKeys.length; i < il; ++i) {
84
+ if (!isNotPrototypeKey(key = sourceKeys[i])) {
85
+ continue;
86
+ }
87
+ if (key in target) {
88
+ if (targetKeys.indexOf(key) !== -1) {
89
+ if (cloneProtoObject && isMergeableObject(source[key]) && Object.getPrototypeOf(source[key]) !== JSON_PROTO) {
90
+ result[key] = cloneProtoObject(source[key]);
91
+ } else {
92
+ result[key] = _deepmerge(target[key], source[key]);
93
+ }
94
+ }
95
+ } else {
96
+ if (onlyDefinedProperties && typeof source[key] === 'undefined') {
97
+ continue;
98
+ }
99
+ result[key] = clone(source[key]);
100
+ }
101
+ }
102
+ return result;
103
+ }
104
+ function _deepmerge(target, source) {
105
+ if (onlyDefinedProperties && typeof source === 'undefined') {
106
+ return clone(target);
107
+ }
108
+ const sourceIsArray = Array.isArray(source);
109
+ const targetIsArray = Array.isArray(target);
110
+ if (isPrimitive(source)) {
111
+ return source;
112
+ }
113
+ if (!isMergeableObject(target)) {
114
+ return clone(source);
115
+ }
116
+ if (sourceIsArray && targetIsArray) {
117
+ return mergeArray(target, source);
118
+ }
119
+ if (sourceIsArray !== targetIsArray) {
120
+ return clone(source);
121
+ }
122
+ return mergeObject(target, source);
123
+ }
124
+ return options !== null && options !== void 0 && options.all ? function () {
125
+ switch (arguments.length) {
126
+ case 0:
127
+ return {};
128
+ case 1:
129
+ return clone(arguments.length <= 0 ? undefined : arguments[0]);
130
+ case 2:
131
+ return _deepmerge(arguments.length <= 0 ? undefined : arguments[0], arguments.length <= 1 ? undefined : arguments[1]);
132
+ }
133
+ let result = {};
134
+ for (let i = 0, il = arguments.length; i < il; ++i) {
135
+ result = _deepmerge(result, i < 0 || arguments.length <= i ? undefined : arguments[i]);
136
+ }
137
+ return result;
138
+ } : _deepmerge;
139
+ }
140
+ export const deepmerge = deepmergeConstructor();
141
+ export { defaultIsMergeableObjectFactory as isMergeableObject };
@@ -12,3 +12,4 @@ export { ChatAgent, ChatSession, StreamResponse, ChatTile, ChatBubbleTile, ChatM
12
12
  export * from './createChatAgent';
13
13
  export * from './types';
14
14
  export * from './json';
15
+ export * from './deepmerge';
@@ -11,4 +11,5 @@ export { createHooks, Hookable } from 'hookable';
11
11
  export { ChatAgent, ChatSession, StreamResponse, ChatTile, ChatBubbleTile, ChatMessage, generateId, getLogger, Logger, Emitter, EmitterEvent, isAbortError, shuffleWithSeed };
12
12
  export * from './createChatAgent';
13
13
  export * from './types';
14
- export * from './json';
14
+ export * from './json';
15
+ export * from './deepmerge';
@@ -1,5 +1,4 @@
1
1
  export declare function generateId(length?: number): string;
2
2
  export declare function deepCloneToPlainObject(obj: any, visited?: WeakMap<object, any>): any;
3
- export declare function deepMerge(target: any, source: any): any;
4
3
  export declare function isAbortError(reason: any): any;
5
4
  export declare function shuffleWithSeed<T>(array: T[], seed: string): T[];
@@ -86,29 +86,6 @@ export function deepCloneToPlainObject(obj) {
86
86
  }
87
87
  return clonedObj;
88
88
  }
89
- export function deepMerge(target, source) {
90
- if (typeof target !== 'object' || target === null) {
91
- return source; // 如果目标不是对象,直接返回来源
92
- }
93
- if (typeof source !== 'object' || source === null) {
94
- return target; // 如果来源不是对象,返回目标
95
- }
96
- for (const key in source) {
97
- if (Object.prototype.hasOwnProperty.call(source, key)) {
98
- const sourceValue = source[key];
99
- const targetValue = target[key];
100
-
101
- // 如果是对象,递归合并
102
- if (typeof sourceValue === 'object' && sourceValue !== null) {
103
- target[key] = deepMerge(Array.isArray(targetValue) ? [] : targetValue || {}, sourceValue);
104
- } else {
105
- // 否则直接赋值
106
- target[key] = sourceValue;
107
- }
108
- }
109
- }
110
- return target;
111
- }
112
89
  export function isAbortError(reason) {
113
90
  return reason && typeof Error !== 'undefined' && reason instanceof Error && reason.name === 'AbortError';
114
91
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/t-agent",
3
- "version": "0.2.7-beta.9",
3
+ "version": "0.2.8-beta.1",
4
4
  "author": "Tuya.inc",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -26,5 +26,5 @@
26
26
  "build": "ray build --type=component --output dist",
27
27
  "clean": "rimraf ./dist"
28
28
  },
29
- "gitHead": "31a0d88f88eddae8bfd802e4861c76cd02c07692"
29
+ "gitHead": "040499ca11aedda3246121f880807da0f8af3cc9"
30
30
  }