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

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
@@ -22,12 +22,21 @@ yarn add @ray-js/t-agent @ray-js/t-agent-plugin-aistream @ray-js/t-agent-ui-ray
22
22
  "DeviceKit": "4.6.1",
23
23
  "HomeKit": "3.4.0",
24
24
  "MiniKit": "3.12.1",
25
- "AIStreamKit": "1.0.0"
25
+ "AIStreamKit": "1.3.2"
26
26
  },
27
27
  "baseversion": "2.21.10"
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 |
39
+
31
40
  ## package.json 依赖要求
32
41
 
33
42
  ```json
@@ -771,6 +780,7 @@ const createAgent = () => {
771
780
  - `indexId` 索引 ID,默认为 'default'
772
781
  - `homeId` 家庭 ID,不填默认当前家庭
773
782
  - `earlyStart` 是否在 onAgentStart 阶段就建立连接
783
+ - `eventIdPrefix` eventId 前缀,用于方便云端调试和日志追踪
774
784
  - `tokenOptions` 获取 agent token 的参数
775
785
  - `api` API 接口名
776
786
  - `version` 接口版本
@@ -779,14 +789,30 @@ const createAgent = () => {
779
789
 
780
790
  方法:
781
791
 
782
- - `agent.plugins.aiStream.send` 向智能体发送一条消息
783
- - `agent.plugins.aiStream.chat` 向智能体发送一条消息,并生成提问 ChatMessage 对象和 AI 回答 ChatMessage 对象,流式更新
792
+ - `agent.plugins.aiStream.send(blocks, signal, userData)` 向智能体发送一条消息
793
+ - `blocks` 输入块数组
794
+ - `signal` 可选的 AbortSignal,用于中断请求
795
+ - `userData` 可选的用户数据,会附带在发送的消息中
796
+ - `agent.plugins.aiStream.chat(blocks, signal, options)` 向智能体发送一条消息,并生成提问 ChatMessage 对象和 AI 回答 ChatMessage 对象,流式更新
797
+ - `blocks` 输入块数组
798
+ - `signal` 可选的 AbortSignal
799
+ - `options.sendBy` 发送者角色,默认为 'user'
800
+ - `options.responseBy` 响应者角色,默认为 'assistant'
801
+ - `options.userData` 可选的用户数据
802
+ - `agent.plugins.aiStream.getChatId()` 获取当前会话的 chatId,返回 Promise<string>
784
803
 
785
804
  Hooks:
786
805
 
787
806
  - `onMessageParse` 当读取历史消息,解析消息时触发,可以在这个 Hook 里修改消息
788
807
  - `msgItem` 存储的消息对象
789
808
  - `result.messages` 解析后的消息列表
809
+ - `onChatMessageSent` 当用户消息和响应消息创建后触发
810
+ - `userMessage` 用户发送的消息
811
+ - `respMessage` AI 的响应消息
812
+ - `onTextCompose` 当收到文本数据时触发,用于处理文本的渲染
813
+ - `respMsg` 响应消息
814
+ - `status` 消息状态
815
+ - `result.text` 文本内容,可以修改
790
816
  - `onSkillCompose` 当收到技能数据时触发,用于处理技能的渲染
791
817
  - `skill` 技能数据数组 (ReceivedTextSkillPacketBody[])
792
818
  - `respMsg` 响应消息
@@ -801,6 +827,47 @@ Hooks:
801
827
  - `onCardsReceived` 当收到卡片数据时触发
802
828
  - `skills` 技能数据列表 (ReceivedTextSkillPacketBody[])
803
829
  - `result.cards` 卡片列表
830
+ - `onUserDataRead` **(0.2.x 新增)** 当需要读取用户自定义数据时触发,用于向 AI 平台传递额外的上下文信息
831
+ - `type` 触发类型,可以是 'create-session' 或 'start-event'
832
+ - `'create-session'` 创建会话时触发,只触发一次
833
+ - `'start-event'` 每次发送消息时触发
834
+ - `data` 上下文数据
835
+ - `data.blocks` 当 type 为 'start-event' 时,包含本次发送的输入块
836
+ - `result.userData` 返回的用户数据对象,会被合并后发送给 AI 平台
837
+
838
+ **使用示例**:
839
+
840
+ ```tsx
841
+ const agent = createChatAgent(
842
+ withUI(),
843
+ withAIStream({
844
+ agentId: 'your-agent-id',
845
+ })
846
+ );
847
+
848
+ agent.plugins.aiStream.onUserDataRead((type, data, result) => {
849
+ // 在创建会话时传递用户信息
850
+ if (type === 'create-session') {
851
+ result.sessionAttributes = {
852
+ 'custom.param': {
853
+ userName: { value: 'John' },
854
+ userLevel: { value: 'Plus' },
855
+ },
856
+ };
857
+ return;
858
+ }
859
+ // 在每次发送消息时传递动态上下文
860
+ if (type === 'start-event') {
861
+ result.eventAttributes = {
862
+ 'custom.param': {
863
+ timestamp: { value: Date.now() },
864
+ pid: { value: '123456' },
865
+ },
866
+ };
867
+ return;
868
+ }
869
+ });
870
+ ```
804
871
 
805
872
  ### withBuildIn 插件
806
873
 
package/README.md CHANGED
@@ -22,7 +22,7 @@ yarn add @ray-js/t-agent @ray-js/t-agent-plugin-aistream @ray-js/t-agent-ui-ray
22
22
  "DeviceKit": "4.6.1",
23
23
  "HomeKit": "3.4.0",
24
24
  "MiniKit": "3.12.1",
25
- "AIStreamKit": "1.0.0"
25
+ "AIStreamKit": "1.3.2"
26
26
  },
27
27
  "baseversion": "2.21.10"
28
28
  }
@@ -766,6 +766,7 @@ Parameters:
766
766
  - `indexId` Index ID, defaults to 'default'
767
767
  - `homeId` Home ID, defaults to current home if not provided
768
768
  - `earlyStart` Whether to establish connection during onAgentStart phase
769
+ - `eventIdPrefix` Event ID prefix for cloud debugging and log tracing
769
770
  - `tokenOptions` Parameters for getting agent token
770
771
  - `api` API interface name
771
772
  - `version` Interface version
@@ -774,14 +775,30 @@ Parameters:
774
775
 
775
776
  Methods:
776
777
 
777
- - `agent.plugins.aiStream.send`: Send a message to the agent
778
- - `agent.plugins.aiStream.chat`: Send a message to the agent while generating a ChatMessage object for the question and the AI's answer, updating in a streaming manner
778
+ - `agent.plugins.aiStream.send(blocks, signal, userData)` Send a message to the agent
779
+ - `blocks` Input block array
780
+ - `signal` Optional AbortSignal for interrupting requests
781
+ - `userData` Optional user data to be included with the message
782
+ - `agent.plugins.aiStream.chat(blocks, signal, options)` Send a message to the agent while generating a ChatMessage object for the question and the AI's answer, updating in a streaming manner
783
+ - `blocks` Input block array
784
+ - `signal` Optional AbortSignal
785
+ - `options.sendBy` Sender role, defaults to 'user'
786
+ - `options.responseBy` Responder role, defaults to 'assistant'
787
+ - `options.userData` Optional user data
788
+ - `agent.plugins.aiStream.getChatId()` Get the current session's chatId, returns Promise<string>
779
789
 
780
790
  Hooks:
781
791
 
782
792
  - `onMessageParse` Triggered when reading history messages and parsing them, allowing for message modification in this Hook
783
793
  - `msgItem` Stored message object
784
794
  - `result.messages` Parsed message list
795
+ - `onChatMessageSent` Triggered after user message and response message are created
796
+ - `userMessage` User's sent message
797
+ - `respMessage` AI's response message
798
+ - `onTextCompose` Triggered when receiving text data, used for handling text rendering
799
+ - `respMsg` Response message
800
+ - `status` Message status
801
+ - `result.text` Text content, can be modified
785
802
  - `onSkillCompose` Triggered when receiving skill data, used for handling skill rendering
786
803
  - `skill` Skill data array (ReceivedTextSkillPacketBody[])
787
804
  - `respMsg` Response message
@@ -796,6 +813,47 @@ Hooks:
796
813
  - `onCardsReceived` Triggered when receiving card data
797
814
  - `skills` Skill data list (ReceivedTextSkillPacketBody[])
798
815
  - `result.cards` Card list
816
+ - `onUserDataRead` **(0.2.x New)** Triggered when user custom data needs to be read, used to pass additional context information to the AI platform
817
+ - `type` Trigger type, can be 'create-session' or 'start-event'
818
+ - `'create-session'` Triggered when creating a session, only once
819
+ - `'start-event'` Triggered each time a message is sent
820
+ - `data` Context data
821
+ - `data.blocks` When type is 'start-event', contains the input blocks for this send
822
+ - `result.userData` Returned user data object, will be merged and sent to the AI platform
823
+
824
+ **Usage Example**:
825
+
826
+ ```tsx
827
+ const agent = createChatAgent(
828
+ withUI(),
829
+ withAIStream({
830
+ agentId: 'your-agent-id',
831
+ })
832
+ );
833
+
834
+ agent.plugins.aiStream.onUserDataRead((type, data, result) => {
835
+ // Pass user information when creating a session
836
+ if (type === 'create-session') {
837
+ result.sessionAttributes = {
838
+ 'custom.param': {
839
+ userName: { value: 'John' },
840
+ userLevel: { value: 'Plus' },
841
+ },
842
+ };
843
+ return;
844
+ }
845
+ // Pass dynamic context each time a message is sent
846
+ if (type === 'start-event') {
847
+ result.eventAttributes = {
848
+ 'custom.param': {
849
+ timestamp: { value: Date.now() },
850
+ pid: { value: '123456' },
851
+ },
852
+ };
853
+ return;
854
+ }
855
+ });
856
+ ```
799
857
 
800
858
  ### withBuildIn Plugin
801
859
 
@@ -107,7 +107,8 @@ export default class ChatAgent {
107
107
  await this.hooks.callHook('onAgentDispose');
108
108
  await this.session.dispose();
109
109
  this.started = false;
110
- for (const hook of this.pluginHooks) {
110
+ for (let i = 0; i < this.pluginHooks.length; i++) {
111
+ const hook = this.pluginHooks[i];
111
112
  hook.removeAllHooks();
112
113
  }
113
114
  await this.hooks.callHook('onAgentDispose:after');
@@ -216,7 +217,8 @@ export default class ChatAgent {
216
217
  }).bubble.setText(text).update();
217
218
  } else if (part.type === 'attachment' && attachmentCompose) {
218
219
  const list = (await attachmentCompose(message, part)) || [];
219
- for (const m of list) {
220
+ for (let i = 0; i < list.length; i++) {
221
+ const m = list[i];
220
222
  m.set({
221
223
  status: ChatMessageStatus.FINISH
222
224
  });
@@ -283,7 +285,8 @@ export default class ChatAgent {
283
285
  });
284
286
  this.hooks = createHooks();
285
287
  const _hooks = ['onAgentStart', 'onChatStart', 'onChatResume', 'onMessageListInit', 'onInputBlocksPush', 'onMessageChange', 'onMessagePersist', 'onTileEvent', 'onAgentDispose', 'onUserAbort', 'onError'];
286
- for (const hook of _hooks) {
288
+ for (let i = 0; i < _hooks.length; i++) {
289
+ const hook = _hooks[i];
287
290
  this[hook] = (fn, type) => {
288
291
  if (type === 'before') {
289
292
  return this.hooks.hook("".concat(hook, ":before"), fn);
@@ -1,5 +1,4 @@
1
1
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
- import "core-js/modules/web.dom-collections.iterator.js";
3
2
  import ChatTile from './ChatTile';
4
3
  import { BubbleTileStatus } from './types';
5
4
  import { generateId } from './utils';
@@ -56,7 +55,8 @@ export default class ChatBubbleTile extends ChatTile {
56
55
  }
57
56
  ensureTextTile() {
58
57
  if (!this.textTile) {
59
- for (const child of this.children) {
58
+ for (let i = 0; i < this.children.length; i++) {
59
+ const child = this.children[i];
60
60
  if (child.type === 'text') {
61
61
  this.textTile = child;
62
62
  break;
@@ -78,7 +78,8 @@ export default class ChatBubbleTile extends ChatTile {
78
78
  initWithInputBlocks(blocks) {
79
79
  this.textTile = null;
80
80
  this.children = [];
81
- for (const block of blocks) {
81
+ for (let i = 0; i < blocks.length; i++) {
82
+ const block = blocks[i];
82
83
  if (block.type === 'text') {
83
84
  if (!this.textTile) {
84
85
  this.textTile = new ChatTile(this.message, 'text', {
@@ -17,7 +17,8 @@ export default class ChatMessage {
17
17
  }
18
18
  get bubble() {
19
19
  if (!this._bubble) {
20
- for (const tile of this.tiles) {
20
+ for (let i = 0; i < this.tiles.length; i++) {
21
+ const tile = this.tiles[i];
21
22
  if (tile.type === 'bubble') {
22
23
  this._bubble = tile;
23
24
  break;
@@ -47,7 +48,8 @@ export default class ChatMessage {
47
48
  this.initTiles(null, tiles || []);
48
49
  }
49
50
  initTiles(root, tiles) {
50
- for (const tile of tiles) {
51
+ for (let i = 0; i < tiles.length; i++) {
52
+ const tile = tiles[i];
51
53
  let r;
52
54
  if (root) {
53
55
  r = root.addTile(tile.type, tile.data, tile.id);
@@ -81,7 +83,8 @@ export default class ChatMessage {
81
83
  }
82
84
  await this.agent.hooks.callHook('onMessageChange:before', 'update', this);
83
85
  await this.agent.hooks.callHook('onMessageChange', 'update', this);
84
- for (const tile of this.tiles) {
86
+ for (let i = 0; i < this.tiles.length; i++) {
87
+ const tile = this.tiles[i];
85
88
  this.agent.session.bindTile(tile);
86
89
  }
87
90
  await this.agent.hooks.callHook('onMessageChange:after', 'update', this);
@@ -111,7 +114,8 @@ export default class ChatMessage {
111
114
  });
112
115
  }
113
116
  setTilesLocked(locked) {
114
- for (const tile of this.tiles) {
117
+ for (let i = 0; i < this.tiles.length; i++) {
118
+ const tile = this.tiles[i];
115
119
  tile.setLocked(locked);
116
120
  }
117
121
  return this;
@@ -153,7 +157,8 @@ export default class ChatMessage {
153
157
  findTileByType(type) {
154
158
  const ret = [];
155
159
  const tiles = this.tiles;
156
- for (const tile of tiles) {
160
+ for (let i = 0; i < tiles.length; i++) {
161
+ const tile = tiles[i];
157
162
  if (tile.type === type) {
158
163
  ret.push(tile);
159
164
  }
@@ -1,6 +1,4 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
2
- import "core-js/modules/esnext.iterator.constructor.js";
3
- import "core-js/modules/esnext.iterator.for-each.js";
4
2
  import "core-js/modules/web.dom-collections.iterator.js";
5
3
  import { ChatMessageStatus } from './types';
6
4
  import { createHooks } from 'hookable';
@@ -36,41 +34,59 @@ export default class ChatSession {
36
34
  return ret;
37
35
  });
38
36
  _defineProperty(this, "initMessages", messages => {
39
- for (const message of messages) {
37
+ for (let i = 0; i < messages.length; i++) {
38
+ const message = messages[i];
40
39
  this.bindMessage(message);
41
- message.set({
42
- status: ChatMessageStatus.FINISH
43
- })._setIsShow();
40
+ // Optimization: Direct assignment to avoid object allocation and excess function calls
41
+ message.status = ChatMessageStatus.FINISH;
42
+ message._setIsShow();
44
43
  }
45
44
  });
46
45
  _defineProperty(this, "getTileById", id => {
47
46
  return this.tileIdIndex.get(id);
48
47
  });
49
48
  _defineProperty(this, "bindTile", tile => {
50
- this.tileIdIndex.set(tile.id, tile);
51
- for (const child of tile.children) {
52
- this.bindTile(child);
49
+ // Iterative approach to avoid recursion overhead
50
+ const stack = [tile];
51
+ while (stack.length > 0) {
52
+ const current = stack.pop();
53
+ this.tileIdIndex.set(current.id, current);
54
+
55
+ // Push children to stack
56
+ if (current.children && current.children.length > 0) {
57
+ for (let i = current.children.length - 1; i >= 0; i--) {
58
+ stack.push(current.children[i]);
59
+ }
60
+ }
53
61
  }
54
62
  });
55
63
  _defineProperty(this, "unbindTile", tile => {
56
- this.tileIdIndex.delete(tile.id);
57
- for (const child of tile.children) {
58
- this.unbindTile(child);
64
+ // Iterative approach
65
+ const stack = [tile];
66
+ while (stack.length > 0) {
67
+ const current = stack.pop();
68
+ this.tileIdIndex.delete(current.id);
69
+ if (current.children && current.children.length > 0) {
70
+ for (let i = current.children.length - 1; i >= 0; i--) {
71
+ stack.push(current.children[i]);
72
+ }
73
+ }
59
74
  }
60
75
  });
61
76
  _defineProperty(this, "bindMessage", message => {
62
77
  this.messages.set(message.id, message);
63
- for (const tile of message.tiles) {
78
+ for (let i = 0; i < message.tiles.length; i++) {
79
+ const tile = message.tiles[i];
64
80
  this.bindTile(tile);
65
81
  }
66
82
  });
67
83
  _defineProperty(this, "unbindMessage", message => {
68
84
  this.messages.delete(message.id);
69
- this.tileIdIndex.forEach(tile => {
70
- if (tile.message.id === message.id) {
71
- this.tileIdIndex.delete(tile.id);
72
- }
73
- });
85
+ // Optimization: Instead of full scan, just unbind tiles known to be in the message
86
+ for (let i = 0; i < message.tiles.length; i++) {
87
+ const tile = message.tiles[i];
88
+ this.unbindTile(tile);
89
+ }
74
90
  });
75
91
  _defineProperty(this, "onChange", fn => this.hooks.hook('onChange', fn));
76
92
  _defineProperty(this, "getLatestMessage", () => {
@@ -85,7 +101,8 @@ export default class ChatSession {
85
101
  this.agent = agent;
86
102
  this.hooks = createHooks();
87
103
  const hooks = ['onChange'];
88
- for (const hook of hooks) {
104
+ for (let i = 0; i < hooks.length; i++) {
105
+ const hook = hooks[i];
89
106
  this[hook] = fn => this.hooks.hook(hook, fn);
90
107
  }
91
108
  }
@@ -2,7 +2,6 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
3
3
  import "core-js/modules/esnext.iterator.constructor.js";
4
4
  import "core-js/modules/esnext.iterator.map.js";
5
- import "core-js/modules/web.dom-collections.iterator.js";
6
5
  import { generateId } from './utils';
7
6
  export default class ChatTile {
8
7
  constructor(message, type) {
@@ -20,7 +19,8 @@ export default class ChatTile {
20
19
  setLocked(value) {
21
20
  this.locked = value;
22
21
  // 递归锁定
23
- for (const child of this.children) {
22
+ for (let i = 0; i < this.children.length; i++) {
23
+ const child = this.children[i];
24
24
  child.setLocked(value);
25
25
  }
26
26
  return this;
@@ -61,7 +61,8 @@ export default class ChatTile {
61
61
  }
62
62
  findByType(type) {
63
63
  let ret = [];
64
- for (const tile of this.children) {
64
+ for (let i = 0; i < this.children.length; i++) {
65
+ const tile = this.children[i];
65
66
  if (tile.type === type) {
66
67
  ret.push(tile);
67
68
  }
@@ -1,4 +1,3 @@
1
- import "core-js/modules/web.dom-collections.iterator.js";
2
1
  import ChatAgent from './ChatAgent';
3
2
 
4
3
  // 提取单个插件的返回类型(排除 hooks)
@@ -7,19 +6,15 @@ import ChatAgent from './ChatAgent';
7
6
 
8
7
  export function createChatAgent() {
9
8
  const agent = new ChatAgent();
10
- for (var _len = arguments.length, plugins = new Array(_len), _key = 0; _key < _len; _key++) {
11
- plugins[_key] = arguments[_key];
12
- }
13
- for (const plugin of plugins) {
9
+ for (let i = 0; i < arguments.length; i++) {
10
+ const plugin = i < 0 || arguments.length <= i ? undefined : arguments[i];
14
11
  agent.applyPlugin(plugin);
15
12
  }
16
13
  return agent;
17
14
  }
18
15
  export function applyChatAgentPlugins(agent) {
19
- for (var _len2 = arguments.length, plugins = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
20
- plugins[_key2 - 1] = arguments[_key2];
21
- }
22
- for (const plugin of plugins) {
16
+ for (let i = 0; i < (arguments.length <= 1 ? 0 : arguments.length - 1); i++) {
17
+ const plugin = i + 1 < 1 || arguments.length <= i + 1 ? undefined : arguments[i + 1];
23
18
  agent.applyPlugin(plugin);
24
19
  }
25
20
  return agent;
@@ -4,10 +4,11 @@ import StreamResponse from './StreamResponse';
4
4
  import ChatTile from './ChatTile';
5
5
  import ChatBubbleTile from './ChatBubbleTile';
6
6
  import ChatMessage from './ChatMessage';
7
- import { generateId, safeParseJSON, isAbortError, shuffleWithSeed } from './utils';
7
+ import { generateId, isAbortError, shuffleWithSeed } from './utils';
8
8
  import Logger, { getLogger } from './Logger';
9
9
  import { Emitter, EmitterEvent } from './Emitter';
10
10
  export { createHooks, Hookable } from 'hookable';
11
- export { ChatAgent, ChatSession, StreamResponse, ChatTile, ChatBubbleTile, ChatMessage, generateId, safeParseJSON, getLogger, Logger, Emitter, EmitterEvent, isAbortError, shuffleWithSeed, };
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';
@@ -4,10 +4,11 @@ import StreamResponse from './StreamResponse';
4
4
  import ChatTile from './ChatTile';
5
5
  import ChatBubbleTile from './ChatBubbleTile';
6
6
  import ChatMessage from './ChatMessage';
7
- import { generateId, safeParseJSON, isAbortError, shuffleWithSeed } from './utils';
7
+ import { generateId, isAbortError, shuffleWithSeed } from './utils';
8
8
  import Logger, { getLogger } from './Logger';
9
9
  import { Emitter, EmitterEvent } from './Emitter';
10
10
  export { createHooks, Hookable } from 'hookable';
11
- export { ChatAgent, ChatSession, StreamResponse, ChatTile, ChatBubbleTile, ChatMessage, generateId, safeParseJSON, getLogger, Logger, Emitter, EmitterEvent, isAbortError, shuffleWithSeed };
11
+ export { ChatAgent, ChatSession, StreamResponse, ChatTile, ChatBubbleTile, ChatMessage, generateId, getLogger, Logger, Emitter, EmitterEvent, isAbortError, shuffleWithSeed };
12
12
  export * from './createChatAgent';
13
- export * from './types';
13
+ export * from './types';
14
+ export * from './json';
@@ -0,0 +1,7 @@
1
+ export declare function partialJSONParse<T = any>(input: string): T;
2
+ export interface JsonParseResult<T = any> {
3
+ value: T;
4
+ complete: boolean;
5
+ }
6
+ export declare function partialJSONParseWithStatus<T = any>(input: string): JsonParseResult<T>;
7
+ export declare function safeParseJSON<T = any>(str: any): T | undefined;
@@ -0,0 +1,540 @@
1
+ import "core-js/modules/es.regexp.exec.js";
2
+ import "core-js/modules/es.regexp.flags.js";
3
+ import "core-js/modules/es.string.trim.js";
4
+ import "core-js/modules/web.dom-collections.iterator.js";
5
+ /* eslint-disable no-continue,no-bitwise,no-param-reassign */
6
+ var TokenType = /*#__PURE__*/function (TokenType) {
7
+ TokenType[TokenType["Brace"] = 0] = "Brace";
8
+ TokenType[TokenType["Paren"] = 1] = "Paren";
9
+ TokenType[TokenType["Separator"] = 2] = "Separator";
10
+ TokenType[TokenType["Delimiter"] = 3] = "Delimiter";
11
+ TokenType[TokenType["String"] = 4] = "String";
12
+ TokenType[TokenType["Number"] = 5] = "Number";
13
+ TokenType[TokenType["Name"] = 6] = "Name";
14
+ return TokenType;
15
+ }(TokenType || {});
16
+ var TokenFlag = /*#__PURE__*/function (TokenFlag) {
17
+ TokenFlag[TokenFlag["None"] = 0] = "None";
18
+ TokenFlag[TokenFlag["IsKey"] = 1] = "IsKey";
19
+ TokenFlag[TokenFlag["IsDanglingKey"] = 2] = "IsDanglingKey";
20
+ return TokenFlag;
21
+ }(TokenFlag || {}); // Parallel arrays container
22
+ const C_BRACE_OPEN = 123; // {
23
+ const C_BRACE_CLOSE = 125; // }
24
+ const C_BRACKET_OPEN = 91; // [
25
+ const C_BRACKET_CLOSE = 93; // ]
26
+ const C_COLON = 58; // :
27
+ const C_COMMA = 44; // ,
28
+ const C_QUOTE = 34; // "
29
+ const C_BACKSLASH = 92; // \
30
+ const C_MINUS = 45; // -
31
+ const C_PLUS = 43; // +
32
+ const C_DOT = 46; // .
33
+ const C_e = 101; // e
34
+ const C_E = 69; // E
35
+ const C_SPACE = 32;
36
+ const C_TAB = 9;
37
+ const C_LF = 10;
38
+ const C_CR = 13;
39
+ function isDigit(code) {
40
+ return code >= 48 && code <= 57; // 0-9
41
+ }
42
+
43
+ // Lightweight stack for tracking context during tokenization
44
+ var ContextKind = /*#__PURE__*/function (ContextKind) {
45
+ ContextKind[ContextKind["Object"] = 0] = "Object";
46
+ ContextKind[ContextKind["Array"] = 1] = "Array";
47
+ return ContextKind;
48
+ }(ContextKind || {});
49
+ function tokenize(input) {
50
+ let current = 0;
51
+ const len = input.length;
52
+ const types = [];
53
+ const values = [];
54
+ const flags = [];
55
+
56
+ // Context stack for tracking JSON structure to flag keys
57
+ const stackKind = [];
58
+ const stackState = [];
59
+
60
+ // Helper to update context after a value
61
+ const afterValue = () => {
62
+ if (stackKind.length > 0) {
63
+ // Direct array access is faster
64
+ const idx = stackKind.length - 1;
65
+ if (stackKind[idx] === ContextKind.Object) {
66
+ stackState[idx] = 3; // Expect comma or end
67
+ } else {
68
+ stackState[idx] = 1; // Expect comma or end
69
+ }
70
+ }
71
+ };
72
+ while (current < len) {
73
+ const code = input.charCodeAt(current);
74
+ if (code === C_SPACE || code === C_TAB || code === C_LF || code === C_CR) {
75
+ current++;
76
+ continue;
77
+ }
78
+ if (code === C_BRACE_OPEN) {
79
+ types.push(TokenType.Brace);
80
+ values.push('{');
81
+ flags.push(TokenFlag.None);
82
+ stackKind.push(ContextKind.Object);
83
+ stackState.push(0); // Expect key
84
+ current++;
85
+ continue;
86
+ }
87
+ if (code === C_BRACE_CLOSE) {
88
+ types.push(TokenType.Brace);
89
+ values.push('}');
90
+ flags.push(TokenFlag.None);
91
+ stackKind.pop();
92
+ stackState.pop();
93
+ afterValue(); // Closing brace acts like a value completion for parent
94
+ current++;
95
+ continue;
96
+ }
97
+ if (code === C_BRACKET_OPEN) {
98
+ types.push(TokenType.Paren);
99
+ values.push('[');
100
+ flags.push(TokenFlag.None);
101
+ stackKind.push(ContextKind.Array);
102
+ stackState.push(0); // Expect value
103
+ current++;
104
+ continue;
105
+ }
106
+ if (code === C_BRACKET_CLOSE) {
107
+ types.push(TokenType.Paren);
108
+ values.push(']');
109
+ flags.push(TokenFlag.None);
110
+ stackKind.pop();
111
+ stackState.pop();
112
+ afterValue(); // Closing bracket acts like a value completion for parent
113
+ current++;
114
+ continue;
115
+ }
116
+ if (code === C_COLON) {
117
+ types.push(TokenType.Separator);
118
+ values.push(':');
119
+ flags.push(TokenFlag.None);
120
+ if (stackKind.length > 0) {
121
+ const idx = stackKind.length - 1;
122
+ if (stackKind[idx] === ContextKind.Object) {
123
+ stackState[idx] = 2; // Expect value
124
+ }
125
+ }
126
+ current++;
127
+ continue;
128
+ }
129
+ if (code === C_COMMA) {
130
+ types.push(TokenType.Delimiter);
131
+ values.push(',');
132
+ flags.push(TokenFlag.None);
133
+ if (stackKind.length > 0) {
134
+ const idx = stackKind.length - 1;
135
+ if (stackKind[idx] === ContextKind.Object) stackState[idx] = 0; // Expect key next
136
+ else stackState[idx] = 0; // Expect value next
137
+ }
138
+ current++;
139
+ continue;
140
+ }
141
+ if (code === C_QUOTE) {
142
+ let flag = TokenFlag.None;
143
+ let isKeyContext = false;
144
+ if (stackKind.length > 0) {
145
+ const idx = stackKind.length - 1;
146
+ if (stackKind[idx] === ContextKind.Object && stackState[idx] === 0) {
147
+ isKeyContext = true;
148
+ flag |= TokenFlag.IsKey;
149
+ stackState[idx] = 1; // Expect colon next
150
+ }
151
+ }
152
+ if (!isKeyContext) {
153
+ afterValue();
154
+ }
155
+ const start = current;
156
+ current++; // skip opening quote
157
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
158
+
159
+ // Fast scan
160
+ while (current < len) {
161
+ const c = input.charCodeAt(current);
162
+ if (c === C_QUOTE) break;
163
+ if (c === C_BACKSLASH) {
164
+ current += 2; // skip backslash and next char
165
+ } else {
166
+ current++;
167
+ }
168
+ }
169
+ if (current >= len) {
170
+ // Unterminated string
171
+ if (flag & TokenFlag.IsKey) {
172
+ flag |= TokenFlag.IsDanglingKey;
173
+ }
174
+ types.push(TokenType.String);
175
+ values.push(input.slice(start + 1, current));
176
+ flags.push(flag);
177
+ break; // End of input
178
+ }
179
+
180
+ // Found closing quote
181
+ const contentEnd = current;
182
+ current++; // consume closing quote
183
+
184
+ types.push(TokenType.String);
185
+ values.push(input.slice(start + 1, contentEnd));
186
+ flags.push(flag);
187
+ continue;
188
+ }
189
+
190
+ // Numbers
191
+ if (isDigit(code) || code === C_MINUS) {
192
+ afterValue();
193
+ const start = current;
194
+ if (code === C_MINUS) current++;
195
+ while (current < len) {
196
+ const c = input.charCodeAt(current);
197
+ if (isDigit(c) || c === C_DOT || c === C_e || c === C_E || c === C_PLUS || c === C_MINUS) {
198
+ current++;
199
+ } else {
200
+ break;
201
+ }
202
+ }
203
+ types.push(TokenType.Number);
204
+ values.push(input.slice(start, current));
205
+ flags.push(TokenFlag.None);
206
+ continue;
207
+ }
208
+
209
+ // true / false / null
210
+ if (code === 116 /* t */) {
211
+ if (input.startsWith('true', current)) {
212
+ afterValue();
213
+ types.push(TokenType.Name);
214
+ values.push('true');
215
+ flags.push(TokenFlag.None);
216
+ current += 4;
217
+ continue;
218
+ }
219
+ }
220
+ if (code === 102 /* f */) {
221
+ if (input.startsWith('false', current)) {
222
+ afterValue();
223
+ types.push(TokenType.Name);
224
+ values.push('false');
225
+ flags.push(TokenFlag.None);
226
+ current += 5;
227
+ continue;
228
+ }
229
+ }
230
+ if (code === 110 /* n */) {
231
+ if (input.startsWith('null', current)) {
232
+ afterValue();
233
+ types.push(TokenType.Name);
234
+ values.push('null');
235
+ flags.push(TokenFlag.None);
236
+ current += 4;
237
+ continue;
238
+ }
239
+ }
240
+
241
+ // Invalid or unknown
242
+ if (code >= 65 && code <= 90 ||
243
+ // A-Z
244
+ code >= 97 && code <= 122 // a-z
245
+ ) {
246
+ let value = '';
247
+ while (current < len) {
248
+ const c = input.charCodeAt(current);
249
+ if (c >= 65 && c <= 90 || c >= 97 && c <= 122) {
250
+ value += input[current];
251
+ current++;
252
+ } else {
253
+ break;
254
+ }
255
+ }
256
+ throw new Error("Invalid token: ".concat(value, " is not a valid token!"));
257
+ }
258
+ current++;
259
+ }
260
+ return {
261
+ types,
262
+ values,
263
+ flags
264
+ };
265
+ }
266
+ function strip(tokens) {
267
+ const {
268
+ types,
269
+ values,
270
+ flags
271
+ } = tokens;
272
+ if (types.length === 0) return tokens;
273
+ while (types.length > 0) {
274
+ const idx = types.length - 1;
275
+ const t = types[idx];
276
+ const v = values[idx];
277
+ const f = flags[idx];
278
+ if (t === TokenType.Separator) {
279
+ types.pop();
280
+ values.pop();
281
+ flags.pop();
282
+ continue;
283
+ }
284
+ if (t === TokenType.Number) {
285
+ // Check for trailing dot or minus
286
+ const lastChar = v.charCodeAt(v.length - 1);
287
+ if (lastChar === C_DOT || lastChar === C_MINUS) {
288
+ types.pop();
289
+ values.pop();
290
+ flags.pop();
291
+ continue;
292
+ }
293
+ break;
294
+ }
295
+ if (t === TokenType.String) {
296
+ if (f & TokenFlag.IsKey) {
297
+ // Dangling key
298
+ // Check "aggressive" strip for empty objects logic from original
299
+ const n = types.length;
300
+ if (n >= 2) {
301
+ const t2Type = types[n - 2];
302
+ const t2Val = values[n - 2];
303
+ if (t2Type === TokenType.Brace && t2Val === '{') {
304
+ if (n >= 3 && types[n - 3] === TokenType.Delimiter) {
305
+ // , { "key" -> pop 3
306
+ types.length -= 3;
307
+ values.length -= 3;
308
+ flags.length -= 3;
309
+ continue;
310
+ }
311
+ }
312
+ if (t2Type === TokenType.Paren && t2Val === '[') {
313
+ if (n >= 3 && types[n - 3] === TokenType.Delimiter) {
314
+ // , [ "key" -> pop 3
315
+ types.length -= 3;
316
+ values.length -= 3;
317
+ flags.length -= 3;
318
+ continue;
319
+ }
320
+ }
321
+ }
322
+ types.pop();
323
+ values.pop();
324
+ flags.pop();
325
+ continue;
326
+ }
327
+ break;
328
+ }
329
+ if (t === TokenType.Delimiter) {
330
+ types.pop();
331
+ values.pop();
332
+ flags.pop();
333
+ continue;
334
+ }
335
+ if (t === TokenType.Brace) {
336
+ if (v === '{') {
337
+ if (types.length >= 2 && types[types.length - 2] === TokenType.Delimiter) {
338
+ types.length -= 2;
339
+ values.length -= 2;
340
+ flags.length -= 2;
341
+ continue;
342
+ }
343
+ }
344
+ break;
345
+ }
346
+ if (t === TokenType.Paren) {
347
+ if (v === '[') {
348
+ if (types.length >= 2 && types[types.length - 2] === TokenType.Delimiter) {
349
+ types.length -= 2;
350
+ values.length -= 2;
351
+ flags.length -= 2;
352
+ continue;
353
+ }
354
+ }
355
+ break;
356
+ }
357
+ break;
358
+ }
359
+ return tokens;
360
+ }
361
+ function unstrip(tokens) {
362
+ const {
363
+ types,
364
+ values,
365
+ flags
366
+ } = tokens;
367
+ const stack = []; // Stack of expected closing chars
368
+
369
+ // Forward pass to build stack
370
+ const len = types.length;
371
+ for (let i = 0; i < len; i++) {
372
+ const t = types[i];
373
+ const v = values[i];
374
+ if (t === TokenType.Brace) {
375
+ if (v === '{') {
376
+ stack.push('}');
377
+ } else if (v === '}') {
378
+ // Optimistic matching: just pop the top expectation
379
+ // If stack is empty or mismatch, we just assume it closes the last open
380
+ stack.pop();
381
+ }
382
+ } else if (t === TokenType.Paren) {
383
+ if (v === '[') {
384
+ stack.push(']');
385
+ } else if (v === ']') {
386
+ stack.pop();
387
+ }
388
+ }
389
+ }
390
+
391
+ // Reverse complete
392
+ while (stack.length > 0) {
393
+ const char = stack.pop();
394
+ if (char === '}') {
395
+ types.push(TokenType.Brace);
396
+ values.push('}');
397
+ flags.push(TokenFlag.None);
398
+ } else {
399
+ types.push(TokenType.Paren);
400
+ values.push(']');
401
+ flags.push(TokenFlag.None);
402
+ }
403
+ }
404
+ return tokens;
405
+ }
406
+ function generate(tokens) {
407
+ const {
408
+ types,
409
+ values
410
+ } = tokens;
411
+ const parts = [];
412
+ const len = types.length;
413
+ for (let i = 0; i < len; i++) {
414
+ const t = types[i];
415
+ const v = values[i];
416
+
417
+ // Trailing comma check
418
+ if (t === TokenType.Delimiter && i + 1 < len) {
419
+ const nextT = types[i + 1];
420
+ const nextV = values[i + 1];
421
+ if (nextT === TokenType.Brace && nextV === '}' || nextT === TokenType.Paren && nextV === ']') {
422
+ // Check if prev was opening brace/paren
423
+ if (i > 0) {
424
+ const prevT = types[i - 1];
425
+ const prevV = values[i - 1];
426
+ if (prevT === TokenType.Brace && prevV === '{' || prevT === TokenType.Paren && prevV === '[') {
427
+ parts.push(v);
428
+ continue;
429
+ }
430
+ }
431
+ continue; // Skip trailing comma
432
+ }
433
+ }
434
+ if (t === TokenType.String) {
435
+ parts.push('"' + v + '"');
436
+ } else {
437
+ parts.push(v);
438
+ }
439
+ }
440
+ return parts.join('');
441
+ }
442
+
443
+ /** * Best-effort partial JSON parser: tries to complete dangling structures * and then JSON.parse */
444
+ function stripCodeFence(input) {
445
+ if (typeof input !== 'string') return input;
446
+ const trimmed = input.trim();
447
+
448
+ // 只处理以 ``` 或 ~~~ 开头的
449
+ const fenceMatch = trimmed.match(/^(```|~~~)/);
450
+ if (!fenceMatch) return input;
451
+ const fence = fenceMatch[1]; // ``` 或 ~~~
452
+
453
+ // 找到第一行结束位置(开头 fence 那一整行,包括语言信息)
454
+ const firstNewlineIndex = trimmed.indexOf('\n');
455
+ if (firstNewlineIndex === -1) {
456
+ // 只有一行,比如 "```json" 这种,说明没内容
457
+ return '';
458
+ }
459
+
460
+ // 去掉开头那一行 fence + 语言说明,剩下正文+结尾 fence+垃圾
461
+ const body = trimmed.slice(firstNewlineIndex + 1);
462
+
463
+ // 在正文中找最后一个同类型 fence
464
+ const lastFenceIndex = body.lastIndexOf(fence);
465
+ if (lastFenceIndex === -1) {
466
+ // 没有闭合 fence,就当只有开头 fence,剩下全是正文
467
+ return body.trim();
468
+ }
469
+
470
+ // 截到最后一个 fence 之前,后面统统当垃圾扔掉
471
+ const content = body.slice(0, lastFenceIndex);
472
+ return content.trim();
473
+ }
474
+ export function partialJSONParse(input) {
475
+ if (!input || typeof input !== 'string') {
476
+ return {};
477
+ }
478
+ // Pipeline
479
+ const tokens = tokenize(input);
480
+ const cleaned = strip(tokens);
481
+ const closed = unstrip(cleaned);
482
+ // normalizeTrailingCommas is integrated into generate
483
+ const jsonish = generate(closed);
484
+ return JSON.parse(jsonish);
485
+ }
486
+ export function partialJSONParseWithStatus(input) {
487
+ if (typeof input !== 'string') {
488
+ return {
489
+ value: {},
490
+ complete: true
491
+ };
492
+ }
493
+ input = stripCodeFence(input.trim());
494
+ if (input === '') {
495
+ return {
496
+ value: {},
497
+ complete: false
498
+ };
499
+ }
500
+
501
+ // 1) 先尝试“原文”是否就是完整 JSON
502
+ try {
503
+ const direct = JSON.parse(input);
504
+ return {
505
+ value: direct,
506
+ complete: true
507
+ };
508
+ } catch (_unused) {
509
+ // noop,继续走容错流程
510
+ }
511
+
512
+ // 2) 走 partial 流程
513
+ try {
514
+ const value = partialJSONParse(input);
515
+ // 既然原文 parse 失败,则这是“修复后”才能成功,标记为不完整
516
+ return {
517
+ value,
518
+ complete: false
519
+ };
520
+ } catch (e) {
521
+ // 按你的“尽力而为”语义:失败就返回空对象 & 不完整
522
+ return {
523
+ value: {},
524
+ complete: false
525
+ };
526
+ }
527
+ }
528
+ export function safeParseJSON(str) {
529
+ if (str && typeof str === 'string') {
530
+ try {
531
+ return JSON.parse(str);
532
+ } catch (e) {
533
+ console.warn('safeParseJSON error:', e);
534
+ }
535
+ }
536
+ if (str && typeof str === 'object') {
537
+ return str;
538
+ }
539
+ return undefined;
540
+ }
@@ -1,6 +1,5 @@
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 safeParseJSON<T = any>(str: any): T | undefined;
4
3
  export declare function deepMerge(target: any, source: any): any;
5
4
  export declare function isAbortError(reason: any): any;
6
5
  export declare function shuffleWithSeed<T>(array: T[], seed: string): T[];
@@ -86,19 +86,6 @@ export function deepCloneToPlainObject(obj) {
86
86
  }
87
87
  return clonedObj;
88
88
  }
89
- export function safeParseJSON(str) {
90
- if (str && typeof str === 'string') {
91
- try {
92
- return JSON.parse(str);
93
- } catch (e) {
94
- console.warn('safeParseJSON error:', e);
95
- }
96
- }
97
- if (str && typeof str === 'object') {
98
- return str;
99
- }
100
- return undefined;
101
- }
102
89
  export function deepMerge(target, source) {
103
90
  if (typeof target !== 'object' || target === null) {
104
91
  return source; // 如果目标不是对象,直接返回来源
@@ -11,7 +11,7 @@ export interface UIEventMap {
11
11
  scrollToBottom: {
12
12
  animation?: boolean;
13
13
  follow?: boolean;
14
- tickValue?: number;
14
+ scene?: string;
15
15
  };
16
16
  sendMessage: {
17
17
  blocks: InputBlock[];
@@ -1,6 +1,5 @@
1
1
  import "core-js/modules/esnext.iterator.constructor.js";
2
2
  import "core-js/modules/esnext.iterator.map.js";
3
- import "core-js/modules/web.dom-collections.iterator.js";
4
3
  import { Emitter, EmitterEvent } from '../chat';
5
4
  import { createHooks } from 'hookable';
6
5
  export function withUI() {
@@ -33,7 +32,8 @@ export function withUI() {
33
32
  }, 'after');
34
33
  agent.onAgentDispose(() => {
35
34
  const types = Object.keys(emitter.listeners);
36
- for (const type of types) {
35
+ for (let i = 0; i < types.length; i++) {
36
+ const type = types[i];
37
37
  delete emitter.listeners[type];
38
38
  }
39
39
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/t-agent",
3
- "version": "0.2.7-beta.1",
3
+ "version": "0.2.7-beta.11",
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": "d7754f9879a7ec837eb343ac759a08734cc789d3"
29
+ "gitHead": "10d81d0352c88b6b2eff7168ed046a065416dc4f"
30
30
  }