@blade-hq/agent-react 2612.0.0-beta.3 → 2612.0.0-beta.5

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.md CHANGED
@@ -471,3 +471,13 @@ blade-chat::part(input-toolbar) { gap: 12px; }
471
471
  完整签名见 [public-api.md](./public-api.md)。本包公开声明:`BladeProvider`、`BladeProviderProps`、`BladeHost`、`HostAttachment`、`HostCatalogItem`、`useBladeClient`、`useAgentSession`、`UseAgentSessionOptions`、`UseAgentSessionResult`、`useReplay`、`UseReplayResult`、`ReplayMismatch`、`ReplayBar`、`ReplayBarProps`、`ReplayMismatchPrompt`、`ReplayMismatchPromptProps`、`ChatView`、`ChatViewProps`、`ChatViewClassNames`、`ChatViewRenderers`、`ChatViewSlots`、`UserMessageRendererProps`、`AssistantTextRendererProps`、`AskUserQuestionRendererProps`、`AskUserQuestionData`、`PlanUpdateRendererProps`、`PlanUpdateData`、`PlanStepStatus`、`ToolCallRenderer`、`ToolRendererProps`、`FollowupInteractionEvent`、`LlmChatHandle`、`LlmChatOptions`、`ChatCompletionTool`、`LlmToolCall`、`MarkdownContent`、`MarkdownContentProps`。
472
472
 
473
473
  `@blade-hq/agent-client` 不再从本包转出,`BladeClient`、`AgentSession` 等直接从 `@blade-hq/agent-client` import。
474
+
475
+ ## 主题资产 @
476
+
477
+ 主题资产候选通过当前主题的资产列表获取,使用专用 `TopicAssetMention` 药丸;没有关联主题时不展示候选。首页与会话输入框复用候选查询、引用序列化和展示结构。
478
+
479
+ `queueTopicAssetReference(sessionId, { topic_id, path })`(`@blade-hq/agent-react/rich-input`)可在导航到当前用户的同主题会话前预填引用。发送只携带引用身份,元数据和权限由服务端重新判断;本轮续跑保留引用,下一条消息默认不继承。
480
+
481
+ 会话文件树可通过宿主提供的提交动作显示“提交到资产”;只读或未关联主题时不显示写入口,服务端仍会独立检查权限。
482
+
483
+ 文件提交共享组件:`TopicAssetSubmitDialog`(属性 `TopicAssetSubmitDialogProps`)提供目录选择和提交说明;`useTopicAssetSubmissionAccess` 查询当前会话的主题及写权限。二者用于宿主文件树接入,不能替代服务端鉴权。
@@ -30,7 +30,7 @@ export type ChatViewClassNames = Partial<{
30
30
  skillStatusBarInner: string;
31
31
  }>;
32
32
  /** 输入框提交时的附加项(模型、推理档位、知识库等由富输入给出)。 */
33
- export interface ChatSendOptions extends Pick<ChatSendExtras, "thinkingOverride" | "reasoningLevel" | "kbIds" | "queueWhenRunning"> {
33
+ export interface ChatSendOptions extends Pick<ChatSendExtras, "thinkingOverride" | "reasoningLevel" | "kbIds" | "topicAssetRefs" | "queueWhenRunning"> {
34
34
  /** 附件实际上传到的会话(首页新建会话时由输入框给出)。 */
35
35
  targetSessionId?: string;
36
36
  /** 本条消息用的模型;null 表示沿用会话已保存的模型。 */
@@ -10,6 +10,7 @@ export interface ChatSendExtras {
10
10
  thinkingOverride?: boolean;
11
11
  reasoningLevel?: string;
12
12
  kbIds?: number[];
13
+ topicAssetRefs?: SendOptions["topicAssetRefs"];
13
14
  whatif?: SendOptions["whatif"];
14
15
  replayDecision?: SendOptions["replayDecision"];
15
16
  /**
@@ -1,3 +1,4 @@
1
+ import { type TopicAssetRef } from "@blade-hq/agent-client";
1
2
  export interface PendingContextSignal {
2
3
  id: string;
3
4
  label: string;
@@ -13,6 +14,7 @@ export interface SendRequestSignal {
13
14
  id: string;
14
15
  }
15
16
  export interface ComposerChannelsState {
17
+ topicAssetReferences: Record<string, TopicAssetRef[]>;
16
18
  pendingContexts: Record<string, PendingContextSignal[]>;
17
19
  draftAppends: Record<string, DraftAppendSignal[]>;
18
20
  sendRequests: Record<string, SendRequestSignal[]>;
@@ -21,6 +23,8 @@ export interface ComposerChannelsState {
21
23
  }
22
24
  export declare const composerChannels: {
23
25
  getState: () => ComposerChannelsState;
26
+ addTopicAssetReference(sessionId: string, ref: TopicAssetRef): void;
27
+ consumeTopicAssetReferences: (sessionId: string) => TopicAssetRef[];
24
28
  subscribe(listener: () => void): () => void;
25
29
  addPendingContext(sessionId: string, context: Omit<PendingContextSignal, "id">): void;
26
30
  removePendingContext(sessionId: string, contextId: string): void;
@@ -6,7 +6,7 @@ import {
6
6
  catalogToConnectorItems,
7
7
  filterConnectorItems,
8
8
  useSessionPluginActivation
9
- } from "./chunk-FQQJP2GJ.js";
9
+ } from "./chunk-XULCQP3Q.js";
10
10
 
11
11
  // src/components/connector-panel-types.ts
12
12
  function selectionFromItem(item) {
@@ -255,4 +255,4 @@ export {
255
255
  ConnectorPanel,
256
256
  PluginTriggerStack
257
257
  };
258
- //# sourceMappingURL=chunk-WCMGQJLE.js.map
258
+ //# sourceMappingURL=chunk-7IPINZHT.js.map
@@ -24,6 +24,7 @@ import {
24
24
  Dialog,
25
25
  DialogClose,
26
26
  DialogContent,
27
+ DialogDescription,
27
28
  DialogTitle,
28
29
  Download,
29
30
  Earth,
@@ -120,7 +121,7 @@ import {
120
121
  useSessionSource,
121
122
  useSessionState,
122
123
  useSingleSessionSource
123
- } from "./chunk-FQQJP2GJ.js";
124
+ } from "./chunk-XULCQP3Q.js";
124
125
  import {
125
126
  __export
126
127
  } from "./chunk-PR4QN5HX.js";
@@ -38728,9 +38729,14 @@ function UserMessageBubble({ message, className }) {
38728
38729
  contexts: textContexts
38729
38730
  } = extractTextAttachments(message.content);
38730
38731
  const trimmedClean = cleanText.trim();
38732
+ const topicAssetPills = message.topic_asset_refs?.length ? /* @__PURE__ */ jsx34("div", { className: "flex max-w-full flex-wrap justify-end gap-1.5", "aria-label": "\u5F15\u7528\u7684\u4E3B\u9898\u8D44\u4EA7", children: message.topic_asset_refs.map((ref) => /* @__PURE__ */ jsxs29("span", { title: ref.path, className: "max-w-full truncate rounded-full border border-border bg-muted px-2.5 py-1 text-xs text-foreground", children: [
38733
+ "\u4E3B\u9898\u8D44\u4EA7 \xB7 ",
38734
+ ref.path
38735
+ ] }, `${ref.topic_id}:${ref.path}`)) }) : null;
38731
38736
  const whatifParsed = trimmedClean && imageParts.length === 0 && fileParts.length === 0 && textAttachments.length === 0 ? parseWhatIfPrompt(cleanText) : null;
38732
38737
  if (whatifParsed && customBubble == null) {
38733
38738
  return /* @__PURE__ */ jsx34("div", { part: "user-row", className: "blade-chat-user-row flex justify-end", children: /* @__PURE__ */ jsxs29("div", { className: "blade-chat-user-col group flex max-w-[72%] flex-col items-end gap-2", children: [
38739
+ topicAssetPills,
38734
38740
  /* @__PURE__ */ jsx34(WhatIfUserBubble, { parsed: whatifParsed }),
38735
38741
  whatifParsed.userText && /* @__PURE__ */ jsx34(
38736
38742
  MessageActions,
@@ -38866,6 +38872,7 @@ function UserMessageBubble({ message, className }) {
38866
38872
  }
38867
38873
  ),
38868
38874
  /* @__PURE__ */ jsx34(MessageContextPills, { contexts: textContexts }),
38875
+ topicAssetPills,
38869
38876
  trimmedClean && // 原型:不对称圆角(指向发送侧的一角收窄),无边框、无内阴影。
38870
38877
  (customBubble != null ? customBubble : /* @__PURE__ */ jsx34(
38871
38878
  "div",
@@ -48240,6 +48247,7 @@ function ChatPanel({
48240
48247
  thinkingOverride: options?.thinkingOverride,
48241
48248
  reasoningLevel: options?.reasoningLevel,
48242
48249
  kbIds: options?.kbIds,
48250
+ topicAssetRefs: options?.topicAssetRefs,
48243
48251
  queueWhenRunning: options?.queueWhenRunning
48244
48252
  });
48245
48253
  },
@@ -49291,6 +49299,99 @@ function ChatView(props) {
49291
49299
  return /* @__PURE__ */ jsx54(AgentChat, { ...rest });
49292
49300
  }
49293
49301
 
49302
+ // src/components/TopicAssetSubmitDialog.tsx
49303
+ import { useRef as useRef29, useState as useState40 } from "react";
49304
+ import { jsx as jsx55, jsxs as jsxs48 } from "react/jsx-runtime";
49305
+ function useTopicAssetSubmissionAccess(sessionId) {
49306
+ const client = useBladeClient();
49307
+ return useAsyncValue(sessionId, async (signal) => {
49308
+ if (!sessionId) return null;
49309
+ const [session, groups] = await Promise.all([
49310
+ client.sessions.getSession(sessionId, { signal }),
49311
+ client.chatGroups.list()
49312
+ ]);
49313
+ if (session.runtime_type !== "sandbox" || !session.chat_group_id || session.viewer_role === "viewer") return null;
49314
+ const group = groups.find((item) => item.id === session.chat_group_id);
49315
+ return group && (group.role === "owner" || group.role === "editor") ? group : null;
49316
+ });
49317
+ }
49318
+ function TopicAssetSubmitDialog({ sessionId, topicId, path: path2, onClose, onSubmitted }) {
49319
+ const client = useBladeClient();
49320
+ const [folder, setFolder] = useState40("");
49321
+ const [message, setMessage] = useState40("");
49322
+ const [error61, setError] = useState40(null);
49323
+ const [busy, setBusy] = useState40(false);
49324
+ const submitting = useRef29(false);
49325
+ const [revision, setRevision] = useState40(0);
49326
+ const assets = useAsyncValue(`${sessionId}:${topicId}:${revision}`, async () => {
49327
+ const [session, groups, result] = await Promise.all([
49328
+ client.sessions.getSession(sessionId),
49329
+ client.chatGroups.list(),
49330
+ client.chatGroups.listAssets(topicId)
49331
+ ]);
49332
+ const role = groups.find((item) => item.id === topicId)?.role;
49333
+ if (session.runtime_type !== "sandbox" || session.chat_group_id !== topicId || session.viewer_role === "viewer") throw new Error("\u8FD9\u4E2A\u4F1A\u8BDD\u4E0D\u80FD\u63D0\u4EA4\u5230\u8BE5\u4E3B\u9898");
49334
+ if (role !== "owner" && role !== "editor") throw new Error("\u4F60\u5728\u8FD9\u4E2A\u4E3B\u9898\u6CA1\u6709\u63D0\u4EA4\u6743\u9650");
49335
+ const folders = /* @__PURE__ */ new Set();
49336
+ for (const asset of result.items) {
49337
+ if (asset.is_dir) folders.add(asset.path);
49338
+ const parts = asset.path.split("/");
49339
+ for (let i2 = 1; i2 < parts.length; i2 += 1) folders.add(parts.slice(0, i2).join("/"));
49340
+ }
49341
+ return [...folders].sort((a, b2) => a.localeCompare(b2));
49342
+ });
49343
+ const submit = async () => {
49344
+ if (submitting.current || !assets.data) return;
49345
+ submitting.current = true;
49346
+ setBusy(true);
49347
+ setError(null);
49348
+ try {
49349
+ const name = path2.split("/").at(-1) ?? path2;
49350
+ await client.chatGroups.submitSessionAsset({ session_id: sessionId, path: path2, target_path: folder ? `${folder}/${name}` : name, message: message.trim() });
49351
+ onSubmitted?.();
49352
+ onClose();
49353
+ } catch (cause) {
49354
+ setError(cause instanceof Error ? cause.message : "\u63D0\u4EA4\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
49355
+ } finally {
49356
+ submitting.current = false;
49357
+ setBusy(false);
49358
+ }
49359
+ };
49360
+ return /* @__PURE__ */ jsx55(Dialog, { open: true, onOpenChange: (open2) => {
49361
+ if (!open2 && !busy) onClose();
49362
+ }, children: /* @__PURE__ */ jsxs48(DialogContent, { className: "max-w-md", children: [
49363
+ /* @__PURE__ */ jsx55(DialogTitle, { children: "\u63D0\u4EA4\u5230\u8D44\u4EA7" }),
49364
+ /* @__PURE__ */ jsx55(DialogDescription, { children: "\u5C06\u6587\u4EF6\u590D\u5236\u5230\u4E3B\u9898\u4F9B\u6210\u5458\u4F7F\u7528\uFF0C\u5DE5\u4F5C\u533A\u539F\u6587\u4EF6\u4F1A\u4FDD\u7559\u3002\u540C\u540D\u8D44\u4EA7\u4F1A\u88AB\u66FF\u6362\u3002" }),
49365
+ /* @__PURE__ */ jsxs48("form", { onSubmit: (event) => {
49366
+ event.preventDefault();
49367
+ void submit();
49368
+ }, className: "flex flex-col gap-4", children: [
49369
+ /* @__PURE__ */ jsx55("p", { className: "break-all text-sm text-secondary-foreground", children: path2 }),
49370
+ /* @__PURE__ */ jsxs48("label", { className: "flex flex-col gap-1 text-sm", children: [
49371
+ "\u76EE\u6807\u6587\u4EF6\u5939",
49372
+ /* @__PURE__ */ jsxs48("select", { "aria-label": "\u76EE\u6807\u6587\u4EF6\u5939", value: folder, onChange: (event) => setFolder(event.target.value), disabled: busy || !assets.data, className: "rounded-md border border-border bg-background p-2", children: [
49373
+ /* @__PURE__ */ jsx55("option", { value: "", children: "\u8D44\u4EA7\u6839\u76EE\u5F55" }),
49374
+ assets.data?.map((item) => /* @__PURE__ */ jsx55("option", { value: item, children: item }, item))
49375
+ ] })
49376
+ ] }),
49377
+ /* @__PURE__ */ jsxs48("label", { className: "flex flex-col gap-1 text-sm", children: [
49378
+ "\u8BF4\u660E\uFF08\u53EF\u9009\uFF09",
49379
+ /* @__PURE__ */ jsx55("textarea", { "aria-label": "\u63D0\u4EA4\u8BF4\u660E", value: message, onChange: (event) => setMessage(event.target.value), maxLength: 1e3, disabled: busy, className: "min-h-20 rounded-md border border-border bg-background p-2" })
49380
+ ] }),
49381
+ assets.error ? /* @__PURE__ */ jsxs48("div", { role: "alert", className: "text-sm text-muted-foreground", children: [
49382
+ assets.error instanceof Error ? assets.error.message : "\u8D44\u4EA7\u52A0\u8F7D\u5931\u8D25",
49383
+ " ",
49384
+ /* @__PURE__ */ jsx55("button", { type: "button", onClick: () => setRevision((n2) => n2 + 1), className: "underline", children: "\u91CD\u8BD5" })
49385
+ ] }) : !assets.data ? /* @__PURE__ */ jsx55("output", { className: "text-sm text-muted-foreground", children: "\u6B63\u5728\u52A0\u8F7D\u8D44\u4EA7\u2026" }) : null,
49386
+ error61 ? /* @__PURE__ */ jsx55("p", { role: "alert", className: "text-sm text-muted-foreground", children: error61 }) : null,
49387
+ /* @__PURE__ */ jsxs48("div", { className: "flex justify-end gap-2", children: [
49388
+ /* @__PURE__ */ jsx55("button", { type: "button", disabled: busy, onClick: onClose, className: "rounded-md border border-border px-3 py-2 text-sm", children: "\u53D6\u6D88" }),
49389
+ /* @__PURE__ */ jsx55("button", { type: "submit", disabled: busy || !assets.data, className: "rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground disabled:opacity-50", children: busy ? "\u63D0\u4EA4\u4E2D\u2026" : "\u63D0\u4EA4" })
49390
+ ] })
49391
+ ] })
49392
+ ] }) });
49393
+ }
49394
+
49294
49395
  export {
49295
49396
  useAgentSession,
49296
49397
  useReplay,
@@ -49319,7 +49420,9 @@ export {
49319
49420
  AgentChat,
49320
49421
  useLlmChat,
49321
49422
  LlmChat,
49322
- ChatView
49423
+ ChatView,
49424
+ useTopicAssetSubmissionAccess,
49425
+ TopicAssetSubmitDialog
49323
49426
  };
49324
49427
  /*! Bundled license information:
49325
49428
 
@@ -49340,4 +49443,4 @@ use-stick-to-bottom/dist/StickToBottom.js:
49340
49443
  js-yaml/dist/js-yaml.mjs:
49341
49444
  (*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT *)
49342
49445
  */
49343
- //# sourceMappingURL=chunk-5J54S5BK.js.map
49446
+ //# sourceMappingURL=chunk-T5WGKZN4.js.map