@ai-wendao/ant 0.5.8 → 0.5.9
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 +1 -1
- package/client.js +226 -92
- package/index.js +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Logo 与侧栏品牌使用 DSH 正式 UI 插槽。新会话标题目前没有官
|
|
|
20
20
|
|
|
21
21
|
飞书机器人连接和消息收发由 DSH-IM 负责;ANT Bundle 不保存机器人密钥,也不直连 IM 平台。成员在 ANT 插件的“协作”页选择机器人、启用本机协作,并设置私聊和各群的普通聊天开关。群第一次 @ 机器人后会出现在群列表。群聊天开关只控制普通聊天;任务卡还要单独指定私聊或群、请求人范围和有效期。
|
|
22
22
|
|
|
23
|
-
开放任务时,成员用自然语言描述什么时候执行、要看哪些资料、怎样处理和返回结果。描述中的 Gitea PR 链接、仓库和编号也用自然语言表达。点击“识别规则”会调用一次当前获准的 DSH
|
|
23
|
+
开放任务时,成员用自然语言描述什么时候执行、要看哪些资料、怎样处理和返回结果。描述中的 Gitea PR 链接、仓库和编号也用自然语言表达。点击“识别规则”会调用一次当前获准的 DSH 团队模型,只生成草稿,不保存任务;草稿包含明确触发短语,由机器人主人检查后再开放。触发短语会显示在任务概览顶部,允许机器人主人直接修改;修改描述后重新识别会更新摘要,保留手动调整的短语并提示复核。只有请求消息以已保存短语开头时任务才会执行。机器人主人指在本人 DSH 中连接并管理该 DSH-IM 机器人的 ANT 成员,不代表群管理员身份;群内谁能请求任务仍由规则中的群范围和请求人范围决定。任务名称、意图、PR 信息和其他参数由识别结果转成内部规则,不要求逐项编辑。执行方式、使用范围、请求人和有效期由固定控件控制;新任务有效期可选 1、7、30 或 90 天,编辑已有任务时保留原到期时间。
|
|
24
24
|
|
|
25
25
|
群任务和群聊天设置只在 ANT 协作页配置。当前版本不设群管理员身份绑定,也不支持在群里开放任务、确认授权或开关群聊。群成员可以普通聊天,或按已开放任务的群范围、请求人范围、触发条件和有效期发起任务;未开放的任务不会因为群成员的消息而执行。关闭群普通聊天不会撤销已开放任务。服务端升级时会停用没有明确触发方式的旧任务卡;要继续使用这类任务,需在新界面重新描述规则并开放。
|
|
26
26
|
|
package/client.js
CHANGED
|
@@ -533,7 +533,9 @@ window.__ModuleLoader__.load({
|
|
|
533
533
|
const [reviewers, setReviewers] = useState(['dsh'])
|
|
534
534
|
const [crossReviewAlways, setCrossReviewAlways] = useState(false)
|
|
535
535
|
const [parameters, setParameters] = useState([])
|
|
536
|
-
const [
|
|
536
|
+
const [multiAgentMode, setMultiAgentMode] = useState(false)
|
|
537
|
+
const [formError, setFormError] = useState(null)
|
|
538
|
+
const triggerPhrasesEditedRef = useRef(false)
|
|
537
539
|
const [giteaToken, setGiteaToken] = useState('')
|
|
538
540
|
const [tokenConfigured, setTokenConfigured] = useState(false)
|
|
539
541
|
const [senderScope, setSenderScope] = useState('any')
|
|
@@ -641,8 +643,18 @@ window.__ModuleLoader__.load({
|
|
|
641
643
|
const codexAvailable = executors.some((item) => item.id === 'codex-readonly' && item.available)
|
|
642
644
|
const claudeCodeAvailable = executors.some((item) => item.id === 'claude-code' && item.available)
|
|
643
645
|
const dshAvailable = executors.some((item) => item.id === 'dsh' && item.available)
|
|
644
|
-
const nativePrUnavailableReason = executors.find((item) => item.reason)?.reason || null
|
|
645
646
|
const reviewerLabel = (id) => ({ dsh: 'DSH', codex: 'Codex', 'claude-code': 'Claude Code' })[id] || id
|
|
647
|
+
const executorReason = (id) => {
|
|
648
|
+
if (loadFailures.includes('executors')) return '无法读取这台 DSH 的执行方状态,请刷新后重试。'
|
|
649
|
+
const reasons = []
|
|
650
|
+
if (['codex', 'claude-code'].includes(id) && !hasPrParameter) {
|
|
651
|
+
reasons.push('请在任务描述中写明 Gitea PR 链接或编号并重新识别')
|
|
652
|
+
}
|
|
653
|
+
const executorId = id === 'codex' ? 'codex-readonly' : id
|
|
654
|
+
const executor = executors.find((item) => item.id === executorId)
|
|
655
|
+
if (!executor?.available) reasons.push(executor?.reason || `这台 DSH 尚未启用 ${reviewerLabel(id)}`)
|
|
656
|
+
return reasons.join(';')
|
|
657
|
+
}
|
|
646
658
|
const cardReviewerSummary = (card) => {
|
|
647
659
|
const selected = Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner]
|
|
648
660
|
return selected.length > 1
|
|
@@ -657,8 +669,11 @@ window.__ModuleLoader__.load({
|
|
|
657
669
|
? `仅指定成员:${senderIds.trim() || '尚未填写成员 ID'}`
|
|
658
670
|
: conversationKind === 'group' ? '群内所有成员' : '任何能私聊此机器人的人'
|
|
659
671
|
const resultRouteSummary = conversationKind === 'group' ? '回复到原群(群内可见)' : '回复到发起任务的私聊'
|
|
660
|
-
const
|
|
661
|
-
const
|
|
672
|
+
const triggerPhraseEntries = triggerPhrases ? triggerPhrases.split('\n') : ['']
|
|
673
|
+
const ruleTriggerSummary = triggerPhraseEntries.map((item) => item.trim()).filter(Boolean)
|
|
674
|
+
const ruleSummaryReady = Boolean(title.trim() && intent.trim() && taskPrompt.trim())
|
|
675
|
+
const ruleDescriptionNeedsRecognition = recognizedDescriptionRef.current !== null
|
|
676
|
+
&& recognizedDescriptionRef.current !== ruleDescription.trim()
|
|
662
677
|
const parameterSummary = (rule) => {
|
|
663
678
|
const parts = [rule.required === false ? '可选' : '必填']
|
|
664
679
|
const choices = Array.isArray(rule.choices) ? rule.choices
|
|
@@ -683,26 +698,26 @@ window.__ModuleLoader__.load({
|
|
|
683
698
|
? `请求时还需提供:${parameters.map(parameterSummary).join(';')}`
|
|
684
699
|
: '无需额外参数,按请求中的内容处理'
|
|
685
700
|
const ruleSummaryNarrative = ruleSummaryReady
|
|
686
|
-
?
|
|
701
|
+
? intent.trim() || title.trim()
|
|
687
702
|
: '识别后会用一段摘要呈现系统理解的任务。'
|
|
688
|
-
const
|
|
689
|
-
?
|
|
690
|
-
:
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const crossReviewHeading = crossReviewAlways && reviewers.length > 1
|
|
695
|
-
? `每次交叉审查:${reviewers.map(reviewerLabel).join('、')}`
|
|
696
|
-
: crossReviewSummary ? `交叉审查:${crossReviewSummary}` : '交叉审查(可选)'
|
|
703
|
+
const reviewModeSummary = multiAgentMode || reviewers.length > 1
|
|
704
|
+
? reviewers.length < 2 ? '交叉审查尚未配置完成,至少再选择一位可用 Agent'
|
|
705
|
+
: crossReviewAlways
|
|
706
|
+
? `每次任务请求都并行运行 ${reviewers.map(reviewerLabel).join('、')}(会增加调用量)`
|
|
707
|
+
: `普通请求由 ${reviewerLabel(runner)} 执行;请求人明确提出“交叉 review”时并行运行 ${reviewers.map(reviewerLabel).join('、')}`
|
|
708
|
+
: `${reviewerLabel(runner)} 单 Agent 执行`
|
|
697
709
|
const cardBlockReason = enrollmentLoadError ? '暂时无法核对机器人是否已启用协作,请刷新状态后再试。'
|
|
698
710
|
: !botConnected || !selectedEnrollmentBound ? '先在上方把机器人绑定到这台 DSH。'
|
|
699
711
|
: loadFailures.includes('settings') || defaults?.task_rule_schema_version !== 2
|
|
700
712
|
? '团队服务端尚未支持触发规则的持久化与执行前核验;请先升级 ANT 服务端。'
|
|
701
713
|
: !ruleDescription.trim() ? '请先用自然语言描述要做的任务。'
|
|
702
|
-
:
|
|
714
|
+
: ruleDescriptionNeedsRecognition
|
|
703
715
|
? '描述已修改,请重新识别,让执行规则与描述保持一致。'
|
|
704
716
|
: !title.trim() || !intent.trim() || !taskPrompt.trim() ? '先识别任务描述并检查摘要,再开放任务。'
|
|
705
|
-
: !triggerPhrases.trim() ? '
|
|
717
|
+
: !triggerPhrases.trim() ? '请填写至少一个触发短语。'
|
|
718
|
+
: multiAgentMode && reviewers.length < 2 ? '交叉审查至少选择两位可用 Agent。'
|
|
719
|
+
: crossReviewAlways && defaults?.cross_review_policy_version !== 1
|
|
720
|
+
? '当前团队服务端尚不支持“每次都交叉审查”,请先升级 ANT 服务端。'
|
|
706
721
|
: conversationKind === 'group' && loadFailures.includes('groups') ? '暂时无法核对目标群,请刷新状态后再试。'
|
|
707
722
|
: conversationKind === 'group' && !groupId ? '请在“请求范围”中选择目标群;若列表为空,让群成员先 @机器人,再刷新群列表。'
|
|
708
723
|
: senderScope === 'allowlist' && !senderIds.trim() ? '请填写可请求成员的 IM ID。'
|
|
@@ -726,18 +741,60 @@ window.__ModuleLoader__.load({
|
|
|
726
741
|
const loadNames = { bots: '机器人', 'enrolled-bots': '协作启用状态', groups: '群聊列表',
|
|
727
742
|
cards: '任务授权', settings: '团队默认值', executors: '本机执行器',
|
|
728
743
|
'local-settings': '本机设置', status: '最近任务' }
|
|
744
|
+
const reportFormError = (field, text) => {
|
|
745
|
+
setFormError({ field, text })
|
|
746
|
+
if (typeof document !== 'undefined') setTimeout(() => {
|
|
747
|
+
const target = document.getElementById(field)
|
|
748
|
+
target?.scrollIntoView?.({ block: 'center', behavior: 'smooth' })
|
|
749
|
+
target?.focus?.()
|
|
750
|
+
}, 0)
|
|
751
|
+
}
|
|
752
|
+
const clearFormError = (field) => setFormError((current) => current?.field === field ? null : current)
|
|
753
|
+
const setTriggerPhraseAt = (index, value) => {
|
|
754
|
+
const next = [...triggerPhraseEntries]
|
|
755
|
+
next[index] = value
|
|
756
|
+
triggerPhrasesEditedRef.current = true
|
|
757
|
+
setTriggerPhrases(next.join('\n'))
|
|
758
|
+
clearFormError('ant-trigger-phrases')
|
|
759
|
+
}
|
|
760
|
+
const addTriggerPhrase = () => {
|
|
761
|
+
triggerPhrasesEditedRef.current = true
|
|
762
|
+
setTriggerPhrases([...triggerPhraseEntries, ''].join('\n'))
|
|
763
|
+
clearFormError('ant-trigger-phrases')
|
|
764
|
+
}
|
|
765
|
+
const removeTriggerPhrase = (index) => {
|
|
766
|
+
triggerPhrasesEditedRef.current = true
|
|
767
|
+
setTriggerPhrases(triggerPhraseEntries.filter((_item, itemIndex) => itemIndex !== index).join('\n'))
|
|
768
|
+
clearFormError('ant-trigger-phrases')
|
|
769
|
+
}
|
|
729
770
|
const changeReviewer = (id, enabled) => {
|
|
730
771
|
const next = enabled ? [...reviewers, id] : reviewers.filter((item) => item !== id)
|
|
731
|
-
|
|
732
|
-
setReviewers(
|
|
733
|
-
if (
|
|
734
|
-
if (
|
|
772
|
+
const unique = [...new Set(next)]
|
|
773
|
+
setReviewers(unique)
|
|
774
|
+
if (unique.length && !unique.includes(runner)) setRunner(unique[0])
|
|
775
|
+
if (multiAgentMode && unique.length < 2) {
|
|
776
|
+
reportFormError('ant-reviewer-list', '交叉审查至少选择两位可用 Agent。')
|
|
777
|
+
setCrossReviewAlways(false)
|
|
778
|
+
} else clearFormError('ant-reviewer-list')
|
|
735
779
|
}
|
|
736
780
|
const selectRunner = (id) => {
|
|
737
781
|
setRunner(id)
|
|
738
|
-
setReviewers((current) =>
|
|
739
|
-
?
|
|
782
|
+
setReviewers((current) => multiAgentMode
|
|
783
|
+
? current.includes(id) ? current : [...current, id]
|
|
740
784
|
: [id])
|
|
785
|
+
clearFormError('ant-agent-mode')
|
|
786
|
+
}
|
|
787
|
+
const selectExecutionMode = (mode) => {
|
|
788
|
+
if (mode === 'single') {
|
|
789
|
+
setMultiAgentMode(false)
|
|
790
|
+
setReviewers([runner])
|
|
791
|
+
setCrossReviewAlways(false)
|
|
792
|
+
} else {
|
|
793
|
+
setMultiAgentMode(true)
|
|
794
|
+
if (reviewers.length < 2) setCrossReviewAlways(false)
|
|
795
|
+
}
|
|
796
|
+
clearFormError('ant-agent-mode')
|
|
797
|
+
clearFormError('ant-reviewer-list')
|
|
741
798
|
}
|
|
742
799
|
const startNewCard = (context = {}) => {
|
|
743
800
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
@@ -745,10 +802,12 @@ window.__ModuleLoader__.load({
|
|
|
745
802
|
setRecognitionMessage('')
|
|
746
803
|
recognizedBaselineRef.current = null
|
|
747
804
|
recognizedDescriptionRef.current = null
|
|
805
|
+
triggerPhrasesEditedRef.current = false
|
|
806
|
+
setFormError(null)
|
|
748
807
|
setRuleDescription('')
|
|
749
808
|
setTriggerPhrases('')
|
|
750
809
|
setTitle(''); setIntent(''); setTaskPrompt(''); setRunner('dsh')
|
|
751
|
-
setReviewers(['dsh']); setCrossReviewAlways(false);
|
|
810
|
+
setReviewers(['dsh']); setCrossReviewAlways(false); setMultiAgentMode(false)
|
|
752
811
|
setParameters([]); setConversationKind(context.conversationKind || 'dm')
|
|
753
812
|
setGroupId(context.groupId || '')
|
|
754
813
|
setSenderScope('any'); setSenderIds(''); setDays(7); setCreateOpen(true)
|
|
@@ -768,6 +827,8 @@ window.__ModuleLoader__.load({
|
|
|
768
827
|
const editCard = (card, copy = false) => {
|
|
769
828
|
setBotId(card.bot_id)
|
|
770
829
|
setRecognitionMessage('')
|
|
830
|
+
setFormError(null)
|
|
831
|
+
triggerPhrasesEditedRef.current = false
|
|
771
832
|
setTrustedOrigin(enrolled.find((bot) => bot.bot_id === card.bot_id)?.gitea_origin || '')
|
|
772
833
|
setEditingCardId(copy ? null : card.id); setEditingExpiresAt(copy ? null : card.expires_at)
|
|
773
834
|
const taskDraft = taskRuleDraftFromCard(card, copy ? `${card.title.slice(0, 94)}(副本)` : card.title)
|
|
@@ -784,9 +845,10 @@ window.__ModuleLoader__.load({
|
|
|
784
845
|
if (copy) setDays(7)
|
|
785
846
|
setIntent(taskDraft.intent)
|
|
786
847
|
setTaskPrompt(taskDraft.task_prompt); setRunner(card.runner)
|
|
787
|
-
|
|
848
|
+
const selectedReviewers = Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner]
|
|
849
|
+
setReviewers(selectedReviewers)
|
|
850
|
+
setMultiAgentMode(selectedReviewers.length > 1)
|
|
788
851
|
setCrossReviewAlways(card.cross_review_always === true)
|
|
789
|
-
setCrossReviewOpen(false)
|
|
790
852
|
setParameters(taskDraft.parameters)
|
|
791
853
|
setConversationKind(card.conversation_kind); setGroupId(card.conversation_id || '')
|
|
792
854
|
setSenderScope(card.sender_scope); setSenderIds((card.sender_ids || []).join(', '))
|
|
@@ -795,7 +857,7 @@ window.__ModuleLoader__.load({
|
|
|
795
857
|
const applyRuleDraft = (draft) => {
|
|
796
858
|
setTitle(draft.title)
|
|
797
859
|
setIntent(draft.intent)
|
|
798
|
-
setTriggerPhrases(draft.trigger_phrases.join('\n'))
|
|
860
|
+
if (!triggerPhrasesEditedRef.current) setTriggerPhrases(draft.trigger_phrases.join('\n'))
|
|
799
861
|
setTaskPrompt(draft.task_prompt)
|
|
800
862
|
setParameters(draft.parameters)
|
|
801
863
|
}
|
|
@@ -804,7 +866,7 @@ window.__ModuleLoader__.load({
|
|
|
804
866
|
setRecognitionMessage('先描述这项任务何时触发、要做什么以及涉及哪些资料。')
|
|
805
867
|
return
|
|
806
868
|
}
|
|
807
|
-
setRecognitionBusy(true);
|
|
869
|
+
setRecognitionBusy(true); setFormError(null); setRecognitionMessage('正在调用团队模型识别规则…')
|
|
808
870
|
try {
|
|
809
871
|
const response = await fetch('/api/ant-teams/delegation/recognize', {
|
|
810
872
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
@@ -837,9 +899,11 @@ window.__ModuleLoader__.load({
|
|
|
837
899
|
applyRuleDraft(candidate)
|
|
838
900
|
recognizedBaselineRef.current = candidate
|
|
839
901
|
recognizedDescriptionRef.current = ruleDescription.trim()
|
|
840
|
-
setRecognitionMessage(
|
|
841
|
-
? '
|
|
842
|
-
:
|
|
902
|
+
setRecognitionMessage(triggerPhrasesEditedRef.current
|
|
903
|
+
? '已更新任务草稿;保留了手动调整的触发短语,请核对它仍符合新描述。'
|
|
904
|
+
: previous
|
|
905
|
+
? '已根据新描述更新草稿,请检查摘要和授权范围。'
|
|
906
|
+
: '已识别为任务草稿。检查摘要和授权范围;此操作不会开放任务。')
|
|
843
907
|
} catch (error) { setRecognitionMessage(error.message) }
|
|
844
908
|
finally { setRecognitionBusy(false) }
|
|
845
909
|
}
|
|
@@ -980,7 +1044,7 @@ window.__ModuleLoader__.load({
|
|
|
980
1044
|
return focusAfterTabChange('collaboration', 'ant-new-task-summary')
|
|
981
1045
|
}
|
|
982
1046
|
setCreateOpen(true)
|
|
983
|
-
focusAfterTabChange('collaboration', 'ant-
|
|
1047
|
+
focusAfterTabChange('collaboration', 'ant-open-confirm')
|
|
984
1048
|
}
|
|
985
1049
|
const collaborationGridStyle = {
|
|
986
1050
|
display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 300px), 1fr))', gap: 12,
|
|
@@ -1025,25 +1089,28 @@ window.__ModuleLoader__.load({
|
|
|
1025
1089
|
const createCard = async (event) => {
|
|
1026
1090
|
event.preventDefault()
|
|
1027
1091
|
setDraftNotice('')
|
|
1028
|
-
|
|
1092
|
+
setFormError(null)
|
|
1093
|
+
if (!selectedBot || !botConnected || !selectedEnrollmentBound) {
|
|
1094
|
+
reportFormError('ant-bot-selector', '请先在这台 DSH 绑定选中的协作机器人。'); return
|
|
1095
|
+
}
|
|
1029
1096
|
if (crossReviewAlways && defaults?.cross_review_policy_version !== 1) {
|
|
1030
|
-
|
|
1097
|
+
reportFormError('ant-open-confirm', '当前团队服务端尚不支持“每次都交叉审查”,请先升级 ANT 服务端。'); return
|
|
1031
1098
|
}
|
|
1032
1099
|
if (editingCardId && (!editingExpiresAt || editingExpiresAt <= Math.floor(Date.now() / 1000))) {
|
|
1033
|
-
|
|
1100
|
+
reportFormError('ant-open-confirm', '这项能力已到期,无法继续编辑;请返回后重新开放一项能力。'); return
|
|
1034
1101
|
}
|
|
1035
1102
|
if (!ruleDescription.trim() || !title.trim() || !intent.trim() || !taskPrompt.trim()) {
|
|
1036
|
-
|
|
1103
|
+
reportFormError('ant-rule-description', '请填写任务描述并点击“识别规则”,检查摘要后再开放。'); return
|
|
1037
1104
|
}
|
|
1038
1105
|
const bytes = (value) => new TextEncoder().encode(value).length
|
|
1039
1106
|
if (bytes(ruleDescription.trim()) > 4000 || bytes(title.trim()) > 100
|
|
1040
1107
|
|| bytes(intent.trim()) > 1000 || bytes(taskPrompt.trim()) > 4000) {
|
|
1041
|
-
|
|
1108
|
+
reportFormError('ant-rule-description', '任务描述或识别摘要过长,请缩短描述并重新识别。'); return
|
|
1042
1109
|
}
|
|
1043
1110
|
const triggerLines = triggerPhrases.split('\n').map((item) => item.normalize('NFKC').trim()).filter(Boolean)
|
|
1044
1111
|
if (!triggerLines.length || triggerLines.length > 8 || triggerLines.some((item) => bytes(item) > 120)
|
|
1045
1112
|
|| new Set(triggerLines.map((item) => item.toLowerCase())).size !== triggerLines.length) {
|
|
1046
|
-
|
|
1113
|
+
reportFormError('ant-trigger-phrases', '触发短语需填写 1–8 条,每条不超过 120 字节且不能重复。'); return
|
|
1047
1114
|
}
|
|
1048
1115
|
const normalized = parameters.map((rule) => ({ name: rule.name.trim(), kind: rule.kind,
|
|
1049
1116
|
description: rule.description.trim() || null,
|
|
@@ -1054,18 +1121,18 @@ window.__ModuleLoader__.load({
|
|
|
1054
1121
|
gitea_origin: rule.kind === 'gitea_pr' ? selectedEnrollment?.gitea_origin || null : null,
|
|
1055
1122
|
repository: rule.kind === 'gitea_pr' ? rule.repository.trim().toLowerCase() : null }))
|
|
1056
1123
|
if (normalized.some((rule) => !/^[A-Za-z][A-Za-z0-9_]{0,31}$/u.test(rule.name))) {
|
|
1057
|
-
|
|
1124
|
+
reportFormError('ant-rule-description', '没能从描述中整理出有效的请求信息。请说明同事需要提供什么,再重新识别。'); return
|
|
1058
1125
|
}
|
|
1059
1126
|
if (new Set(normalized.map((rule) => rule.name)).size !== normalized.length) {
|
|
1060
|
-
|
|
1127
|
+
reportFormError('ant-rule-description', '请求信息识别有重复项。请修改任务描述并重新识别。'); return
|
|
1061
1128
|
}
|
|
1062
1129
|
if (normalized.some((rule) => rule.description && bytes(rule.description) > 100)) {
|
|
1063
|
-
|
|
1130
|
+
reportFormError('ant-rule-description', '请求信息说明过长。请缩短任务描述并重新识别。'); return
|
|
1064
1131
|
}
|
|
1065
1132
|
if (normalized.some((rule) => rule.kind === 'choice' && (!rule.choices?.length || rule.choices.length > 32
|
|
1066
1133
|
|| rule.choices.some((choice) => bytes(choice) > 100)
|
|
1067
1134
|
|| new Set(rule.choices).size !== rule.choices.length))) {
|
|
1068
|
-
|
|
1135
|
+
reportFormError('ant-rule-description', '描述中的可选范围不清楚或重复。请修改描述并重新识别。'); return
|
|
1069
1136
|
}
|
|
1070
1137
|
if (normalized.some((rule) => (rule.min != null && !Number.isInteger(rule.min))
|
|
1071
1138
|
|| (rule.max != null && !Number.isInteger(rule.max))
|
|
@@ -1073,10 +1140,10 @@ window.__ModuleLoader__.load({
|
|
|
1073
1140
|
|| (rule.kind === 'text' && rule.max != null && (rule.max < 1 || rule.max > 4096))
|
|
1074
1141
|
|| (rule.kind === 'gitea_pr' && ((rule.min != null && rule.min < 1)
|
|
1075
1142
|
|| (rule.max != null && rule.max > 1_000_000_000))))) {
|
|
1076
|
-
|
|
1143
|
+
reportFormError('ant-rule-description', '描述中的请求范围无效。请明确范围或提供完整 PR 链接,再重新识别。'); return
|
|
1077
1144
|
}
|
|
1078
1145
|
if (normalized.some((rule) => rule.kind === 'gitea_pr' && (!rule.gitea_origin || !rule.repository))) {
|
|
1079
|
-
|
|
1146
|
+
reportFormError('ant-rule-description', '没能从描述中识别 PR 来源或仓库。请补充完整 PR 链接或仓库名,再重新识别。'); return
|
|
1080
1147
|
}
|
|
1081
1148
|
const validRepository = (value) => {
|
|
1082
1149
|
const parts = value.split('/')
|
|
@@ -1084,14 +1151,17 @@ window.__ModuleLoader__.load({
|
|
|
1084
1151
|
&& /^[A-Za-z0-9_.-]{1,100}$/u.test(part))
|
|
1085
1152
|
}
|
|
1086
1153
|
if (normalized.some((rule) => rule.kind === 'gitea_pr' && !validRepository(rule.repository))) {
|
|
1087
|
-
|
|
1154
|
+
reportFormError('ant-rule-description', '仓库名无法识别。请在描述中写明“团队名/仓库名”或粘贴完整 PR 链接,再重新识别。'); return
|
|
1088
1155
|
}
|
|
1089
|
-
if (!reviewers.length || !reviewers.includes(runner)
|
|
1090
|
-
|
|
1156
|
+
if (!reviewers.length || !reviewers.includes(runner)
|
|
1157
|
+
|| (multiAgentMode && reviewers.length < 2)
|
|
1158
|
+
|| (!multiAgentMode && reviewers.length !== 1)) {
|
|
1159
|
+
reportFormError('ant-agent-mode', multiAgentMode
|
|
1160
|
+
? '交叉审查至少选择两位可用 Agent。' : '单 Agent 模式请选择一位执行 Agent。'); return
|
|
1091
1161
|
}
|
|
1092
1162
|
if (nativeReviewerSelected && (normalized.length !== 1
|
|
1093
1163
|
|| normalized[0].kind !== 'gitea_pr' || !normalized[0].required)) {
|
|
1094
|
-
|
|
1164
|
+
reportFormError('ant-rule-description', 'Codex 和 Claude Code 目前只支持审查一个 PR。请在任务描述中写明 PR 链接,再重新识别。'); return
|
|
1095
1165
|
}
|
|
1096
1166
|
const scope = conversationKind === 'group' ? `群 ${groupId}` : '私聊'
|
|
1097
1167
|
const resultDestination = conversationKind === 'group' ? '原群(群内可见)' : '发起任务的私聊'
|
|
@@ -1100,14 +1170,11 @@ window.__ModuleLoader__.load({
|
|
|
1100
1170
|
codex: 'Codex 使用已核验 PR Head 的临时代码快照,并启用本机固定只读权限;不接收 Gitea 令牌。',
|
|
1101
1171
|
'claude-code': 'Claude Code 使用同一份临时代码快照,沿用这台 DSH 的本机权限设置;请确认该权限适合由获准成员请求审查。',
|
|
1102
1172
|
}
|
|
1103
|
-
const executionInfo = reviewers.
|
|
1104
|
-
|
|
1105
|
-
? `每次任务请求都会并行启动 ${reviewers.map(reviewerLabel).join('、')},分别返回审查结果,无需请求人额外提出交叉 review。${reviewers.map((item) => executionDetails[item]).join(' ')}`
|
|
1106
|
-
: `普通请求默认由 ${reviewerLabel(runner)} 执行;请求人明确说“交叉 review”时,会并行启动 ${reviewers.map(reviewerLabel).join('、')},分别返回审查结果。${reviewers.map((item) => executionDetails[item]).join(' ')}`
|
|
1107
|
-
: executionDetails[reviewers[0]]
|
|
1173
|
+
const executionInfo = `${reviewModeSummary}。${reviewers.map((item) => executionDetails[item]).join(' ')}`
|
|
1174
|
+
const triggerSummary = `消息以“${triggerLines.join('”或“')}”开头时触发。`
|
|
1108
1175
|
const actionDescription = editingCardId
|
|
1109
|
-
? `更新“${title}”后,${requesterSummary}可在${scope}
|
|
1110
|
-
: `开放“${title}”后,${requesterSummary}可在${scope}
|
|
1176
|
+
? `更新“${title}”后,${requesterSummary}可在${scope}按任务描述中的规则发起请求。${triggerSummary}旧待确认请求将失效,已提交任务继续使用提交时的配置。结果${resultDestination}。到期时间保持为 ${new Date(editingExpiresAt * 1000).toLocaleString('zh-CN')}。`
|
|
1177
|
+
: `开放“${title}”后,${requesterSummary}可在${scope}按任务描述中的规则发起请求。${triggerSummary}有效期 ${days} 天;结果${resultDestination}。`
|
|
1111
1178
|
if (!window.confirm(`${actionDescription}${executionInfo}请确认。`)) return
|
|
1112
1179
|
setBusy(true)
|
|
1113
1180
|
try {
|
|
@@ -1150,17 +1217,18 @@ window.__ModuleLoader__.load({
|
|
|
1150
1217
|
}
|
|
1151
1218
|
recognizedBaselineRef.current = null
|
|
1152
1219
|
recognizedDescriptionRef.current = null
|
|
1220
|
+
triggerPhrasesEditedRef.current = false
|
|
1153
1221
|
setRuleDescription(''); setTriggerPhrases('')
|
|
1154
1222
|
setTitle(''); setIntent(''); setTaskPrompt(''); setParameters([]); setRunner('dsh'); setReviewers(['dsh'])
|
|
1155
1223
|
setCrossReviewAlways(false)
|
|
1156
|
-
|
|
1224
|
+
setMultiAgentMode(false)
|
|
1157
1225
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1158
1226
|
await load()
|
|
1159
1227
|
setCreateOpen(false)
|
|
1160
1228
|
setMessage(editingCardId
|
|
1161
1229
|
? '能力配置已更新。新请求将使用新配置,已提交任务保留原配置。'
|
|
1162
1230
|
: '任务已开放。机器人会按已确认的描述判断请求;缺少必要信息时会向请求人追问。')
|
|
1163
|
-
} catch (error) {
|
|
1231
|
+
} catch (error) { reportFormError('ant-open-confirm', error.message) }
|
|
1164
1232
|
finally { setBusy(false) }
|
|
1165
1233
|
}
|
|
1166
1234
|
const revokeCard = async (card) => {
|
|
@@ -1177,7 +1245,7 @@ window.__ModuleLoader__.load({
|
|
|
1177
1245
|
}
|
|
1178
1246
|
const copyCardToDraft = (card) => {
|
|
1179
1247
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1180
|
-
|
|
1248
|
+
setFormError(null); triggerPhrasesEditedRef.current = false
|
|
1181
1249
|
const hasTriggerRule = Array.isArray(card.trigger_phrases) && card.trigger_phrases.length > 0
|
|
1182
1250
|
const copiedDescription = card.rule_description || card.intent || ''
|
|
1183
1251
|
recognizedDescriptionRef.current = hasTriggerRule ? copiedDescription.trim() || null : null
|
|
@@ -1198,7 +1266,9 @@ window.__ModuleLoader__.load({
|
|
|
1198
1266
|
setIntent(taskDraft.intent)
|
|
1199
1267
|
setTaskPrompt(taskDraft.task_prompt)
|
|
1200
1268
|
setRunner(['dsh', 'codex', 'claude-code'].includes(card.runner) ? card.runner : 'dsh')
|
|
1201
|
-
|
|
1269
|
+
const copiedReviewers = Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner || 'dsh']
|
|
1270
|
+
setReviewers(copiedReviewers)
|
|
1271
|
+
setMultiAgentMode(copiedReviewers.length > 1)
|
|
1202
1272
|
setCrossReviewAlways(card.cross_review_always === true)
|
|
1203
1273
|
setParameters(taskDraft.parameters)
|
|
1204
1274
|
setConversationKind(card.conversation_kind === 'group' ? 'group' : 'dm')
|
|
@@ -1353,6 +1423,8 @@ window.__ModuleLoader__.load({
|
|
|
1353
1423
|
? h('span', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, card.rule_description)
|
|
1354
1424
|
: h('span', { role: 'status', style: { ...mutedTextStyle, color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1355
1425
|
'旧规则没有明确触发方式;新版插件不会按它启动任务。升级 ANT 服务端后需重新描述并开放。'),
|
|
1426
|
+
Array.isArray(card.trigger_phrases) && card.trigger_phrases.length
|
|
1427
|
+
? h('span', { style: { overflowWrap: 'anywhere' } }, '触发短语:' + card.trigger_phrases.join('、')) : null,
|
|
1356
1428
|
h('details', null,
|
|
1357
1429
|
h('summary', { style: { cursor: 'pointer', color: 'var(--dsw-alias-link)', fontSize: 13 } }, '查看规则说明'),
|
|
1358
1430
|
h('div', { style: { display: 'grid', gap: 4, paddingTop: 6 } },
|
|
@@ -1412,6 +1484,7 @@ window.__ModuleLoader__.load({
|
|
|
1412
1484
|
h('p', { style: mutedTextStyle }, selectedBot
|
|
1413
1485
|
? `当前机器人:${selectedBot.name}${selectedEnrollmentBound ? ' · 已绑定此设备' : ' · 请先绑定到此设备'}`
|
|
1414
1486
|
: '请先在上方选择机器人并启用协作。'),
|
|
1487
|
+
formError?.field === 'ant-bot-selector' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1415
1488
|
draftNotice ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1416
1489
|
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } }, draftNotice) : null,
|
|
1417
1490
|
selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
@@ -1420,29 +1493,68 @@ window.__ModuleLoader__.load({
|
|
|
1420
1493
|
'这是尚未绑定到设备的机器人。请在要继续使用它的电脑重新启用;Gitea 地址和配置记录会保留。若旧设备访问曾被撤销,原任务权限不会恢复,需要重新开放。') : null,
|
|
1421
1494
|
selectedBot && conversationKind === 'group' && !selectedBot.history_available ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1422
1495
|
'这个机器人当前不能读取平台群历史;群聊只能参考实际送达的消息和本机近期记录。请检查 DSH-IM 权限。') : null,
|
|
1423
|
-
h('
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1496
|
+
h('section', { id: 'ant-task-context', role: 'region', 'aria-label': '当前任务内容',
|
|
1497
|
+
style: { display: 'grid', gap: 8, padding: 12, borderLeft: '4px solid var(--dsw-alias-state-business-primary)',
|
|
1498
|
+
backgroundColor: 'var(--dsw-alias-bg-layer-1)', position: 'sticky', top: 0, zIndex: 2 } },
|
|
1499
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' } },
|
|
1500
|
+
h('strong', { style: { fontSize: 17 } }, title || (ruleSummaryReady ? '识别后的任务' : '当前任务')),
|
|
1501
|
+
h('span', { style: ruleDescriptionNeedsRecognition ? { ...mutedTextStyle,
|
|
1502
|
+
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } : mutedTextStyle },
|
|
1503
|
+
ruleDescriptionNeedsRecognition ? '描述已修改 · 需重新识别'
|
|
1504
|
+
: ruleSummaryReady ? '草稿 · 尚未开放' : '填写描述并识别')),
|
|
1505
|
+
h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere', display: '-webkit-box',
|
|
1506
|
+
WebkitBoxOrient: 'vertical', WebkitLineClamp: 2, overflow: 'hidden' } }, ruleSummaryNarrative)),
|
|
1507
|
+
h('div', { style: { display: 'grid', gap: 4, padding: '0 4px' } },
|
|
1508
|
+
h('strong', null, '识别详情'),
|
|
1509
|
+
h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, taskPrompt.trim() || '执行要求会在识别后显示。'),
|
|
1510
|
+
h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, recognizedInputsSummary)),
|
|
1511
|
+
h('section', { style: { display: 'grid', gap: 6, padding: 10, borderRadius: 8,
|
|
1512
|
+
border: '1px solid var(--dsw-alias-border-l2)', backgroundColor: 'var(--dsw-alias-bg-layer-1)' } },
|
|
1513
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' } },
|
|
1514
|
+
h('strong', null, '触发短语(必填)'), h('span', { style: mutedTextStyle }, '模型建议 · 可修改')),
|
|
1515
|
+
...triggerPhraseEntries.map((phrase, index) => h('div', {
|
|
1516
|
+
key: 'trigger-' + index, style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
|
|
1517
|
+
h('input', { id: index === 0 ? 'ant-trigger-phrases' : undefined,
|
|
1518
|
+
type: 'text', 'aria-label': '触发短语 ' + (index + 1), value: phrase,
|
|
1519
|
+
'aria-invalid': formError?.field === 'ant-trigger-phrases' ? 'true' : undefined,
|
|
1520
|
+
maxLength: 120, placeholder: '填写触发短语', style: { ...inputStyle, flex: '1 1 220px' },
|
|
1521
|
+
onChange: (event) => setTriggerPhraseAt(index, event.target.value) }),
|
|
1522
|
+
triggerPhraseEntries.length > 1 ? h('button', { type: 'button', disabled: busy,
|
|
1523
|
+
onClick: () => removeTriggerPhrase(index), style: secondaryButtonStyle }, '移除') : null)),
|
|
1524
|
+
h('button', { type: 'button', disabled: busy || triggerPhraseEntries.length >= 8,
|
|
1525
|
+
onClick: addTriggerPhrase, style: { ...secondaryButtonStyle, width: 'fit-content' } }, '添加触发短语'),
|
|
1526
|
+
formError?.field === 'ant-trigger-phrases' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1527
|
+
h('p', { style: mutedTextStyle }, '只有消息以这里的短语开头才会触发任务。模型会根据描述给出建议,也可以直接调整。')),
|
|
1528
|
+
h('div', { style: { padding: 10, borderRadius: 8, backgroundColor: 'var(--dsw-alias-bg-layer-2)' } },
|
|
1529
|
+
h('strong', null, '描述提示:'),
|
|
1530
|
+
h('span', null, '写清触发消息以什么开头、要做什么、需要哪些资料和结果怎样返回。识别只整理草稿,开放前请核对。')),
|
|
1531
|
+
h('label', { htmlFor: 'ant-rule-description' }, '描述规则', h('textarea', {
|
|
1532
|
+
id: 'ant-rule-description', required: true, maxLength: 4000, rows: 4, value: ruleDescription,
|
|
1533
|
+
'aria-invalid': formError?.field === 'ant-rule-description' ? 'true' : undefined,
|
|
1534
|
+
onChange: (event) => { setRuleDescription(event.target.value); setRecognitionMessage(''); clearFormError('ant-rule-description') },
|
|
1535
|
+
placeholder: '用日常的话说明什么时候触发、任务要做什么、需要哪些资料以及结果怎么返回。', style: inputStyle })),
|
|
1536
|
+
formError?.field === 'ant-rule-description' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1427
1537
|
h('div', { style: { display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' } },
|
|
1428
1538
|
h('button', { type: 'button', disabled: recognitionBusy || busy || !ruleDescription.trim(),
|
|
1429
1539
|
onClick: () => void identifyRule(), style: secondaryButtonStyle }, recognitionBusy ? '正在识别…' : ruleSummaryReady ? '重新识别' : '识别规则'),
|
|
1430
1540
|
h('span', { style: mutedTextStyle }, '每次点击调用一次团队模型并计入用量;只整理草稿,不会开放任务。改完描述后可重新识别。')),
|
|
1431
1541
|
recognitionMessage ? h('p', { role: 'status', 'aria-live': 'polite', style: feedbackNoticeStyle }, recognitionMessage) : null,
|
|
1432
|
-
h('section', { role: 'region', 'aria-label': '识别结果摘要', style: { ...panelStyle, padding: 10, gap: 7 } },
|
|
1433
|
-
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
1434
|
-
h('strong', null, ruleSummaryReady ? (title || '识别结果') : '识别结果预览'),
|
|
1435
|
-
h('span', { style: mutedTextStyle }, ruleSummaryReady ? '草稿 · 尚未开放' : '填写描述后可识别')),
|
|
1436
|
-
h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, ruleSummaryNarrative),
|
|
1437
|
-
h('p', { style: mutedTextStyle }, ruleSummaryReady
|
|
1438
|
-
? '草稿未开放。检查这段理解;有误就改上方描述并重新识别。'
|
|
1439
|
-
: '填写描述后识别;识别只生成草稿,不会开放任务。')),
|
|
1440
1542
|
h('section', { style: { ...panelStyle, padding: 10, gap: 9, display: 'grid',
|
|
1441
1543
|
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 280px), 1fr))' } },
|
|
1442
|
-
h('strong', { style: { gridColumn: '1 / -1' } }, '
|
|
1544
|
+
h('strong', { style: { gridColumn: '1 / -1' } }, 'Agent 运行方式'),
|
|
1443
1545
|
h('p', { style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1444
|
-
'
|
|
1445
|
-
h('
|
|
1546
|
+
'选择一位 Agent 处理任务,或选择多位 Agent 按需/每次并行审查。'),
|
|
1547
|
+
h('div', { id: 'ant-agent-mode', role: 'group', 'aria-label': 'Agent 运行方式', tabIndex: -1,
|
|
1548
|
+
style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
|
|
1549
|
+
h('button', { type: 'button', 'aria-pressed': !multiAgentMode,
|
|
1550
|
+
onClick: () => selectExecutionMode('single'),
|
|
1551
|
+
style: !multiAgentMode ? primaryButtonStyle : secondaryButtonStyle }, '单 Agent'),
|
|
1552
|
+
h('button', { type: 'button', 'aria-pressed': multiAgentMode,
|
|
1553
|
+
onClick: () => selectExecutionMode('multi'),
|
|
1554
|
+
style: multiAgentMode ? primaryButtonStyle : secondaryButtonStyle }, '多 Agent 交叉审查')),
|
|
1555
|
+
formError?.field === 'ant-agent-mode' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1556
|
+
multiAgentMode && crossReviewAlways ? null : h('label', null,
|
|
1557
|
+
multiAgentMode ? '普通请求默认由' : '执行 Agent', h('select', { value: runner, onChange: (event) => selectRunner(event.target.value),
|
|
1446
1558
|
style: inputStyle },
|
|
1447
1559
|
h('option', { value: 'dsh', disabled: !dshAvailable && runner !== 'dsh' }, 'DSH · 当前团队模型'),
|
|
1448
1560
|
h('option', { value: 'codex', disabled: (!codexAvailable || !hasPrParameter) && runner !== 'codex' }, 'Codex · PR 快照只读'),
|
|
@@ -1451,11 +1563,15 @@ window.__ModuleLoader__.load({
|
|
|
1451
1563
|
? 'Codex 使用本机登录,仅读取已核验的 PR 快照。'
|
|
1452
1564
|
: runner === 'claude-code' ? 'Claude Code 沿用这台 DSH 的权限设置。'
|
|
1453
1565
|
: 'DSH 使用获准团队模型处理会话中可见或已提供的内容。')),
|
|
1454
|
-
h('
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1566
|
+
!multiAgentMode && !dshAvailable ? h('p', { role: 'status', style: mutedTextStyle }, executorReason('dsh')) : null,
|
|
1567
|
+
!multiAgentMode && (!codexAvailable || !hasPrParameter) ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1568
|
+
`Codex 暂不可选:${executorReason('codex')}`) : null,
|
|
1569
|
+
!multiAgentMode && (!claudeCodeAvailable || !hasPrParameter) ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1570
|
+
`Claude Code 暂不可选:${executorReason('claude-code')}`) : null,
|
|
1571
|
+
h('div', { id: 'ant-reviewer-list', role: 'group', 'aria-label': '交叉审查 Agent', tabIndex: -1,
|
|
1572
|
+
style: { gridColumn: '1 / -1', display: multiAgentMode ? 'grid' : 'none', gap: 8 } },
|
|
1573
|
+
h('strong', null, '参与交叉审查的 Agent(至少选择两位)'),
|
|
1574
|
+
h('p', { style: mutedTextStyle }, '请求人需要在触发短语开头的任务消息中明确提出“交叉 review”,按需策略才会并行运行。'),
|
|
1459
1575
|
h('div', { style: { display: 'flex', gap: 14, flexWrap: 'wrap' } },
|
|
1460
1576
|
h('label', null, h('input', { type: 'checkbox', 'aria-label': '审查 Agent DSH', checked: reviewers.includes('dsh'),
|
|
1461
1577
|
disabled: !dshAvailable && !reviewers.includes('dsh'),
|
|
@@ -1466,6 +1582,15 @@ window.__ModuleLoader__.load({
|
|
|
1466
1582
|
h('label', null, h('input', { type: 'checkbox', 'aria-label': '审查 Agent Claude Code', checked: reviewers.includes('claude-code'),
|
|
1467
1583
|
disabled: (!claudeCodeAvailable || !hasPrParameter) && !reviewers.includes('claude-code'),
|
|
1468
1584
|
onChange: (event) => changeReviewer('claude-code', event.target.checked) }), ' Claude Code')),
|
|
1585
|
+
!dshAvailable ? h('p', { role: 'status', style: mutedTextStyle }, executorReason('dsh')) : null,
|
|
1586
|
+
!codexAvailable || !hasPrParameter ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1587
|
+
`Codex 暂不可选:${executorReason('codex')}`) : null,
|
|
1588
|
+
!claudeCodeAvailable || !hasPrParameter ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1589
|
+
`Claude Code 暂不可选:${executorReason('claude-code')}`) : null,
|
|
1590
|
+
multiAgentMode && reviewers.length < 2 && formError?.field !== 'ant-reviewer-list'
|
|
1591
|
+
? h('p', { role: 'alert', style: feedbackNoticeStyle },
|
|
1592
|
+
'交叉审查至少选择两位可用 Agent。') : null,
|
|
1593
|
+
formError?.field === 'ant-reviewer-list' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1469
1594
|
reviewers.length > 1 && defaults?.cross_review_policy_version === 1
|
|
1470
1595
|
? h('fieldset', { style: { margin: 0, padding: '8px 10px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8 } },
|
|
1471
1596
|
h('legend', { style: mutedTextStyle }, '运行策略'),
|
|
@@ -1479,45 +1604,54 @@ window.__ModuleLoader__.load({
|
|
|
1479
1604
|
h('span', null, '每次任务都并行运行全部已选 Agent;请求人无需额外提出交叉审查。')))
|
|
1480
1605
|
: reviewers.length > 1 ? h('p', { style: mutedTextStyle }, '每次并行模式需升级 ANT 服务端;当前仍可由请求人逐次提出交叉审查。') : null,
|
|
1481
1606
|
!hasPrParameter ? h('p', { style: mutedTextStyle }, '从描述识别出 PR 任务后,才能使用 Codex 或 Claude Code。') : null),
|
|
1482
|
-
|
|
1483
|
-
`${nativePrUnavailableReason},已禁用 Codex/Claude Code 完整快照选项;可选择 DSH 差异审查。`) : null,
|
|
1607
|
+
h('strong', { style: { gridColumn: '1 / -1', paddingTop: 8, borderTop: '1px solid var(--dsw-alias-border-l2)' } }, '请求范围'),
|
|
1484
1608
|
h('label', null, '使用场景', h('select', { value: conversationKind,
|
|
1485
1609
|
onChange: (event) => { setConversationKind(event.target.value); setGroupId('') }, style: inputStyle },
|
|
1486
1610
|
h('option', { value: 'dm' }, '私聊'), h('option', { value: 'group' }, '指定群聊'))),
|
|
1487
|
-
conversationKind === 'group' ? h('div', { style: {
|
|
1488
|
-
gap: 8, alignItems: 'end' } },
|
|
1489
|
-
h('label', null, '
|
|
1490
|
-
onChange: (event) => setGroupId(event.target.value), style: inputStyle },
|
|
1611
|
+
conversationKind === 'group' ? h('div', { style: { gridColumn: '1 / -1', display: 'grid',
|
|
1612
|
+
gridTemplateColumns: 'minmax(0, 1fr) auto', gap: 8, alignItems: 'end' } },
|
|
1613
|
+
h('label', null, '目标群', h('select', { id: 'ant-group-selector', required: true, value: groupId,
|
|
1614
|
+
onChange: (event) => { setGroupId(event.target.value); clearFormError('ant-group-selector') }, style: inputStyle },
|
|
1491
1615
|
h('option', { value: '' }, '让群成员先 @ 机器人,再刷新列表'),
|
|
1492
1616
|
...availableGroups.map((group) => h('option', { key: group.group_id, value: group.group_id, title: group.group_code },
|
|
1493
1617
|
groupSummary(group))))),
|
|
1494
1618
|
h('button', { type: 'button', disabled: busy,
|
|
1495
|
-
onClick: () => void load().catch((error) =>
|
|
1619
|
+
onClick: () => void load().catch((error) => reportFormError('ant-group-selector', error.message)), style: secondaryButtonStyle }, '刷新群列表')) : null,
|
|
1620
|
+
conversationKind === 'group' && !groupId ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1621
|
+
'请选择目标群;列表为空时,让群成员先 @ 机器人,再刷新群列表。') : null,
|
|
1622
|
+
formError?.field === 'ant-group-selector' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1496
1623
|
h('label', null, '谁可以请求', h('select', { value: senderScope,
|
|
1497
|
-
onChange: (event) => setSenderScope(event.target.value), style: inputStyle },
|
|
1624
|
+
onChange: (event) => { setSenderScope(event.target.value); clearFormError('ant-sender-ids') }, style: inputStyle },
|
|
1498
1625
|
h('option', { value: 'any' }, conversationKind === 'group' ? '此群任何成员' : '任何能私聊此机器人者'),
|
|
1499
1626
|
h('option', { value: 'allowlist' }, '指定 IM 用户 ID'))),
|
|
1500
|
-
senderScope === 'allowlist' ? h('label', null, '可请求成员 ID', h('input', { 'aria-label': '可请求的 IM 用户 ID',
|
|
1501
|
-
required: true, value: senderIds, onChange: (event) => setSenderIds(event.target.value),
|
|
1627
|
+
senderScope === 'allowlist' ? h('label', null, '可请求成员 ID', h('input', { id: 'ant-sender-ids', 'aria-label': '可请求的 IM 用户 ID',
|
|
1628
|
+
required: true, value: senderIds, onChange: (event) => { setSenderIds(event.target.value); clearFormError('ant-sender-ids') },
|
|
1502
1629
|
placeholder: '多个 ID 用逗号分隔', style: inputStyle })) : null,
|
|
1630
|
+
senderScope === 'allowlist' && !senderIds.trim() ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1631
|
+
'填写至少一个可请求成员的 IM 用户 ID。') : null,
|
|
1632
|
+
formError?.field === 'ant-sender-ids' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1503
1633
|
editingCardId ? h('p', { style: { ...mutedTextStyle, alignSelf: 'end' } },
|
|
1504
1634
|
`有效期:${new Date(editingExpiresAt * 1000).toLocaleString('zh-CN')}(编辑时保持不变)`)
|
|
1505
1635
|
: h('label', null, '有效期', h('select', { value: days, onChange: (event) => setDays(Number(event.target.value)), style: inputStyle },
|
|
1506
|
-
h('option', { value: 1 }, '1 天'), h('option', { value: 7 }, '7 天'),
|
|
1636
|
+
h('option', { value: 1 }, '1 天'), h('option', { value: 7 }, '7 天'),
|
|
1637
|
+
h('option', { value: 30 }, '30 天'), h('option', { value: 90 }, '90 天'))),
|
|
1507
1638
|
nativeReviewerSelected ? h('p', { style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1508
1639
|
'本机 PR 审查需要 Gitea 只读权限 read:repository 和 read:user;ANT 不会把令牌交给执行方。') : null),
|
|
1509
1640
|
),
|
|
1510
|
-
h('aside', { id: 'ant-
|
|
1641
|
+
h('aside', { id: 'ant-open-confirm', role: 'region', 'aria-label': '开放前复核', tabIndex: -1,
|
|
1511
1642
|
style: { ...settingsPanelStyle, padding: 14, gap: 10, alignSelf: 'stretch' } },
|
|
1512
1643
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
1513
|
-
h('h4', { style: sectionTitleStyle }, '
|
|
1644
|
+
h('h4', { style: sectionTitleStyle }, '开放前确认'),
|
|
1514
1645
|
h('span', { style: { ...mutedTextStyle, padding: '3px 8px', borderRadius: 999,
|
|
1515
1646
|
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, '草稿 · 未开放')),
|
|
1647
|
+
h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } },
|
|
1648
|
+
(title || '未命名任务') + ' · 触发短语“' + (ruleTriggerSummary.join('”或“') || '尚未填写') + '”'),
|
|
1516
1649
|
h('p', { style: mutedTextStyle }, `这项任务将在${conversationKind === 'group'
|
|
1517
|
-
? `群“${selectedGroupSummary || '尚未选择'}”` : '私聊'}开放给${requesterSummary}
|
|
1650
|
+
? `群“${selectedGroupSummary || '尚未选择'}”` : '私聊'}开放给${requesterSummary};${reviewModeSummary};`
|
|
1518
1651
|
+ `有效期 ${editingCardId ? new Date(editingExpiresAt * 1000).toLocaleString('zh-CN') : `${days} 天`},${resultRouteSummary}。`),
|
|
1519
1652
|
cardBlockReason ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1520
1653
|
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } }, cardBlockReason) : null,
|
|
1654
|
+
formError?.field === 'ant-open-confirm' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1521
1655
|
h('button', { type: 'submit', form: 'ant-capability-create-form', disabled: busy || Boolean(cardBlockReason),
|
|
1522
1656
|
style: { ...primaryButtonStyle, width: '100%' } }, editingCardId ? '保存能力修改' : '确认并开放能力'))))),
|
|
1523
1657
|
h('div', { role: 'region', 'aria-label': '本机处理设置', style: { display: activeTab === 'local' ? 'grid' : 'none', gap: 12 } },
|
package/index.js
CHANGED
|
@@ -530,14 +530,18 @@ async function fetchDelegationExecutors(request, ctx) {
|
|
|
530
530
|
const nativePrSnapshotSupported = process.platform !== 'win32'
|
|
531
531
|
const nativePrUnavailableReason = nativePrSnapshotSupported ? null
|
|
532
532
|
: '当前 Windows 环境无法限制 Git 仓库缓存容量'
|
|
533
|
+
const codexReadonlyReason = nativePrUnavailableReason
|
|
534
|
+
|| (codexCliAvailable ? null : '这台 DSH 没有可用的固定只读 Codex CLI,请检查安装和 PATH')
|
|
535
|
+
const claudeCodeReason = nativePrUnavailableReason
|
|
536
|
+
|| (available.has('claude-code') ? null : '这台 DSH 尚未注册 Claude Code 子代理,请先在 DSH 中安装并启用它')
|
|
533
537
|
return jsonResponse(200, [
|
|
534
538
|
{ id: 'dsh', title: 'DSH', available: Boolean(optionalHostService(ctx, 'sessionController')
|
|
535
539
|
&& optionalHostService(ctx, 'agents')) },
|
|
536
540
|
{ id: 'codex', title: 'Codex', available: available.has('codex') },
|
|
537
541
|
{ id: 'codex-readonly', title: 'Codex 专用只读差异审查', available: codexCliAvailable && nativePrSnapshotSupported,
|
|
538
|
-
...(
|
|
542
|
+
...(codexReadonlyReason ? { reason: codexReadonlyReason } : {}) },
|
|
539
543
|
{ id: 'claude-code', title: 'Claude Code', available: available.has('claude-code') && nativePrSnapshotSupported,
|
|
540
|
-
...(
|
|
544
|
+
...(claudeCodeReason ? { reason: claudeCodeReason } : {}) },
|
|
541
545
|
{ id: 'acp', title: 'DSH ACP', available: available.has('acp') },
|
|
542
546
|
{ id: 'dsh-sdk', title: 'DSH SDK 子代理', available: available.has('dsh-sdk') },
|
|
543
547
|
])
|