@agentscope-ai/chat 1.1.66 → 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 (27) 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 +34 -3
  6. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Message.tsx +2 -1
  7. package/components/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/types.tsx +1 -0
  8. package/components/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.tsx +129 -33
  9. package/components/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatRequest.tsx +74 -39
  10. package/components/AgentScopeRuntimeWebUI/core/types/IChatAnywhere.ts +10 -0
  11. package/components/DefaultCards/Files/index.tsx +5 -1
  12. package/components/Markdown/Markdown.tsx +2 -1
  13. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Actions.js +8 -1
  14. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Builder.js +4 -0
  15. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Request/Card.js +8 -2
  16. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Actions.js +1 -1
  17. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Builder.js +58 -12
  18. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/Response/Message.js +6 -1
  19. package/lib/AgentScopeRuntimeWebUI/core/AgentScopeRuntime/types.d.ts +1 -0
  20. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.d.ts +1 -1
  21. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.js +167 -68
  22. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatRequest.d.ts +7 -3
  23. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatRequest.js +151 -117
  24. package/lib/AgentScopeRuntimeWebUI/core/types/IChatAnywhere.d.ts +15 -0
  25. package/lib/DefaultCards/Files/index.js +5 -1
  26. package/lib/Markdown/Markdown.js +1 -0
  27. package/package.json +1 -1
@@ -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,13 +3,13 @@ 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 _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; } } }; }
7
6
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
8
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."); }
9
- 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
- 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; }
11
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; } }
12
9
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
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; } } }; }
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); }
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
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
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; }
15
15
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@@ -40,10 +40,56 @@ var AgentScopeRuntimeResponseBuilder = /*#__PURE__*/function () {
40
40
  key: "handleResponse",
41
41
  value: function handleResponse(data) {
42
42
  this.data = produce(this.data, function (draft) {
43
- if (!data.output) {
44
- data.output = [];
45
- }
43
+ var existingOutput = draft.output || [];
44
+ var incomingOutput = data.output;
46
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
+ }
47
93
  });
48
94
  }
49
95
  }, {
@@ -177,12 +223,12 @@ var AgentScopeRuntimeResponseBuilder = /*#__PURE__*/function () {
177
223
  value: function mergeToolMessages(messages) {
178
224
  var bufferMessagesMap = new Map();
179
225
  var resMessages = [];
180
- var _iterator = _createForOfIteratorHelper(messages),
181
- _step;
226
+ var _iterator2 = _createForOfIteratorHelper(messages),
227
+ _step2;
182
228
  try {
183
229
  var _loop = function _loop() {
184
230
  var _message$content, _message$content2;
185
- var message = _step.value;
231
+ var message = _step2.value;
186
232
  if (AgentScopeRuntimeResponseBuilder.maybeToolInput(message) && (_message$content = message.content) !== null && _message$content !== void 0 && _message$content.length) {
187
233
  var content = message.content[0];
188
234
  var key = content.data.call_id || content.data.name;
@@ -210,13 +256,13 @@ var AgentScopeRuntimeResponseBuilder = /*#__PURE__*/function () {
210
256
  resMessages.push(message);
211
257
  }
212
258
  };
213
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
259
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
214
260
  _loop();
215
261
  }
216
262
  } catch (err) {
217
- _iterator.e(err);
263
+ _iterator2.e(err);
218
264
  } finally {
219
- _iterator.f();
265
+ _iterator2.f();
220
266
  }
221
267
  return resMessages;
222
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, {
@@ -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;