@ray-js/t-agent 0.2.7-beta.6 → 0.2.7-beta.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.
@@ -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
  }
@@ -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; // 如果目标不是对象,直接返回来源
@@ -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.6",
3
+ "version": "0.2.7-beta.8",
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": "8c74cd727802f09ac6294a88ee72464fc39db49c"
29
+ "gitHead": "31a0d88f88eddae8bfd802e4861c76cd02c07692"
30
30
  }