Browse Source

merge: 合并新代码

master
Senor-Liu 6 hours ago
parent
commit
75ae782f92
  1. 16
      web/client/src/jsonRender/registry.jsx
  2. 2
      web/client/src/layout/index.jsx
  3. 103
      web/client/src/sections/superAgent/hooks/useSuperAgentStream.js

16
web/client/src/jsonRender/registry.jsx

@ -1049,9 +1049,10 @@ const { registry, handlers, executeAction } = defineRegistry(catalog, {
//
Table: ({ props, bindings }) => {
const schema = props.__inPlaceEditSchema || {};
const isInPlaceEditEnabled = Boolean(props.__inPlaceEditEnabled);
const [editableValue, setEditableValue] = useEditableProp(
props.__inPlaceEditValue,
bindings?.__inPlaceEditValue,
props.dataSource || [],
isInPlaceEditEnabled ? bindings?.__inPlaceEditValue : undefined,
props.dataSource || []
);
const itemFields = schema.item_fields || [];
@ -1059,7 +1060,7 @@ const { registry, handlers, executeAction } = defineRegistry(catalog, {
itemFields.map((field) => [String(field.name), field])
);
const isInlineEditing = Boolean(
props.__inPlaceEditEnabled
isInPlaceEditEnabled
&& bindings?.__inPlaceEditValue
&& Array.isArray(editableValue)
&& schema.kind === "list"
@ -1109,17 +1110,18 @@ const { registry, handlers, executeAction } = defineRegistry(catalog, {
Tree: ({ props, bindings }) => {
const schema = props.__inPlaceEditSchema || {};
const childrenKey = schema.children_key || "children";
const isInPlaceEditEnabled = Boolean(props.__inPlaceEditEnabled);
const [editableValue, setEditableValue] = useEditableProp(
props.__inPlaceEditValue,
bindings?.__inPlaceEditValue,
[]
props.treeData || [],
isInPlaceEditEnabled ? bindings?.__inPlaceEditValue : undefined,
props.treeData || []
);
const itemFields = schema.item_fields || [];
const titleField = itemFields.find(
(field) => field.name === schema.item_title_field
);
const isInlineEditing = Boolean(
props.__inPlaceEditEnabled
isInPlaceEditEnabled
&& bindings?.__inPlaceEditValue
&& Array.isArray(editableValue)
&& schema.kind === "tree"

2
web/client/src/layout/index.jsx

@ -95,7 +95,7 @@ export default function index(props) {
<Routes>
{outerRoutes}
<Route path="/" element={<Layout routerNav={routerNav} />}>
<Route index element={<Navigate to="/superAgent" replace />} />
<Route index element={<Navigate to="/aichat" replace />} />
{innerRoutes}
</Route>
</Routes>

103
web/client/src/sections/superAgent/hooks/useSuperAgentStream.js

@ -73,6 +73,51 @@ const getInterruptStatus = (item = {}) => {
const isTerminalInterrupt = (item = {}) =>
TERMINAL_INTERRUPT_STATUSES.has(getInterruptStatus(item));
const getInterruptInteractionKey = (item = {}) => {
const payload = item?.value || item?.payload || item || {};
const interactionId = String(
payload.interaction_id ||
payload.interactionId ||
item.interaction_id ||
item.interactionId ||
""
).trim();
if (!interactionId) return "";
const revision = payload.revision ?? item.revision ?? "";
return `${interactionId}:${String(revision)}`;
};
const parseMessageContent = (content) => {
if (content && typeof content === "object") return content;
if (typeof content !== "string") return null;
try {
return JSON.parse(content);
} catch (_) {
return null;
}
};
const collectResolvedInterruptInteractions = (messages = []) => {
const resolvedInteractions = new Set();
(Array.isArray(messages) ? messages : []).forEach((message) => {
if (String(message?.type || "").toLowerCase() !== "tool") return;
const result = parseMessageContent(message.content);
const decisionType = String(result?.type || "").toLowerCase();
const interactionId = String(result?.interaction_id || "").trim();
const revision = result?.revision ?? "";
if (
interactionId &&
["approve", "edit", "reject"].includes(decisionType)
) {
resolvedInteractions.add(`${interactionId}:${String(revision)}`);
}
});
return resolvedInteractions;
};
const isSuperAgentEventRequest = (input) => {
const requestUrl = String(input?.url || input || "");
return /\/threads\/[^/]+\/stream\/events(?:\?|$)/.test(requestUrl);
@ -394,6 +439,7 @@ const useSuperAgentStream = ({
const [runtimeInterrupts, setRuntimeInterrupts] = useState([]);
const [dismissedInterruptIds, setDismissedInterruptIds] = useState(new Set());
const resolvedInterruptIdsRef = useRef(new Set());
const resolvedInterruptInteractionsRef = useRef(new Set());
const [currentInterruptId, setCurrentInterruptId] = useState("");
const [hydratedThreadId, setHydratedThreadId] = useState("");
const [transportStateRevision, setTransportStateRevision] = useState(0);
@ -580,6 +626,7 @@ const useSuperAgentStream = ({
setRuntimeInterrupts([]);
setDismissedInterruptIds(new Set());
resolvedInterruptIdsRef.current.clear();
resolvedInterruptInteractionsRef.current.clear();
setCurrentInterruptId("");
setHydratedThreadId("");
}, [activeSessionId]);
@ -671,7 +718,12 @@ const useSuperAgentStream = ({
if (event?.method === "input.requested") {
const data = event.params?.data || {};
const interruptId = String(data.interrupt_id || "").trim();
if (interruptId && !resolvedInterruptIdsRef.current.has(interruptId)) {
const interactionKey = getInterruptInteractionKey(data.payload);
const isResolvedInterrupt =
resolvedInterruptIdsRef.current.has(interruptId) ||
(interactionKey &&
resolvedInterruptInteractionsRef.current.has(interactionKey));
if (interruptId && !isResolvedInterrupt) {
localRunActiveRef.current = false;
setIsLocalRunActive(false);
setCurrentInterruptId(interruptId);
@ -763,6 +815,7 @@ const useSuperAgentStream = ({
setIsBusinessRunning(false);
setRuntimeInterrupts([]);
resolvedInterruptIdsRef.current.clear();
resolvedInterruptInteractionsRef.current.clear();
// 会话切换只断开当前浏览器订阅,不能发送 stop/cancel 命令。
// stopRun 只由用户点击停止按钮触发,否则后端会把原会话改为 suspended。
@ -862,6 +915,12 @@ const useSuperAgentStream = ({
}).then((result) => {
if (interruptId) {
resolvedInterruptIdsRef.current.add(interruptId);
const interactionKey = getInterruptInteractionKey(
response?.value || response?.payload || response
);
if (interactionKey) {
resolvedInterruptInteractionsRef.current.add(interactionKey);
}
setRuntimeInterrupts((currentInterrupts) =>
currentInterrupts.filter(
(item) => String(item?.id || "") !== interruptId
@ -952,6 +1011,19 @@ const useSuperAgentStream = ({
const hydratedMessages = Array.isArray(hydratedState?.values?.messages)
? hydratedState.values.messages
: [];
const historicalResolvedInteractionKeys = useMemo(
() =>
collectResolvedInterruptInteractions([
...hydratedMessages,
...rawStreamMessages,
]),
[hydratedMessages, rawStreamMessages]
);
useEffect(() => {
historicalResolvedInteractionKeys.forEach((interactionKey) => {
resolvedInterruptInteractionsRef.current.add(interactionKey);
});
}, [historicalResolvedInteractionKeys]);
// 只有 hydrate 真正拿到消息后,才能解除首次提交的 thread 投影保护。
// 仅有 task 快照不代表消息已完成落库,否则 task 先到时会让当前消息再次被隐藏。
const hasHydratedSessionContent = hydratedMessages.length > 0;
@ -1058,10 +1130,18 @@ const useSuperAgentStream = ({
}))
: []),
];
const hasPersistedInterrupts = hydratedInterrupts.length > 0;
const pendingHydratedInterrupts = hydratedInterrupts.filter((item) => {
const interruptId = String(item?.id || item?.interruptId || "").trim();
const interactionKey = getInterruptInteractionKey(item);
return (
!resolvedInterruptIdsRef.current.has(interruptId) &&
!historicalResolvedInteractionKeys.has(interactionKey)
);
});
const hasPersistedInterrupts = pendingHydratedInterrupts.length > 0;
const persistedInterrupts =
hydratedState
? hydratedInterrupts
? pendingHydratedInterrupts
: isStreamProjectionReady
? stream.interrupts
: [];
@ -1132,14 +1212,25 @@ const useSuperAgentStream = ({
streamInterrupts.forEach((item) => appendInterrupt(item));
return [...merged.values()].filter(
(item) =>
!dismissedInterruptIds.has(String(item.id || "")) &&
(item) => {
const interruptId = String(item.id || "");
const interactionKey = getInterruptInteractionKey(item);
const isHistoricalResolved =
historicalResolvedInteractionKeys.has(interactionKey) ||
resolvedInterruptInteractionsRef.current.has(interactionKey);
return (
!dismissedInterruptIds.has(interruptId) &&
!resolvedInterruptIdsRef.current.has(interruptId) &&
!isHistoricalResolved &&
!isTerminalInterrupt(item)
);
}
);
}, [
dismissedInterruptIds,
isInterruptProjectionReady,
isStreamProjectionReady,
historicalResolvedInteractionKeys,
streamInterrupts,
]);
@ -1170,7 +1261,7 @@ const useSuperAgentStream = ({
isPersistedThreadActive,
currentInterruptId:
hydratedState &&
hydratedInterrupts.length === 0 &&
pendingHydratedInterrupts.length === 0 &&
runtimeInterrupts.length === 0
? ""
: currentInterruptId,

Loading…
Cancel
Save