@agentscope-ai/chat 1.1.65 → 1.1.67

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.
Files changed (46) hide show
  1. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Actions.tsx +8 -4
  2. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Builder.tsx +7 -1
  3. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Card.tsx +5 -1
  4. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Actions.tsx +1 -1
  5. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Builder.tsx +55 -4
  6. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Message.tsx +2 -1
  7. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Tool.tsx +28 -5
  8. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/types.tsx +1 -0
  9. package/components/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.tsx +129 -33
  10. package/components/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatRequest.tsx +74 -39
  11. package/components/AgentScopeRuntimeWebUI/core/types/IChatAnywhere.ts +10 -0
  12. package/components/DefaultCards/Files/index.tsx +5 -1
  13. package/components/Markdown/Markdown.tsx +2 -1
  14. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Actions.js +8 -1
  15. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Builder.js +4 -0
  16. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Card.js +8 -2
  17. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Actions.js +1 -1
  18. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Builder.js +80 -12
  19. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Message.js +6 -1
  20. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Tool.js +29 -4
  21. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/types.d.ts +1 -0
  22. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.d.ts +1 -1
  23. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.js +167 -68
  24. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatRequest.d.ts +7 -3
  25. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatRequest.js +151 -117
  26. package/lib/AgentScopeRuntimeWebUI/core/types/IChatAnywhere.d.ts +15 -0
  27. package/lib/DefaultCards/Files/index.js +5 -1
  28. package/lib/Markdown/Markdown.js +1 -0
  29. package/package.json +1 -1
  30. package/bin/starter_webui/README.md +0 -75
  31. package/bin/starter_webui/eslint.config.js +0 -28
  32. package/bin/starter_webui/index.html +0 -12
  33. package/bin/starter_webui/package.json +0 -34
  34. package/bin/starter_webui/src/App.tsx +0 -20
  35. package/bin/starter_webui/src/components/Chat/OptionsPanel/FormItem.tsx +0 -37
  36. package/bin/starter_webui/src/components/Chat/OptionsPanel/OptionsEditor.tsx +0 -160
  37. package/bin/starter_webui/src/components/Chat/OptionsPanel/defaultConfig.ts +0 -41
  38. package/bin/starter_webui/src/components/Chat/OptionsPanel/index.tsx +0 -27
  39. package/bin/starter_webui/src/components/Chat/index.tsx +0 -45
  40. package/bin/starter_webui/src/components/Chat/sessionApi/index.ts +0 -53
  41. package/bin/starter_webui/src/main.tsx +0 -9
  42. package/bin/starter_webui/src/vite-env.d.ts +0 -4
  43. package/bin/starter_webui/tsconfig.app.json +0 -24
  44. package/bin/starter_webui/tsconfig.json +0 -7
  45. package/bin/starter_webui/tsconfig.node.json +0 -22
  46. package/bin/starter_webui/vite.config.ts +0 -11
@@ -11,6 +11,10 @@ interface UseChatRequestOptions {
11
11
  request?: IAgentScopeRuntimeWebUIMessage;
12
12
  response?: IAgentScopeRuntimeWebUIMessage;
13
13
  abortController?: AbortController;
14
+ /** Active request id, maintained by the controller. Incrementing it invalidates any in-flight SSE. */
15
+ activeRequestId: number;
16
+ /** Session id snapshot for the active request. */
17
+ activeSessionId?: string;
14
18
  }>;
15
19
  updateMessage: (message: IAgentScopeRuntimeWebUIMessage) => void;
16
20
  getCurrentSessionId: () => string;
@@ -18,13 +22,13 @@ interface UseChatRequestOptions {
18
22
  }
19
23
 
20
24
  /**
21
- * 处理 API 请求和流式响应的 Hook
25
+ * Hook for handling API requests and streaming SSE responses.
22
26
  */
23
27
  export default function useChatRequest(options: UseChatRequestOptions) {
24
28
  const { currentQARef, updateMessage, getCurrentSessionId, onFinish } = options;
25
29
  const apiOptions = useChatAnywhereOptions(v => v.api);
26
30
 
27
- // 使用 ref 保存最新的 apiOptions,避免闭包陷阱
31
+ // Keep apiOptions in a ref to avoid stale closure issues
28
32
  const apiOptionsRef = useRef(apiOptions);
29
33
 
30
34
  useEffect(() => {
@@ -57,7 +61,11 @@ export default function useChatRequest(options: UseChatRequestOptions) {
57
61
  }, [])
58
62
 
59
63
 
60
- const processSSEResponse = useCallback(async (response: Response) => {
64
+ const processSSEResponse = useCallback(async (
65
+ response: Response,
66
+ myRequestId: number,
67
+ mySessionId?: string,
68
+ ) => {
61
69
  const currentApiOptions = apiOptionsRef.current;
62
70
  const agentScopeRuntimeResponseBuilder = new AgentScopeRuntimeResponseBuilder({
63
71
  id: '',
@@ -65,6 +73,19 @@ export default function useChatRequest(options: UseChatRequestOptions) {
65
73
  created_at: 0,
66
74
  });
67
75
 
76
+ /**
77
+ * Guard: check whether this SSE stream is still the active request.
78
+ * If any of the following is true, writing should stop immediately:
79
+ * - requestId mismatch: user cancelled / sent new message / switched session
80
+ * - sessionId mismatch: session was switched away, prevents cross-session leakage
81
+ */
82
+ const isStillActive = () => {
83
+ if (currentQARef.current.activeRequestId !== myRequestId) return false;
84
+ if (mySessionId && currentQARef.current.activeSessionId &&
85
+ currentQARef.current.activeSessionId !== mySessionId) return false;
86
+ return true;
87
+ };
88
+
68
89
  if (!response.ok) {
69
90
  try {
70
91
  const data = await response.json();
@@ -79,16 +100,18 @@ export default function useChatRequest(options: UseChatRequestOptions) {
79
100
  message: JSON.stringify(data),
80
101
  });
81
102
 
82
- currentQARef.current.response.cards = [
83
- {
84
- code: 'AgentScopeRuntimeResponseCard',
85
- data: res,
86
- }
87
- ];
103
+ if (isStillActive() && currentQARef.current.response) {
104
+ currentQARef.current.response.cards = [
105
+ {
106
+ code: 'AgentScopeRuntimeResponseCard',
107
+ data: res,
108
+ }
109
+ ];
110
+ }
88
111
  } catch {
89
112
  // Ignore JSON parse errors — still call onFinish to reset loading state
90
113
  }
91
- onFinish();
114
+ if (isStillActive()) onFinish();
92
115
  return;
93
116
  }
94
117
 
@@ -99,22 +122,23 @@ export default function useChatRequest(options: UseChatRequestOptions) {
99
122
  readableStream: response.body,
100
123
  signal: abortSignal,
101
124
  })) {
125
+ // Primary guard: if this SSE is no longer active, stop immediately
126
+ // to prevent ghost writes into a different session/request.
127
+ if (!isStillActive()) break;
128
+
102
129
  if (currentQARef.current.response?.msgStatus === 'interrupted') {
103
130
  currentQARef.current.abortController?.abort();
104
- if (currentApiOptions.cancel) {
105
- currentApiOptions.cancel({
106
- session_id: getCurrentSessionId(),
107
- });
131
+ // Cancel was already sent by handleCancel; don't repeat it here.
132
+
133
+ if (isStillActive() && currentQARef.current.response) {
134
+ currentQARef.current.response.cards = [
135
+ {
136
+ code: 'AgentScopeRuntimeResponseCard',
137
+ data: agentScopeRuntimeResponseBuilder.cancel(),
138
+ }
139
+ ];
140
+ updateMessage(currentQARef.current.response);
108
141
  }
109
-
110
- currentQARef.current.response.cards = [
111
- {
112
- code: 'AgentScopeRuntimeResponseCard',
113
- data: agentScopeRuntimeResponseBuilder.cancel(),
114
- }
115
- ];
116
-
117
- updateMessage(currentQARef.current.response);
118
142
  break;
119
143
  }
120
144
 
@@ -122,7 +146,9 @@ export default function useChatRequest(options: UseChatRequestOptions) {
122
146
  const chunkData = responseParser(chunk.data);
123
147
  const res = agentScopeRuntimeResponseBuilder.handle(chunkData);
124
148
 
125
- if (res.status !== AgentScopeRuntimeRunStatus.Failed && !res.output?.[0]?.content?.length) continue;
149
+ if (res.status !== AgentScopeRuntimeRunStatus.Failed && !res.output?.some(msg => msg.content?.length)) continue;
150
+
151
+ if (!isStillActive()) break;
126
152
 
127
153
  if (currentQARef.current.response) {
128
154
  currentQARef.current.response.cards = [
@@ -140,19 +166,21 @@ export default function useChatRequest(options: UseChatRequestOptions) {
140
166
  }
141
167
  }
142
168
  } catch (error) {
169
+ if (!isStillActive()) {
170
+ // Request is no longer active; do not write cards or fire cancel.
171
+ return;
172
+ }
143
173
  if (currentQARef.current.response?.msgStatus === 'interrupted') {
144
- if (currentApiOptions.cancel) {
145
- currentApiOptions.cancel({
146
- session_id: getCurrentSessionId(),
147
- });
174
+ // Cancel was already sent by handleCancel; don't repeat it here.
175
+ if (currentQARef.current.response) {
176
+ currentQARef.current.response.cards = [
177
+ {
178
+ code: 'AgentScopeRuntimeResponseCard',
179
+ data: agentScopeRuntimeResponseBuilder.cancel(),
180
+ }
181
+ ];
182
+ updateMessage(currentQARef.current.response);
148
183
  }
149
- currentQARef.current.response.cards = [
150
- {
151
- code: 'AgentScopeRuntimeResponseCard',
152
- data: agentScopeRuntimeResponseBuilder.cancel(),
153
- }
154
- ];
155
- updateMessage(currentQARef.current.response);
156
184
  } else {
157
185
  console.error(error);
158
186
  }
@@ -160,10 +188,16 @@ export default function useChatRequest(options: UseChatRequestOptions) {
160
188
  }, [getCurrentSessionId, currentQARef, updateMessage, onFinish]);
161
189
 
162
190
 
163
- const request = useCallback(async (historyMessages: any[], biz_params?: IAgentScopeRuntimeWebUIInputData['biz_params']) => {
191
+ const request = useCallback(async (
192
+ historyMessages: any[],
193
+ biz_params?: IAgentScopeRuntimeWebUIInputData['biz_params'],
194
+ myRequestId?: number,
195
+ ) => {
164
196
  const currentApiOptions = apiOptionsRef.current;
165
197
  const { enableHistoryMessages = false } = currentApiOptions;
166
198
  const abortSignal = currentQARef.current.abortController?.signal;
199
+ const requestId = myRequestId ?? currentQARef.current.activeRequestId;
200
+ const sessionId = currentQARef.current.activeSessionId ?? getCurrentSessionId();
167
201
  let response
168
202
  try {
169
203
  response = currentApiOptions.fetch ? await currentApiOptions.fetch({
@@ -188,15 +222,16 @@ export default function useChatRequest(options: UseChatRequestOptions) {
188
222
  }
189
223
 
190
224
  if (response && response.body) {
191
- await processSSEResponse(response);
225
+ await processSSEResponse(response, requestId, sessionId);
192
226
  }
193
227
  }, [getCurrentSessionId, currentQARef, processSSEResponse]);
194
228
 
195
- const reconnect = useCallback(async (sessionId: string) => {
229
+ const reconnect = useCallback(async (sessionId: string, myRequestId?: number) => {
196
230
  const currentApiOptions = apiOptionsRef.current;
197
231
  if (!currentApiOptions.reconnect) return;
198
232
 
199
233
  const abortSignal = currentQARef.current.abortController?.signal;
234
+ const requestId = myRequestId ?? currentQARef.current.activeRequestId;
200
235
  let response: Response | undefined;
201
236
  try {
202
237
  response = await currentApiOptions.reconnect({
@@ -207,7 +242,7 @@ export default function useChatRequest(options: UseChatRequestOptions) {
207
242
  }
208
243
 
209
244
  if (response && response.body) {
210
- await processSSEResponse(response);
245
+ await processSSEResponse(response, requestId, sessionId);
211
246
  }
212
247
  }, [currentQARef, processSSEResponse]);
213
248
 
@@ -68,6 +68,12 @@ export interface IAgentScopeRuntimeWebUIAPIOptions {
68
68
  * @descriptionEn Custom media URL transformer (e.g. sign URL, replace CDN domain)
69
69
  */
70
70
  replaceMediaURL?: (url: string) => string;
71
+
72
+ /**
73
+ * @description 自定义文件点击事件(桌面端可通过此钩子调用原生 API 打开文件链接),不传则默认 window.open
74
+ * @descriptionEn Custom file click handler (desktop apps can use native APIs to open file URLs), defaults to window.open
75
+ */
76
+ onFileCardClick?: (file: { url?: string; name?: string; size?: number }) => void;
71
77
  }
72
78
 
73
79
  /**
@@ -434,6 +440,8 @@ export interface IAgentScopeRuntimeWebUIRequestActionsOptions {
434
440
  */
435
441
  list?: {
436
442
  icon?: React.ReactElement;
443
+ children?: React.ReactElement;
444
+ render?: ({ data }: { data: IAgentScopeRuntimeRequest }) => React.ReactElement;
437
445
  onClick?: ({ data }: { data: IAgentScopeRuntimeRequest }) => void;
438
446
  }[];
439
447
  }
@@ -445,6 +453,7 @@ export interface IAgentScopeRuntimeWebUIActionsOptions {
445
453
  */
446
454
  list: {
447
455
  icon?: React.ReactElement;
456
+ children?: React.ReactElement;
448
457
  render?: ({
449
458
  data,
450
459
  }: {
@@ -459,6 +468,7 @@ export interface IAgentScopeRuntimeWebUIActionsOptions {
459
468
  */
460
469
  right?: false | {
461
470
  icon?: React.ReactElement;
471
+ children?: React.ReactElement;
462
472
  render?: ({ data }: { data: IAgentScopeRuntimeResponse }) => React.ReactElement;
463
473
  onClick?: ({ data }: { data: IAgentScopeRuntimeResponse }) => void;
464
474
  }[];
@@ -56,7 +56,11 @@ export default function Files(props) {
56
56
 
57
57
  {
58
58
  fileInfo.url && <div className={`${prefixCls}-download`} onClick={() => {
59
- window.open(fileInfo.url, '_blank');
59
+ if (props.onClick) {
60
+ props.onClick(fileInfo);
61
+ } else {
62
+ window.open(fileInfo.url, '_blank');
63
+ }
60
64
  }}>
61
65
  <SparkDownloadLine />
62
66
  </div>
@@ -88,11 +88,12 @@ export default memo(function (props: MarkdownProps) {
88
88
  const config = useMemo(() => ({
89
89
  extensions,
90
90
  walkTokens,
91
- // 当 allowHtml 为 false 时,转义 HTML 标签使其显示为字符串
91
+ // 当 allowHtml 为 false 时,仅放行安全的内联 HTML 标签,其余转义
92
92
  ...(!allowHtml && {
93
93
  renderer: {
94
94
  html(token: { text?: string; raw?: string }) {
95
95
  const text = token.text || token.raw || '';
96
+ if (/^<br\s*\/?>$/i.test(text.trim())) return '<br />';
96
97
  return text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
97
98
  }
98
99
  }
@@ -4,6 +4,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
4
4
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
5
5
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
6
6
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
+ import React from "react";
7
8
  import { SparkCopyLine } from "@agentscope-ai/icons";
8
9
  import { AgentScopeRuntimeContentType } from "../types";
9
10
  import { Bubble } from "../../../..";
@@ -32,7 +33,13 @@ export default function RequestActions(props) {
32
33
  }
33
34
  }] : [];
34
35
  var actions = (requestActionsOptions.list || defaultActions).map(function (i) {
35
- return _objectSpread(_objectSpread({}, i), {}, {
36
+ var res = _objectSpread({}, i);
37
+ if (i.render) {
38
+ res.children = /*#__PURE__*/React.createElement(i.render, {
39
+ data: props.data
40
+ });
41
+ }
42
+ return _objectSpread(_objectSpread({}, res), {}, {
36
43
  onClick: function onClick() {
37
44
  var _i$onClick;
38
45
  (_i$onClick = i.onClick) === null || _i$onClick === void 0 || _i$onClick.call(i, {
@@ -103,6 +103,9 @@ var AgentScopeRuntimeRequestBuilder = /*#__PURE__*/function () {
103
103
  });
104
104
  }
105
105
  this.data = {
106
+ // Client-side send timestamp (seconds), aligns with response.created_at.
107
+ // Backend has not yet returned, so this represents the local send moment.
108
+ created_at: Math.floor(Date.now() / 1000),
106
109
  input: [{
107
110
  role: 'user',
108
111
  type: AgentScopeRuntimeMessageType.MESSAGE,
@@ -115,6 +118,7 @@ var AgentScopeRuntimeRequestBuilder = /*#__PURE__*/function () {
115
118
  key: "handleApproval",
116
119
  value: function handleApproval(input) {
117
120
  this.data = {
121
+ created_at: Math.floor(Date.now() / 1000),
118
122
  input: input
119
123
  };
120
124
  return this.data;
@@ -2,10 +2,15 @@ import { AgentScopeRuntimeContentType } from "../types";
2
2
  import { useMemo } from 'react';
3
3
  import { Bubble } from "../../../..";
4
4
  import Actions from "./Actions";
5
+ import { useChatAnywhereOptions } from "../../Context/ChatAnywhereOptionsContext";
5
6
  import { jsx as _jsx } from "react/jsx-runtime";
6
7
  import { Fragment as _Fragment } from "react/jsx-runtime";
7
8
  import { jsxs as _jsxs } from "react/jsx-runtime";
8
9
  export default function AgentScopeRuntimeRequestCard(props) {
10
+ var onFileCardClick = useChatAnywhereOptions(function (v) {
11
+ var _v$api;
12
+ return (_v$api = v.api) === null || _v$api === void 0 ? void 0 : _v$api.onFileCardClick;
13
+ });
9
14
  var cards = useMemo(function () {
10
15
  return props.data.input[0].content.reduce(function (p, c) {
11
16
  if (c.type === AgentScopeRuntimeContentType.TEXT) {
@@ -81,7 +86,8 @@ export default function AgentScopeRuntimeRequestCard(props) {
81
86
  url: c.file_url,
82
87
  name: c.file_name || c.fileName,
83
88
  size: c.file_size
84
- }]
89
+ }],
90
+ onClick: onFileCardClick
85
91
  });
86
92
  } else {
87
93
  fileCard.data.push({
@@ -93,7 +99,7 @@ export default function AgentScopeRuntimeRequestCard(props) {
93
99
  }
94
100
  return p;
95
101
  }, []);
96
- }, [props.data.input]);
102
+ }, [props.data.input, onFileCardClick]);
97
103
  if (!(cards !== null && cards !== void 0 && cards.length)) return null;
98
104
  return /*#__PURE__*/_jsxs(_Fragment, {
99
105
  children: [/*#__PURE__*/_jsx(Bubble, {
@@ -49,7 +49,7 @@ export default function Tools(props) {
49
49
  return (_v$actions3 = v.actions) === null || _v$actions3 === void 0 ? void 0 : _v$actions3.right;
50
50
  });
51
51
  var actions = compact([].concat(_toConsumableArray(actionsOptionsList.map(function (i) {
52
- var res = i;
52
+ var res = _objectSpread({}, i);
53
53
  if (i.render) {
54
54
  res.children = /*#__PURE__*/React.createElement(i.render, {
55
55
  data: props
@@ -3,11 +3,15 @@ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableTo
3
3
  function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
4
4
  function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
5
5
  function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
6
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
7
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
7
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
8
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
9
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
8
10
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
9
11
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
10
12
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
13
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
11
15
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
12
16
  function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
13
17
  function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
@@ -36,10 +40,56 @@ var AgentScopeRuntimeResponseBuilder = /*#__PURE__*/function () {
36
40
  key: "handleResponse",
37
41
  value: function handleResponse(data) {
38
42
  this.data = produce(this.data, function (draft) {
39
- if (!data.output) {
40
- data.output = [];
41
- }
43
+ var existingOutput = draft.output || [];
44
+ var incomingOutput = data.output;
42
45
  Object.assign(draft, data);
46
+
47
+ // If incoming response has no output or empty output, preserve the
48
+ // accumulated output from streaming to avoid losing intermediate
49
+ // tool-call messages that were already collected.
50
+ if (!incomingOutput || incomingOutput.length === 0) {
51
+ draft.output = existingOutput;
52
+ } else if (existingOutput.length > 0) {
53
+ // Merge by id: prefer the version with non-empty content to avoid
54
+ // a partial-update response wiping out previously accumulated
55
+ // tool-call data (Bug 2 of issue #4644).
56
+ var existingMap = new Map(existingOutput.map(function (m) {
57
+ return [m.id, m];
58
+ }));
59
+ var incomingIds = new Set(incomingOutput.map(function (m) {
60
+ return m.id;
61
+ }));
62
+ var merged = incomingOutput.map(function (incoming) {
63
+ var _incoming$content, _existing$content;
64
+ var existing = existingMap.get(incoming.id);
65
+ if (!existing) return incoming;
66
+ // Prefer the message with content already populated.
67
+ var incomingHasContent = ((_incoming$content = incoming.content) === null || _incoming$content === void 0 ? void 0 : _incoming$content.length) > 0;
68
+ var existingHasContent = ((_existing$content = existing.content) === null || _existing$content === void 0 ? void 0 : _existing$content.length) > 0;
69
+ if (existingHasContent && !incomingHasContent) {
70
+ return _objectSpread(_objectSpread({}, incoming), {}, {
71
+ content: existing.content
72
+ });
73
+ }
74
+ return incoming;
75
+ });
76
+ // Append existing-only messages (not present in incoming).
77
+ var _iterator = _createForOfIteratorHelper(existingOutput),
78
+ _step;
79
+ try {
80
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
81
+ var existing = _step.value;
82
+ if (!incomingIds.has(existing.id)) {
83
+ merged.push(existing);
84
+ }
85
+ }
86
+ } catch (err) {
87
+ _iterator.e(err);
88
+ } finally {
89
+ _iterator.f();
90
+ }
91
+ draft.output = merged;
92
+ }
43
93
  });
44
94
  }
45
95
  }, {
@@ -85,7 +135,25 @@ var AgentScopeRuntimeResponseBuilder = /*#__PURE__*/function () {
85
135
  } else if (data.type === AgentScopeRuntimeContentType.IMAGE) {
86
136
  lastContent.image_url = data.image_url;
87
137
  } else if (data.type === AgentScopeRuntimeContentType.DATA) {
88
- lastContent.data = data.data;
138
+ var isStreamingToolInput = [AgentScopeRuntimeMessageType.PLUGIN_CALL, AgentScopeRuntimeMessageType.TOOL_CALL, AgentScopeRuntimeMessageType.MCP_CALL].includes(msg.type);
139
+ if (isStreamingToolInput) {
140
+ var oldData = lastContent.data || {};
141
+ var newData = data.data || {};
142
+ var merged = _objectSpread({}, oldData);
143
+ for (var _i = 0, _Object$entries = Object.entries(newData); _i < _Object$entries.length; _i++) {
144
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
145
+ key = _Object$entries$_i[0],
146
+ value = _Object$entries$_i[1];
147
+ if (typeof value === 'string' && typeof merged[key] === 'string') {
148
+ merged[key] = merged[key] + value;
149
+ } else {
150
+ merged[key] = value;
151
+ }
152
+ }
153
+ lastContent.data = merged;
154
+ } else {
155
+ lastContent.data = data.data;
156
+ }
89
157
  }
90
158
  } else {
91
159
  msg.content.push(data);
@@ -155,12 +223,12 @@ var AgentScopeRuntimeResponseBuilder = /*#__PURE__*/function () {
155
223
  value: function mergeToolMessages(messages) {
156
224
  var bufferMessagesMap = new Map();
157
225
  var resMessages = [];
158
- var _iterator = _createForOfIteratorHelper(messages),
159
- _step;
226
+ var _iterator2 = _createForOfIteratorHelper(messages),
227
+ _step2;
160
228
  try {
161
229
  var _loop = function _loop() {
162
230
  var _message$content, _message$content2;
163
- var message = _step.value;
231
+ var message = _step2.value;
164
232
  if (AgentScopeRuntimeResponseBuilder.maybeToolInput(message) && (_message$content = message.content) !== null && _message$content !== void 0 && _message$content.length) {
165
233
  var content = message.content[0];
166
234
  var key = content.data.call_id || content.data.name;
@@ -188,13 +256,13 @@ var AgentScopeRuntimeResponseBuilder = /*#__PURE__*/function () {
188
256
  resMessages.push(message);
189
257
  }
190
258
  };
191
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
259
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
192
260
  _loop();
193
261
  }
194
262
  } catch (err) {
195
- _iterator.e(err);
263
+ _iterator2.e(err);
196
264
  } finally {
197
- _iterator.f();
265
+ _iterator2.f();
198
266
  }
199
267
  return resMessages;
200
268
  }
@@ -15,6 +15,10 @@ var Message = /*#__PURE__*/React.memo(function (_ref) {
15
15
  var _v$api;
16
16
  return (_v$api = v.api) === null || _v$api === void 0 ? void 0 : _v$api.replaceMediaURL;
17
17
  });
18
+ var onFileCardClick = useChatAnywhereOptions(function (v) {
19
+ var _v$api2;
20
+ return (_v$api2 = v.api) === null || _v$api2 === void 0 ? void 0 : _v$api2.onFileCardClick;
21
+ });
18
22
  var formatMediaURL = React.useCallback(function (url) {
19
23
  if (!url) return url;
20
24
  return (replaceMediaURL === null || replaceMediaURL === void 0 ? void 0 : replaceMediaURL(url)) || url;
@@ -52,7 +56,8 @@ var Message = /*#__PURE__*/React.memo(function (_ref) {
52
56
  url: formatMediaURL(item.file_url),
53
57
  name: item.file_name || item.fileName || item.file_id,
54
58
  size: item.file_size
55
- }]
59
+ }],
60
+ onClick: onFileCardClick
56
61
  }, index);
57
62
  case AgentScopeRuntimeContentType.AUDIO:
58
63
  return /*#__PURE__*/_jsx(Audios, {
@@ -1,11 +1,20 @@
1
- import React from "react";
2
- import { AgentScopeRuntimeRunStatus } from "../types";
1
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
2
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
5
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
+ import React, { useEffect, useState } from "react";
8
+ import { AgentScopeRuntimeMessageType, AgentScopeRuntimeRunStatus } from "../types";
3
9
  import { ToolCall } from "../../../..";
4
10
  import { useChatAnywhereOptions } from "../../Context/ChatAnywhereOptionsContext";
5
11
  import Approval from "./Approval";
12
+
13
+ // output展示后,2s自动关闭
6
14
  import { jsx as _jsx } from "react/jsx-runtime";
7
15
  import { Fragment as _Fragment } from "react/jsx-runtime";
8
16
  import { jsxs as _jsxs } from "react/jsx-runtime";
17
+ var OUTPUT_AUTO_COLLAPSE_MS = 2000;
9
18
  var Tool = /*#__PURE__*/React.memo(function (_ref) {
10
19
  var _data$content;
11
20
  var data = _ref.data,
@@ -14,12 +23,28 @@ var Tool = /*#__PURE__*/React.memo(function (_ref) {
14
23
  var customToolRenderConfig = useChatAnywhereOptions(function (v) {
15
24
  return v.customToolRenderConfig;
16
25
  }) || {};
26
+ var isOutput = [AgentScopeRuntimeMessageType.PLUGIN_CALL_OUTPUT, AgentScopeRuntimeMessageType.TOOL_CALL_OUTPUT, AgentScopeRuntimeMessageType.MCP_CALL_OUTPUT].includes(data.type);
27
+ var _useState = useState(false),
28
+ _useState2 = _slicedToArray(_useState, 2),
29
+ autoCollapsed = _useState2[0],
30
+ setAutoCollapsed = _useState2[1];
31
+ useEffect(function () {
32
+ if (!isOutput || autoCollapsed) return;
33
+ var timer = setTimeout(function () {
34
+ return setAutoCollapsed(true);
35
+ }, OUTPUT_AUTO_COLLAPSE_MS);
36
+ return function () {
37
+ return clearTimeout(timer);
38
+ };
39
+ }, [isOutput, autoCollapsed]);
17
40
  if (!((_data$content = data.content) !== null && _data$content !== void 0 && _data$content.length)) return null;
18
41
  var content = data.content;
19
42
  var loading = data.status === AgentScopeRuntimeRunStatus.InProgress;
20
43
  var toolName = content[0].data.name;
21
44
  var serverLabel = "".concat(content[0].data.server_label ? content[0].data.server_label + ' / ' : '');
22
45
  var title = "".concat(serverLabel).concat(toolName);
46
+ var isInput = [AgentScopeRuntimeMessageType.PLUGIN_CALL, AgentScopeRuntimeMessageType.TOOL_CALL, AgentScopeRuntimeMessageType.MCP_CALL].includes(data.type);
47
+ var defaultOpen = isInput || isOutput && !autoCollapsed;
23
48
  var node;
24
49
  if (customToolRenderConfig[toolName]) {
25
50
  var C = customToolRenderConfig[toolName];
@@ -30,11 +55,11 @@ var Tool = /*#__PURE__*/React.memo(function (_ref) {
30
55
  var _content$, _content$2;
31
56
  node = /*#__PURE__*/_jsx(ToolCall, {
32
57
  loading: loading,
33
- defaultOpen: false,
58
+ defaultOpen: defaultOpen,
34
59
  title: title === 'undefined' ? '' : title,
35
60
  input: (_content$ = content[0]) === null || _content$ === void 0 || (_content$ = _content$.data) === null || _content$ === void 0 ? void 0 : _content$.arguments,
36
61
  output: (_content$2 = content[1]) === null || _content$2 === void 0 || (_content$2 = _content$2.data) === null || _content$2 === void 0 ? void 0 : _content$2.output
37
- });
62
+ }, autoCollapsed ? 'collapsed' : 'open');
38
63
  }
39
64
  return /*#__PURE__*/_jsxs(_Fragment, {
40
65
  children: [node, isApproval && /*#__PURE__*/_jsx(Approval, {
@@ -108,6 +108,7 @@ export interface IAgentScopeRuntimeError {
108
108
  message: string;
109
109
  }
110
110
  export interface IAgentScopeRuntimeRequest {
111
+ created_at?: number;
111
112
  input: {
112
113
  role: AgentScopeRuntimeMessageRole | string;
113
114
  type: AgentScopeRuntimeMessageType;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 聊天控制器 Hook - 协调所有聊天相关操作
2
+ * Chat controller hook — coordinates all chat-related operations.
3
3
  */
4
4
  export default function useChatController(): {
5
5
  handleSubmit: (data: import("../../../..").IAgentScopeRuntimeWebUIInputData) => void;