feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type AgentModalTabKey } from '@coze-agent-ide/tool-config';
import { useEvent } from '../event/use-event';
import { EventCenterEventName } from '../../typings/scoped-events';
import {
type IAgentModalTabChangeEventParams,
type IAgentModalVisibleChangeEventParams,
} from '../../typings/event';
/**
* 内部使用的方法,不对外使用,用于抛出事件
*/
export const useAgentModalTriggerEvent = () => {
const { emit } = useEvent();
const emitTabChangeEvent = (tabKey: AgentModalTabKey) => {
emit<IAgentModalTabChangeEventParams>(
EventCenterEventName.AgentModalTabChange,
{ tabKey },
);
};
const emitModalVisibleChangeEvent = (isVisible: boolean) => {
emit<IAgentModalVisibleChangeEventParams>(
EventCenterEventName.AgentModalVisibleChange,
{
isVisible,
},
);
};
return { emitTabChangeEvent, emitModalVisibleChangeEvent };
};

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState } from 'react';
import {
AgentSkillModal,
type IAgentSkillModalPane,
} from '../../components/agent-skill-modal';
import { useAgentModalTriggerEvent } from './use-agent-modal-trigger-event';
export const useAgentSkillModal = (tabPanes: IAgentSkillModalPane[]) => {
const [visible, setVisible] = useState(false);
const { emitModalVisibleChangeEvent } = useAgentModalTriggerEvent();
const close = () => {
setVisible(false);
emitModalVisibleChangeEvent(false);
};
const open = () => {
setVisible(true);
emitModalVisibleChangeEvent(true);
};
return {
node: visible ? (
<AgentSkillModal tabPanes={tabPanes} onCancel={close} />
) : null,
close,
open,
};
};

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useShallow } from 'zustand/react/shallow';
import { type AgentSkillKey } from '@coze-agent-ide/tool-config';
import { useAbilityAreaContext } from '../../context/ability-area-context';
/**
* @deprecated 内部使用过渡期方案针对非注册组件使用外部的skill设置
*/
export const useHasAgentSkillWithPK = () => {
const {
store: { useAgentAreaStore },
} = useAbilityAreaContext();
const { existManualAgentSkillKey, realSetHasAgentSkill } = useAgentAreaStore(
state => ({
existManualAgentSkillKey: state.existManualAgentSkillKey,
realSetHasAgentSkill: state.setHasAgentSkillKey,
}),
);
/**
* @deprecated 内部使用,过渡期
*/
const setHasAgentSkill = (
agentSkillKey: AgentSkillKey,
hasSkill: boolean,
) => {
const isManual = existManualAgentSkillKey(agentSkillKey);
if (!isManual) {
realSetHasAgentSkill(agentSkillKey, hasSkill);
}
};
return {
setHasAgentSkill,
};
};
export const useNoneAgentSkill = () => {
const {
store: { useAgentAreaStore },
} = useAbilityAreaContext();
const noneAgentSkill = useAgentAreaStore(
useShallow(state =>
state.registeredAgentSkillKeyList.every(
agentSkillKey => !state.hasAgentSkillKeyList.includes(agentSkillKey),
),
),
);
return noneAgentSkill;
};

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useAbilityConfigContext } from '../../context/ability-config-context';
/**
* 用户内部获取ToolKey使用
*/
export const useAbilityConfig = () => {
const { abilityKey, scope } = useAbilityConfigContext();
return { abilityKey, scope };
};

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useMemo } from 'react';
import { createToolAreaStore } from '../../store/tool-area';
import { createAgentAreaStore } from '../../store/agent-area';
export const useCreateStore = () => {
const memoedUseToolAreaStore = useMemo(() => createToolAreaStore(), []);
const memoedUseAgentAreaStore = useMemo(() => createAgentAreaStore(), []);
return {
useToolAreaStore: memoedUseToolAreaStore,
useAgentAreaStore: memoedUseAgentAreaStore,
};
};

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type AbilityKey } from '@coze-agent-ide/tool-config';
import { useAbilityAreaContext } from '../../context/ability-area-context';
export const useGetToolConfig = () => {
const {
store: { useToolAreaStore },
} = useAbilityAreaContext();
const registeredToolKeyConfigList = useToolAreaStore(
state => state.registeredToolKeyConfigList,
);
return (abilityKey?: AbilityKey) =>
registeredToolKeyConfigList.find(
toolConfig => toolConfig.toolKey === abilityKey,
);
};

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type AgentSkillKey } from '@coze-agent-ide/tool-config';
import { useAbilityAreaContext } from '../../context/ability-area-context';
/**
* 用于内部注册AgentSkill使用
*/
export const useRegisterAgentSkillKey = () => {
const {
store: { useAgentAreaStore },
} = useAbilityAreaContext();
const appendRegisteredAgentSkillKeyList = useAgentAreaStore(
state => state.appendRegisteredAgentSkillKeyList,
);
return (agentSkillKey: AgentSkillKey) => {
appendRegisteredAgentSkillKeyList(agentSkillKey);
};
};

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useShallow } from 'zustand/react/shallow';
import { type IRegisteredToolGroupConfig } from '../../store/tool-area';
import { useAbilityAreaContext } from '../../context/ability-area-context';
export const useRegisterToolGroup = () => {
const {
store: { useToolAreaStore },
} = useAbilityAreaContext();
const appendIntoRegisteredToolGroupList = useToolAreaStore(
useShallow(state => state.appendIntoRegisteredToolGroupList),
);
return (params: IRegisteredToolGroupConfig) => {
appendIntoRegisteredToolGroupList(params);
};
};
export const useRegisteredToolGroupList = () => {
const {
store: { useToolAreaStore },
} = useAbilityAreaContext();
const registeredToolGroupList = useToolAreaStore(
useShallow(state => state.registeredToolGroupList),
);
return registeredToolGroupList;
};

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useShallow } from 'zustand/react/shallow';
import { type IRegisteredToolKeyConfig } from '../../store/tool-area';
import { useAbilityAreaContext } from '../../context/ability-area-context';
/**
* 用于内部注册Tool使用
*/
export const useRegisterToolKey = () => {
const {
store: { useToolAreaStore },
} = useAbilityAreaContext();
const appendIntoRegisteredToolKeyConfigList = useToolAreaStore(
useShallow(state => state.appendIntoRegisteredToolKeyConfigList),
);
return (params: IRegisteredToolKeyConfig) => {
appendIntoRegisteredToolKeyConfigList(params);
};
};
export const useRegisteredToolKeyConfigList = () => {
const {
store: { useToolAreaStore },
} = useAbilityAreaContext();
const registeredToolKeyConfigList = useToolAreaStore(
useShallow(state => state.registeredToolKeyConfigList),
);
return registeredToolKeyConfigList;
};

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useAbilityAreaContext } from '../../context/ability-area-context';
export const useEvent = () => {
const { scopedEventBus } = useAbilityAreaContext();
function on<T extends Record<string, any>>(
eventName: string,
listener: (params: T) => void,
) {
scopedEventBus.on(eventName, listener);
return () => {
scopedEventBus.off(eventName, listener);
};
}
function once<T extends Record<string, any>>(
eventName: string,
listener: (params: T) => void,
) {
scopedEventBus.once(eventName, listener);
}
function emit<T extends Record<string, any>>(eventName: string, params: T) {
scopedEventBus.emit(eventName, params);
}
return {
on,
once,
emit,
};
};

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useShallow } from 'zustand/react/shallow';
import { type AgentSkillKey } from '@coze-agent-ide/tool-config';
import { useAbilityAreaContext } from '../../../context/ability-area-context';
export const useHasAgentSkill = () => {
const {
store: { useAgentAreaStore },
} = useAbilityAreaContext();
const {
setHasAgentSkillKey,
existHasAgentSkillKey,
appendManualAgentSkillKeyList,
} = useAgentAreaStore(
useShallow(state => ({
setHasAgentSkillKey: state.setHasAgentSkillKey,
existHasAgentSkillKey: state.existHasAgentSkillKey,
appendManualAgentSkillKeyList: state.appendManualAgentSkillKeyList,
})),
);
const setHasAgentSkill = (
agentSkillKey: AgentSkillKey,
hasSkill: boolean,
) => {
setHasAgentSkillKey(agentSkillKey, hasSkill);
appendManualAgentSkillKeyList(agentSkillKey);
};
const getHasAgentSkill = (agentSkillKey: AgentSkillKey) =>
existHasAgentSkillKey(agentSkillKey);
return {
setHasAgentSkill,
getHasAgentSkill,
};
};

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useMemo } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { size } from 'lodash-es';
import { type SkillKeyEnum } from '@coze-agent-ide/tool-config';
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
import { useBotDetailIsReadonly } from '@coze-studio/bot-detail-store';
import {
TabStatus,
type TabDisplayItems,
} from '@coze-arch/bot-api/developer_api';
import { useAbilityConfig } from '../../builtin/use-ability-config';
import { toolKeyToApiStatusKeyTransformer } from '../../../utils/tool-content-block';
/**
* 用于校验当前模块默认展开收起状态
*
* @param blockKey 主键 - tool 插件化改造后无需传入
* @param configured 是否有配置内容
* @param when 是否校验
*
* @see
*/
export const useToolContentBlockDefaultExpand = (
$params: {
blockKey?: SkillKeyEnum;
configured: boolean;
},
$when = true,
) => {
const { abilityKey } = useAbilityConfig();
const { blockKey, configured = false } = $params;
const isReadonly = useBotDetailIsReadonly();
const { init, editable, botSkillBlockCollapsibleState } = usePageRuntimeStore(
useShallow(store => ({
init: store.init,
editable: store.editable,
botSkillBlockCollapsibleState: store.botSkillBlockCollapsibleState,
})),
);
return useMemo(() => {
// 不做校验
if (!$when) {
return undefined;
// 状态机未就绪
} else if (!init || size(botSkillBlockCollapsibleState) === 0) {
return undefined;
/**
* @description 仅在满足以下条件时用户行为记录才能生效
*
* 1. 拥有编辑权限
* 2. 不能是历史预览环境
* 3. 必须已配置
*/
} else if (editable && !isReadonly && configured) {
const key = abilityKey ?? blockKey;
if (!key) {
return;
}
const transformerBlockKey = toolKeyToApiStatusKeyTransformer(key);
const collapsibleState =
botSkillBlockCollapsibleState[
transformerBlockKey as keyof TabDisplayItems
];
if (collapsibleState === TabStatus.Open) {
return true;
} else if (collapsibleState === TabStatus.Close) {
return false;
}
}
return configured;
}, [
$when,
blockKey,
configured,
init,
isReadonly,
editable,
botSkillBlockCollapsibleState,
]);
};

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type AbilityKey } from '@coze-agent-ide/tool-config';
import { useEvent } from '../../event/use-event';
import { EventCenterEventName } from '../../../typings/scoped-events';
import { type IToggleContentBlockEventParams } from '../../../typings/event';
interface IUseToolToggleCollapseParams {
abilityKeyList: AbilityKey[];
isExpand: boolean;
}
export const useToolToggleCollapse = () => {
const { emit } = useEvent();
return ({ abilityKeyList, isExpand }: IUseToolToggleCollapseParams) => {
if (!abilityKeyList.length) {
return;
}
abilityKeyList.forEach(abilityKey => {
emit<IToggleContentBlockEventParams>(
EventCenterEventName.ToggleContentBlock,
{
abilityKey,
isExpand,
},
);
});
};
};

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useShallow } from 'zustand/react/shallow';
import { TOOL_KEY_TO_API_STATUS_KEY_MAP } from '@coze-agent-ide/tool-config';
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
import { TabStatus } from '@coze-arch/bot-api/developer_api';
import { useRegisteredToolKeyConfigList } from '../../builtin/use-register-tool-key';
import { usePreference } from '../../../context/preference-context';
export const useIsAllToolHidden = () => {
const { isReadonly } = usePreference();
const botSkillBlockCollapsibleState = usePageRuntimeStore(
useShallow(state => state.botSkillBlockCollapsibleState),
);
const registeredToolKeyConfigList = useRegisteredToolKeyConfigList();
if (isReadonly) {
return registeredToolKeyConfigList.every(
toolConfig => !toolConfig.hasValidData,
);
}
const statusKeyMap = registeredToolKeyConfigList.map(
toolConfig => TOOL_KEY_TO_API_STATUS_KEY_MAP[toolConfig.toolKey],
);
return statusKeyMap.every(
statusKey => botSkillBlockCollapsibleState[statusKey] === TabStatus.Hide,
);
};

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TOOL_KEY_TO_API_STATUS_KEY_MAP } from '@coze-agent-ide/tool-config';
import { usePageRuntimeStore } from '@coze-studio/bot-detail-store/page-runtime';
import { TabStatus } from '@coze-arch/bot-api/developer_api';
import { useAbilityConfig } from '../../builtin/use-ability-config';
import { isToolKey } from '../../../utils/is-tool-key';
import { usePreference } from '../../../context/preference-context';
import { useAbilityAreaContext } from '../../../context/ability-area-context';
export const useToolValidData = () => {
const {
store: { useToolAreaStore },
} = useAbilityAreaContext();
const setToolHasValidData = useToolAreaStore(
state => state.setToolHasValidData,
);
const setBotSkillBlockCollapsibleState = usePageRuntimeStore(
state => state.setBotSkillBlockCollapsibleState,
);
const { abilityKey, scope } = useAbilityConfig();
const toolStatus = usePageRuntimeStore(state =>
abilityKey
? state.botSkillBlockCollapsibleState[
TOOL_KEY_TO_API_STATUS_KEY_MAP[abilityKey]
]
: null,
);
const { isReadonly } = usePreference();
return (hasValidData: boolean) => {
if (!isToolKey(abilityKey, scope)) {
return;
}
setToolHasValidData({
toolKey: abilityKey,
hasValidData,
});
/**
* 异常场景兜底,视图和服务端数据无法匹配,需要触发更新服务端数据
* 有数据但是隐藏状态
*/
if (toolStatus === TabStatus.Hide && hasValidData) {
setBotSkillBlockCollapsibleState(
{
[TOOL_KEY_TO_API_STATUS_KEY_MAP[abilityKey]]: TabStatus.Default,
},
isReadonly,
);
}
};
};

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useShallow } from 'zustand/react/shallow';
import {
AbilityScope,
type ToolKey,
type AgentSkillKey,
type AbilityKey,
} from '@coze-agent-ide/tool-config';
import { useEvent } from '../../event/use-event';
import { useAbilityConfig } from '../../builtin/use-ability-config';
import { generateError } from '../../../utils/error';
import { EventCenterEventName } from '../../../typings/scoped-events';
import { type IAbilityInitialedEventParams } from '../../../typings/event';
import { useAbilityAreaContext } from '../../../context/ability-area-context';
export const useInit = () => {
const { on, emit } = useEvent();
const { abilityKey, scope } = useAbilityConfig();
if (!abilityKey || !scope) {
throw generateError('AbilityKey or Scope is undefined');
}
const {
store: { useToolAreaStore, useAgentAreaStore },
} = useAbilityAreaContext();
const { registeredToolKeyConfigList, appendIntoInitialedToolKeyList } =
useToolAreaStore(
useShallow(state => ({
registeredToolKeyConfigList: state.registeredToolKeyConfigList,
appendIntoInitialedToolKeyList: state.appendIntoInitialedToolKeyList,
})),
);
const { registeredAgentSkillKeyList, appendIntoInitialedAgentSkillKeyList } =
useAgentAreaStore(
useShallow(state => ({
registeredAgentSkillKeyList: state.registeredAgentSkillKeyList,
appendIntoInitialedAgentSkillKeyList:
state.appendIntoInitialedAgentSkillKeyList,
})),
);
function markToolInitialed() {
let updatedInitialedAbilityKeyList: ToolKey[] | AgentSkillKey[] = [];
if (scope === AbilityScope.TOOL) {
appendIntoInitialedToolKeyList(abilityKey as unknown as ToolKey);
updatedInitialedAbilityKeyList =
useToolAreaStore.getState().initialedToolKeyList;
} else if (scope === AbilityScope.AGENT_SKILL) {
appendIntoInitialedAgentSkillKeyList(
abilityKey as unknown as AgentSkillKey,
);
updatedInitialedAbilityKeyList =
useAgentAreaStore.getState().initialedAgentSkillKeyList;
}
emit<IAbilityInitialedEventParams>(EventCenterEventName.AbilityInitialed, {
initialedAbilityKeyList: updatedInitialedAbilityKeyList,
});
}
function onToolInitialed(
listener: () => void,
listenedAbilityKey: AbilityKey,
) {
const offToolInitialed = on<IAbilityInitialedEventParams>(
EventCenterEventName.AbilityInitialed,
params => {
const { initialedAbilityKeyList } = params;
if (initialedAbilityKeyList.includes(listenedAbilityKey)) {
listener();
offToolInitialed();
}
},
);
}
function onAllToolInitialed(listener: (isAllInitialed: boolean) => void) {
const offToolInitialed = on<IAbilityInitialedEventParams>(
EventCenterEventName.AbilityInitialed,
params => {
const { initialedAbilityKeyList } = params;
let isAllRegisteredAbilityKeyInitialed = false;
if (scope === AbilityScope.TOOL) {
isAllRegisteredAbilityKeyInitialed =
registeredToolKeyConfigList.every(toolKeyConfig =>
initialedAbilityKeyList.includes(toolKeyConfig.toolKey),
);
} else if (scope === AbilityScope.AGENT_SKILL) {
isAllRegisteredAbilityKeyInitialed =
registeredAgentSkillKeyList.every(agentSkillKey =>
initialedAbilityKeyList.includes(agentSkillKey),
);
}
listener(isAllRegisteredAbilityKeyInitialed);
},
);
return offToolInitialed;
}
return {
markToolInitialed,
onToolInitialed,
onAllToolInitialed,
};
};

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// eslint-disable-next-line @coze-arch/no-pkg-dir-import
import { type AgentModalTabKey } from '@coze-agent-ide/tool-config/src/types';
import { useEvent } from '../../event/use-event';
import { EventCenterEventName } from '../../../typings/scoped-events';
import {
type IAgentModalTabChangeEventParams,
type IAgentModalVisibleChangeEventParams,
} from '../../../typings/event';
export const useAgentSkillModalCallbacks = () => {
const { on } = useEvent();
const onTabChange = (listener: (tabKey: AgentModalTabKey) => void) => {
on<IAgentModalTabChangeEventParams>(
EventCenterEventName.AgentModalTabChange,
params => {
const { tabKey } = params;
listener(tabKey);
},
);
};
const onModalVisibleChange = (listener: (isVisible: boolean) => void) => {
on<IAgentModalVisibleChangeEventParams>(
EventCenterEventName.AgentModalVisibleChange,
params => {
const { isVisible } = params;
listener(isVisible);
},
);
};
return {
onTabChange,
onModalVisibleChange,
};
};

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useEffect } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { size } from 'lodash-es';
import {
AbilityScope,
TOOL_KEY_STORE_MAP,
AGENT_SKILL_KEY_MAP,
} from '@coze-agent-ide/tool-config';
import { useMultiAgentStore } from '@coze-studio/bot-detail-store/multi-agent';
import {
type BotSkillAction,
type BotSkillStore,
useBotSkillStore,
} from '@coze-studio/bot-detail-store/bot-skill';
import { findTargetAgent } from '@coze-studio/bot-detail-store';
import { useAbilityStoreContext } from '../../store/use-ability-store-context';
import { useAbilityConfig } from '../../builtin/use-ability-config';
import { generateError } from '../../../utils/error';
const KEY_MAP = {
[AbilityScope.TOOL]: TOOL_KEY_STORE_MAP,
[AbilityScope.AGENT_SKILL]: AGENT_SKILL_KEY_MAP,
};
// 访问全局状态机中的状态
export function useToolStore<U>(selector: (state: BotSkillStore) => U): U {
const { abilityKey } = useAbilityConfig();
if (!abilityKey) {
throw generateError('not find abilityKey');
}
return useBotSkillStore(selector) as U;
}
// 访问全局状态机中的方法
export function useToolStoreAction<U>(
selector: (state: BotSkillAction) => U,
): U {
const { abilityKey } = useAbilityConfig();
if (!abilityKey) {
throw generateError('not find abilityKey');
}
return useBotSkillStore(selector) as U;
}
// 提交数据
export function useToolDispatch<T>() {
const { abilityKey, scope } = useAbilityConfig();
const { state, setState } = useAbilityStoreContext();
if (!abilityKey || !scope) {
throw generateError('not find abilityKey or scope');
}
return (newState: T) => {
setState({
[scope]: {
...state[scope],
// @ts-expect-error -- 以后想着解决一下这里的类型问题
[KEY_MAP[scope][abilityKey]]: newState,
},
});
};
}
// 监听 tool 状态机数据变化,并同步到 bot detail store
export function useSubscribeToolStore(scope: AbilityScope, agentId?: string) {
// bot detail store 更新方法
const { setBotSkill } = useBotSkillStore(
useShallow(state => ({
setBotSkill: state.setBotSkill,
})),
);
const { setMultiAgentByImmer } = useMultiAgentStore(
useShallow(state => ({
setMultiAgentByImmer: state.setMultiAgentByImmer,
})),
);
// tools store 数据
const { state } = useAbilityStoreContext();
const newState = state[scope];
// 同步数据
useEffect(() => {
if (size(newState)) {
if (!newState) {
return;
}
if (scope === AbilityScope.TOOL) {
setBotSkill(newState);
} else if (scope === AbilityScope.AGENT_SKILL) {
setMultiAgentByImmer(agentState => {
const agent = findTargetAgent(agentState.agents, agentId);
if (agent) {
agent.skills = newState;
}
});
}
}
}, [newState]);
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type FC,
type PropsWithChildren,
createContext,
useContext,
} from 'react';
import { noop } from 'lodash-es';
import { type IAbilityStoreState } from '../../typings/store';
interface IAbilityStoreContext {
state: IAbilityStoreState;
setState: (state: IAbilityStoreState) => void;
}
const AbilityStoreContext = createContext<IAbilityStoreContext>({
state: {},
setState: noop,
});
export const AbilityStoreProvider: FC<
PropsWithChildren<IAbilityStoreContext>
> = ({ children, state, setState }) => (
<AbilityStoreContext.Provider
value={{
state,
setState,
}}
>
{children}
</AbilityStoreContext.Provider>
);
export const useAbilityStoreContext = () => useContext(AbilityStoreContext);

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type AbilityKey } from '@coze-agent-ide/tool-config';
import { useEvent } from '../event/use-event';
import { EventCenterEventName } from '../../typings/scoped-events';
import { type IToggleContentBlockEventParams } from '../../typings/event';
/**
* 私有的hooks不对外暴露使用
* @returns
*/
export const useRegisterCollapse = () => {
const { on } = useEvent();
const registerCollapse = (
listener: (isExpand: boolean) => void,
abilityKey: AbilityKey,
) =>
on<IToggleContentBlockEventParams>(
EventCenterEventName.ToggleContentBlock,
params => {
const { abilityKey: currentAbilityKey, isExpand } = params;
if (abilityKey === currentAbilityKey) {
listener(isExpand);
}
},
);
return {
registerCollapse,
};
};