@ai-wendao/ant 0.5.9 → 0.5.10
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 +9 -5
- package/client.js +321 -421
- package/conversation.js +203 -263
- package/cordis.patch.yml +6 -6
- package/delegation.js +55 -101
- package/index.js +4 -23
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -517,25 +517,26 @@ window.__ModuleLoader__.load({
|
|
|
517
517
|
const [localConcurrency, setLocalConcurrency] = useState(null)
|
|
518
518
|
const [repositoryCacheLimitGib, setRepositoryCacheLimitGib] = useState(10)
|
|
519
519
|
const [botId, setBotId] = useState('')
|
|
520
|
-
const [trustedOrigin, setTrustedOrigin] = useState('')
|
|
521
520
|
const [conversationKind, setConversationKind] = useState('dm')
|
|
522
521
|
const [groupId, setGroupId] = useState('')
|
|
523
522
|
const [ruleDescription, setRuleDescription] = useState('')
|
|
524
|
-
const [triggerPhrases, setTriggerPhrases] = useState('')
|
|
525
523
|
const recognizedBaselineRef = useRef(null)
|
|
526
524
|
const recognizedDescriptionRef = useRef(null)
|
|
525
|
+
const autoAnalyzeDescriptionRef = useRef(null)
|
|
526
|
+
const recognitionRequestRef = useRef(0)
|
|
527
|
+
const recognitionTimerRef = useRef(null)
|
|
527
528
|
const [recognitionBusy, setRecognitionBusy] = useState(false)
|
|
528
529
|
const [recognitionMessage, setRecognitionMessage] = useState('')
|
|
530
|
+
const [missingRequired, setMissingRequired] = useState([])
|
|
531
|
+
const [suggestions, setSuggestions] = useState([])
|
|
532
|
+
const [ruleExamples, setRuleExamples] = useState([])
|
|
529
533
|
const [title, setTitle] = useState('')
|
|
530
534
|
const [intent, setIntent] = useState('')
|
|
531
535
|
const [taskPrompt, setTaskPrompt] = useState('')
|
|
532
536
|
const [runner, setRunner] = useState('dsh')
|
|
533
537
|
const [reviewers, setReviewers] = useState(['dsh'])
|
|
534
|
-
const [crossReviewAlways, setCrossReviewAlways] = useState(false)
|
|
535
538
|
const [parameters, setParameters] = useState([])
|
|
536
|
-
const [
|
|
537
|
-
const [formError, setFormError] = useState(null)
|
|
538
|
-
const triggerPhrasesEditedRef = useRef(false)
|
|
539
|
+
const [parallelAgentsOpen, setParallelAgentsOpen] = useState(false)
|
|
539
540
|
const [giteaToken, setGiteaToken] = useState('')
|
|
540
541
|
const [tokenConfigured, setTokenConfigured] = useState(false)
|
|
541
542
|
const [senderScope, setSenderScope] = useState('any')
|
|
@@ -591,6 +592,9 @@ window.__ModuleLoader__.load({
|
|
|
591
592
|
const selectedBot = bots.find((bot) => bot.bot_id === botId)
|
|
592
593
|
const botConnected = Boolean(selectedBot?.available && selectedBot?.connected)
|
|
593
594
|
const selectedEnrollment = enrolled.find((bot) => bot.bot_fingerprint === selectedBot?.fingerprint)
|
|
595
|
+
const selectedGiteaOrigin = normalizeServiceUrl(selectedEnrollment?.gitea_origin)
|
|
596
|
+
const taskPrOrigins = [...new Set(parameters.filter((item) => item.kind === 'gitea_pr')
|
|
597
|
+
.map((item) => normalizeServiceUrl(item.gitea_origin)).filter(Boolean))]
|
|
594
598
|
const selectedEnrollmentBound = selectedEnrollment?.device_bound_to_current === true
|
|
595
599
|
const dmChatSettingAvailable = typeof selectedEnrollment?.dm_chat_enabled === 'boolean'
|
|
596
600
|
const dmChatEnabled = selectedEnrollment?.dm_chat_enabled !== false
|
|
@@ -606,9 +610,12 @@ window.__ModuleLoader__.load({
|
|
|
606
610
|
? '允许此机器人接收私聊;任务仍按已开放的规则执行。'
|
|
607
611
|
: '新私聊消息不会交给 DSH;已提交任务仍会在原私聊回复。已开放能力不撤销,重新开启后未过期能力恢复触发。'
|
|
608
612
|
const availableGroups = groups.filter((group) => group.bot_fingerprint === selectedBot?.fingerprint)
|
|
613
|
+
const groupLabel = (group) => {
|
|
614
|
+
if (!group) return ''
|
|
615
|
+
const name = typeof group?.group_name === 'string' ? group.group_name.trim() : ''
|
|
616
|
+
return name ? `${name}(${group.group_code.slice(-2)})` : group.group_code
|
|
617
|
+
}
|
|
609
618
|
const groupChatLoadFailed = loadFailures.includes('groups')
|
|
610
|
-
const groupSummary = (group) => group?.group_name
|
|
611
|
-
? `${group.group_name} · ${group.group_code.slice(-2)}` : group?.group_code || ''
|
|
612
619
|
const activeCards = cards.filter((card) => !card.revoked_at && card.expires_at * 1000 > Date.now())
|
|
613
620
|
const connectedBots = bots.filter((bot) => bot.available && bot.connected)
|
|
614
621
|
const authorizationLoadError = loadFailures.includes('cards')
|
|
@@ -616,8 +623,6 @@ window.__ModuleLoader__.load({
|
|
|
616
623
|
const enrolledCount = enrolled.filter((item) => item.device_bound_to_current
|
|
617
624
|
&& connectedBots.some((bot) => bot.fingerprint === item.bot_fingerprint)).length
|
|
618
625
|
const selectedTaskCount = activeCards.filter((card) => card.bot_fingerprint === selectedBot?.fingerprint).length
|
|
619
|
-
const legacyTriggerCardCount = activeCards.filter((card) => card.bot_fingerprint === selectedBot?.fingerprint
|
|
620
|
-
&& (!Array.isArray(card.trigger_phrases) || !card.trigger_phrases.length)).length
|
|
621
626
|
const collaborationStatus = loading ? '正在读取机器人与任务状态…'
|
|
622
627
|
: botLoadError ? '暂时无法读取机器人状态。请刷新后再试。'
|
|
623
628
|
: connectedBots.length === 0 ? '尚未发现已连接的机器人。请先在 DSH-IM 中连接飞书机器人。'
|
|
@@ -638,47 +643,42 @@ window.__ModuleLoader__.load({
|
|
|
638
643
|
const hasPrParameter = parameters.some((item) => item.kind === 'gitea_pr')
|
|
639
644
|
const hasSelectedPrRule = activeCards.some((card) => card.bot_fingerprint === selectedBot?.fingerprint
|
|
640
645
|
&& Array.isArray(card.parameters) && card.parameters.some((rule) => rule.kind === 'gitea_pr'))
|
|
641
|
-
const showPrSecuritySettings = Boolean(selectedBot
|
|
642
|
-
|
|
643
|
-
const
|
|
646
|
+
const showPrSecuritySettings = Boolean(selectedBot
|
|
647
|
+
&& (hasPrParameter || hasSelectedPrRule))
|
|
648
|
+
const selectedGroupSummary = groupLabel(availableGroups.find((group) => group.group_id === groupId)) || groupId
|
|
649
|
+
const codexAvailable = executors.some((item) => item.id === 'codex' && item.available)
|
|
644
650
|
const claudeCodeAvailable = executors.some((item) => item.id === 'claude-code' && item.available)
|
|
645
651
|
const dshAvailable = executors.some((item) => item.id === 'dsh' && item.available)
|
|
652
|
+
const nativePrUnavailableReason = executors.find((item) => item.reason)?.reason || null
|
|
646
653
|
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
|
-
}
|
|
658
654
|
const cardReviewerSummary = (card) => {
|
|
659
655
|
const selected = Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner]
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
:
|
|
665
|
-
: card.runner === 'claude-code' ? 'Claude Code PR 快照审查'
|
|
666
|
-
: 'DSH 文字处理'
|
|
656
|
+
const unavailable = selected.filter((runner) => runner !== 'dsh')
|
|
657
|
+
if (card.runner !== 'dsh') return `${reviewerLabel(card.runner)} 默认执行方暂不可用`
|
|
658
|
+
return unavailable.length
|
|
659
|
+
? `默认 DSH;${unavailable.map(reviewerLabel).join('、')} 暂不可用`
|
|
660
|
+
: 'DSH 任务 Agent'
|
|
667
661
|
}
|
|
668
662
|
const requesterSummary = senderScope === 'allowlist'
|
|
669
663
|
? `仅指定成员:${senderIds.trim() || '尚未填写成员 ID'}`
|
|
670
664
|
: conversationKind === 'group' ? '群内所有成员' : '任何能私聊此机器人的人'
|
|
671
665
|
const resultRouteSummary = conversationKind === 'group' ? '回复到原群(群内可见)' : '回复到发起任务的私聊'
|
|
672
|
-
const
|
|
673
|
-
const
|
|
674
|
-
const ruleSummaryReady = Boolean(
|
|
675
|
-
const
|
|
676
|
-
|
|
666
|
+
const ruleSummaryCurrent = recognizedDescriptionRef.current === ruleDescription.trim()
|
|
667
|
+
const ruleSummaryPending = Boolean(ruleDescription.trim() && !ruleSummaryCurrent)
|
|
668
|
+
const ruleSummaryReady = Boolean(ruleSummaryCurrent && (title || intent || parameters.length || taskPrompt))
|
|
669
|
+
const missingHarnessInputs = [...new Set([
|
|
670
|
+
...missingRequired,
|
|
671
|
+
...(parameters.some((rule) => rule.kind === 'gitea_pr'
|
|
672
|
+
&& (typeof rule.gitea_origin !== 'string' || !normalizeServiceUrl(rule.gitea_origin) || !rule.repository))
|
|
673
|
+
? ['PR 来源和仓库'] : []),
|
|
674
|
+
])]
|
|
677
675
|
const parameterSummary = (rule) => {
|
|
678
676
|
const parts = [rule.required === false ? '可选' : '必填']
|
|
679
677
|
const choices = Array.isArray(rule.choices) ? rule.choices
|
|
680
678
|
: typeof rule.choices === 'string' ? rule.choices.split(/[,,\n]/u).map((item) => item.trim()).filter(Boolean) : []
|
|
681
679
|
if (rule.kind === 'gitea_pr' && rule.repository) parts.push(`仓库 ${rule.repository}`)
|
|
680
|
+
if (rule.kind === 'gitea_pr' && rule.gitea_origin) parts.push(`来源 ${rule.gitea_origin}`)
|
|
681
|
+
if (rule.kind === 'git_sha') parts.push('可指定 Head SHA;未指定时用最新 Head')
|
|
682
682
|
else if (rule.kind === 'choice' && choices.length) parts.push(`选项 ${choices.join('、')}`)
|
|
683
683
|
else if (rule.kind === 'integer') {
|
|
684
684
|
const minValue = rule.min == null || rule.min === '' ? null : Number(rule.min)
|
|
@@ -698,103 +698,56 @@ window.__ModuleLoader__.load({
|
|
|
698
698
|
? `请求时还需提供:${parameters.map(parameterSummary).join(';')}`
|
|
699
699
|
: '无需额外参数,按请求中的内容处理'
|
|
700
700
|
const ruleSummaryNarrative = ruleSummaryReady
|
|
701
|
-
? intent.trim() || title.trim()
|
|
701
|
+
? `${intent.trim() || title.trim()}${taskPrompt.trim() ? `;${taskPrompt.trim()}` : ''};${recognizedInputsSummary}`
|
|
702
702
|
: '识别后会用一段摘要呈现系统理解的任务。'
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
? `每次任务请求都并行运行 ${reviewers.map(reviewerLabel).join('、')}(会增加调用量)`
|
|
707
|
-
: `普通请求由 ${reviewerLabel(runner)} 执行;请求人明确提出“交叉 review”时并行运行 ${reviewers.map(reviewerLabel).join('、')}`
|
|
708
|
-
: `${reviewerLabel(runner)} 单 Agent 执行`
|
|
703
|
+
const executorSummary = runner === 'codex' ? 'DSH Codex 子 Agent'
|
|
704
|
+
: runner === 'claude-code' ? 'DSH Claude Code 子 Agent' : 'DSH · 当前授权模型'
|
|
705
|
+
const parallelAgentsSummary = reviewers.filter((item) => item !== runner).map(reviewerLabel).join('、')
|
|
709
706
|
const cardBlockReason = enrollmentLoadError ? '暂时无法核对机器人是否已启用协作,请刷新状态后再试。'
|
|
710
707
|
: !botConnected || !selectedEnrollmentBound ? '先在上方把机器人绑定到这台 DSH。'
|
|
711
|
-
: loadFailures.includes('settings') || defaults?.task_rule_schema_version !==
|
|
712
|
-
? '
|
|
708
|
+
: loadFailures.includes('settings') || defaults?.task_rule_schema_version !== 3
|
|
709
|
+
? '团队服务端尚未支持 AI 任务 Harness 与运行时授权核验;请先升级 ANT 服务端。'
|
|
713
710
|
: !ruleDescription.trim() ? '请先用自然语言描述要做的任务。'
|
|
714
|
-
:
|
|
715
|
-
? '
|
|
716
|
-
: !title.trim() || !intent.trim() || !taskPrompt.trim() ? '
|
|
717
|
-
:
|
|
718
|
-
: multiAgentMode && reviewers.length < 2 ? '交叉审查至少选择两位可用 Agent。'
|
|
719
|
-
: crossReviewAlways && defaults?.cross_review_policy_version !== 1
|
|
720
|
-
? '当前团队服务端尚不支持“每次都交叉审查”,请先升级 ANT 服务端。'
|
|
711
|
+
: recognizedDescriptionRef.current !== ruleDescription.trim()
|
|
712
|
+
? 'AI 正在根据最新描述检查任务 Harness;分析完成后再开放。'
|
|
713
|
+
: !title.trim() || !intent.trim() || !taskPrompt.trim() ? '等待 AI 生成任务理解和处理要求。'
|
|
714
|
+
: missingHarnessInputs.length ? `任务范围还有必需信息未明确:${missingHarnessInputs.join('、')}。请补充描述。`
|
|
721
715
|
: conversationKind === 'group' && loadFailures.includes('groups') ? '暂时无法核对目标群,请刷新状态后再试。'
|
|
722
716
|
: conversationKind === 'group' && !groupId ? '请在“请求范围”中选择目标群;若列表为空,让群成员先 @机器人,再刷新群列表。'
|
|
723
717
|
: senderScope === 'allowlist' && !senderIds.trim() ? '请填写可请求成员的 IM ID。'
|
|
718
|
+
: reviewers.some((item) => item !== 'dsh')
|
|
719
|
+
? '当前只允许 DSH 执行协作任务。请将默认执行方改为 DSH,并移除未获准的外部 Agent。'
|
|
724
720
|
: parameters.some((rule) => rule.kind === 'gitea_pr') && !tokenConfigured ? '请先在“本机与任务”中保存本机 Gitea 只读令牌。'
|
|
725
|
-
: parameters.some((rule) => rule.kind === 'gitea_pr'
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
721
|
+
: parameters.some((rule) => rule.kind === 'gitea_pr'
|
|
722
|
+
&& (typeof rule.gitea_origin !== 'string' || !normalizeServiceUrl(rule.gitea_origin) || !rule.repository))
|
|
723
|
+
? 'PR 来源或仓库尚未从自然语言描述中明确识别,请补充完整的 PR 来源链接或仓库。'
|
|
724
|
+
: parameters.some((rule) => rule.kind === 'gitea_pr'
|
|
725
|
+
&& selectedGiteaOrigin && normalizeServiceUrl(rule.gitea_origin) !== selectedGiteaOrigin)
|
|
726
|
+
? `任务描述中的 Gitea 来源必须与此机器人的连接器主机一致(${selectedGiteaOrigin});为保护令牌,ANT 不会向其他主机发送凭据。`
|
|
727
|
+
: taskPrOrigins.length > 1
|
|
728
|
+
? '同一任务中的 PR 资源必须来自同一个 Gitea 主机。'
|
|
729
|
+
: nativeReviewerSelected && (parameters.filter((rule) => rule.kind === 'gitea_pr').length !== 1
|
|
730
|
+
|| parameters.some((rule) => !['gitea_pr', 'git_sha'].includes(rule.kind)))
|
|
731
|
+
? 'Codex 和 Claude Code 当前只支持一个 Gitea PR 资源连接器;任务具体做审查、总结或解释仍由自然语言描述决定。'
|
|
732
|
+
: nativeReviewerSelected && !parameters.find((rule) => rule.kind === 'gitea_pr')?.required
|
|
733
|
+
? '使用本机 Provider 时,Gitea PR 资源必须是必填项。'
|
|
734
|
+
: reviewers.includes('dsh') && !dshAvailable
|
|
735
|
+
? '这台 DSH 尚未准备好任务执行器。'
|
|
740
736
|
: ''
|
|
741
737
|
const loadNames = { bots: '机器人', 'enrolled-bots': '协作启用状态', groups: '群聊列表',
|
|
742
738
|
cards: '任务授权', settings: '团队默认值', executors: '本机执行器',
|
|
743
739
|
'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
|
-
}
|
|
770
740
|
const changeReviewer = (id, enabled) => {
|
|
771
741
|
const next = enabled ? [...reviewers, id] : reviewers.filter((item) => item !== id)
|
|
772
|
-
|
|
773
|
-
setReviewers(
|
|
774
|
-
if (
|
|
775
|
-
if (multiAgentMode && unique.length < 2) {
|
|
776
|
-
reportFormError('ant-reviewer-list', '交叉审查至少选择两位可用 Agent。')
|
|
777
|
-
setCrossReviewAlways(false)
|
|
778
|
-
} else clearFormError('ant-reviewer-list')
|
|
742
|
+
if (!next.length) { setMessage('至少保留一位任务执行 Agent。'); return }
|
|
743
|
+
setReviewers([...new Set(next)])
|
|
744
|
+
if (!next.includes(runner)) setRunner(next[0])
|
|
779
745
|
}
|
|
780
746
|
const selectRunner = (id) => {
|
|
781
747
|
setRunner(id)
|
|
782
|
-
setReviewers((current) =>
|
|
783
|
-
?
|
|
748
|
+
setReviewers((current) => current.length > 1
|
|
749
|
+
? [...new Set([...current, id])]
|
|
784
750
|
: [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')
|
|
798
751
|
}
|
|
799
752
|
const startNewCard = (context = {}) => {
|
|
800
753
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
@@ -802,53 +755,57 @@ window.__ModuleLoader__.load({
|
|
|
802
755
|
setRecognitionMessage('')
|
|
803
756
|
recognizedBaselineRef.current = null
|
|
804
757
|
recognizedDescriptionRef.current = null
|
|
805
|
-
|
|
806
|
-
|
|
758
|
+
autoAnalyzeDescriptionRef.current = null
|
|
759
|
+
recognitionRequestRef.current += 1
|
|
807
760
|
setRuleDescription('')
|
|
808
|
-
|
|
761
|
+
setMissingRequired([]); setSuggestions([]); setRuleExamples([])
|
|
809
762
|
setTitle(''); setIntent(''); setTaskPrompt(''); setRunner('dsh')
|
|
810
|
-
setReviewers(['dsh']);
|
|
763
|
+
setReviewers(['dsh']); setParallelAgentsOpen(false)
|
|
811
764
|
setParameters([]); setConversationKind(context.conversationKind || 'dm')
|
|
812
765
|
setGroupId(context.groupId || '')
|
|
813
766
|
setSenderScope('any'); setSenderIds(''); setDays(7); setCreateOpen(true)
|
|
814
767
|
if (context.botId) setBotId(context.botId)
|
|
815
768
|
}
|
|
816
|
-
const taskRuleDraftFromCard = (card, title) =>
|
|
817
|
-
|
|
818
|
-
trigger_phrases: Array.isArray(card.trigger_phrases) ? card.trigger_phrases.map((item) => item.trim()).filter(Boolean) : [],
|
|
819
|
-
parameters: (card.parameters || []).map((rule) => ({
|
|
769
|
+
const taskRuleDraftFromCard = (card, title) => {
|
|
770
|
+
const rules = (card.parameters || []).map((rule) => ({
|
|
820
771
|
name: rule.name, kind: rule.kind, required: rule.required,
|
|
821
772
|
description: rule.description || '', min: rule.min == null ? '' : String(rule.min),
|
|
822
773
|
max: rule.max == null ? '' : String(rule.max),
|
|
823
774
|
choices: Array.isArray(rule.choices) ? rule.choices.join(', ') : String(rule.choices ?? ''),
|
|
824
775
|
gitea_origin: rule.gitea_origin || '', repository: rule.repository || '',
|
|
825
|
-
}))
|
|
826
|
-
|
|
776
|
+
}))
|
|
777
|
+
if (rules.some((rule) => rule.kind === 'gitea_pr')) {
|
|
778
|
+
const headIndex = rules.findIndex((rule) => rule.name === 'head_sha')
|
|
779
|
+
const headRule = { name: 'head_sha', kind: 'git_sha', required: false,
|
|
780
|
+
description: 'PR Head SHA(可选;未指定时使用任务开始处理时的最新 Head)',
|
|
781
|
+
min: '', max: '', choices: '', gitea_origin: '', repository: '' }
|
|
782
|
+
if (headIndex >= 0 && rules[headIndex].kind === 'git_sha') rules[headIndex] = headRule
|
|
783
|
+
else if (headIndex < 0 && rules.length < 12) rules.push(headRule)
|
|
784
|
+
}
|
|
785
|
+
return { title: title || '', intent: card.intent || '', task_prompt: card.task_prompt || '',
|
|
786
|
+
parameters: rules, examples: Array.isArray(card.examples) ? card.examples : [],
|
|
787
|
+
missing_required: [], suggestions: [] }
|
|
788
|
+
}
|
|
827
789
|
const editCard = (card, copy = false) => {
|
|
828
790
|
setBotId(card.bot_id)
|
|
829
791
|
setRecognitionMessage('')
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
setTrustedOrigin(enrolled.find((bot) => bot.bot_id === card.bot_id)?.gitea_origin || '')
|
|
792
|
+
autoAnalyzeDescriptionRef.current = null
|
|
793
|
+
recognitionRequestRef.current += 1
|
|
833
794
|
setEditingCardId(copy ? null : card.id); setEditingExpiresAt(copy ? null : card.expires_at)
|
|
834
795
|
const taskDraft = taskRuleDraftFromCard(card, copy ? `${card.title.slice(0, 94)}(副本)` : card.title)
|
|
835
796
|
recognizedBaselineRef.current = taskDraft
|
|
836
|
-
const existingDescription = card.rule_description ||
|
|
797
|
+
const existingDescription = card.rule_description || ''
|
|
837
798
|
recognizedDescriptionRef.current = existingDescription.trim() || null
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
? '这项旧任务没有明确触发方式;新版插件不会按它启动任务。请升级 ANT 服务端后重新描述、识别并开放规则。'
|
|
841
|
-
: '')
|
|
799
|
+
setDraftNotice(existingDescription.trim() ? ''
|
|
800
|
+
: '这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。')
|
|
842
801
|
setRuleDescription(existingDescription)
|
|
843
|
-
|
|
802
|
+
setMissingRequired([]); setSuggestions(taskDraft.suggestions); setRuleExamples(taskDraft.examples)
|
|
844
803
|
setTitle(taskDraft.title)
|
|
845
804
|
if (copy) setDays(7)
|
|
846
805
|
setIntent(taskDraft.intent)
|
|
847
806
|
setTaskPrompt(taskDraft.task_prompt); setRunner(card.runner)
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
setMultiAgentMode(selectedReviewers.length > 1)
|
|
851
|
-
setCrossReviewAlways(card.cross_review_always === true)
|
|
807
|
+
setReviewers(Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner])
|
|
808
|
+
setParallelAgentsOpen(false)
|
|
852
809
|
setParameters(taskDraft.parameters)
|
|
853
810
|
setConversationKind(card.conversation_kind); setGroupId(card.conversation_id || '')
|
|
854
811
|
setSenderScope(card.sender_scope); setSenderIds((card.sender_ids || []).join(', '))
|
|
@@ -857,28 +814,35 @@ window.__ModuleLoader__.load({
|
|
|
857
814
|
const applyRuleDraft = (draft) => {
|
|
858
815
|
setTitle(draft.title)
|
|
859
816
|
setIntent(draft.intent)
|
|
860
|
-
if (!triggerPhrasesEditedRef.current) setTriggerPhrases(draft.trigger_phrases.join('\n'))
|
|
861
817
|
setTaskPrompt(draft.task_prompt)
|
|
862
818
|
setParameters(draft.parameters)
|
|
819
|
+
setMissingRequired(draft.missing_required || [])
|
|
820
|
+
setSuggestions(draft.suggestions || [])
|
|
821
|
+
setRuleExamples(draft.examples || [])
|
|
863
822
|
}
|
|
864
|
-
const identifyRule = async () => {
|
|
865
|
-
|
|
823
|
+
const identifyRule = async (description = ruleDescription) => {
|
|
824
|
+
const requestedDescription = description.trim()
|
|
825
|
+
if (!requestedDescription) {
|
|
866
826
|
setRecognitionMessage('先描述这项任务何时触发、要做什么以及涉及哪些资料。')
|
|
867
827
|
return
|
|
868
828
|
}
|
|
869
|
-
|
|
829
|
+
if (recognitionTimerRef.current) clearTimeout(recognitionTimerRef.current)
|
|
830
|
+
const requestNumber = ++recognitionRequestRef.current
|
|
831
|
+
setRecognitionBusy(true); setMessage(''); setRecognitionMessage('AI 正在分析必需范围、可选条件和执行方式…')
|
|
870
832
|
try {
|
|
871
833
|
const response = await fetch('/api/ant-teams/delegation/recognize', {
|
|
872
834
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
873
|
-
body: JSON.stringify({ description:
|
|
835
|
+
body: JSON.stringify({ description: requestedDescription }),
|
|
874
836
|
})
|
|
875
837
|
const result = await readLocalJsonResponse(response)
|
|
876
838
|
if (!response.ok) throw new Error(result.message || '规则识别失败。')
|
|
877
839
|
const candidate = {
|
|
878
840
|
title: typeof result.title === 'string' ? result.title : '',
|
|
879
841
|
intent: typeof result.intent === 'string' ? result.intent : '',
|
|
880
|
-
trigger_phrases: Array.isArray(result.trigger_phrases) ? result.trigger_phrases : [],
|
|
881
842
|
task_prompt: typeof result.task_prompt === 'string' ? result.task_prompt : '',
|
|
843
|
+
missing_required: Array.isArray(result.missing_required) ? result.missing_required : [],
|
|
844
|
+
suggestions: Array.isArray(result.suggestions) ? result.suggestions : [],
|
|
845
|
+
examples: Array.isArray(result.examples) ? result.examples : [],
|
|
882
846
|
parameters: Array.isArray(result.parameters) ? result.parameters.map((rule) => ({
|
|
883
847
|
name: typeof rule?.name === 'string' ? rule.name : '',
|
|
884
848
|
kind: typeof rule?.kind === 'string' ? rule.kind : 'text',
|
|
@@ -892,21 +856,45 @@ window.__ModuleLoader__.load({
|
|
|
892
856
|
repository: typeof rule?.repository === 'string' ? rule.repository : '',
|
|
893
857
|
})) : [],
|
|
894
858
|
}
|
|
895
|
-
if (
|
|
896
|
-
|
|
859
|
+
if (candidate.parameters.some((rule) => rule.kind === 'gitea_pr')) {
|
|
860
|
+
const headIndex = candidate.parameters.findIndex((rule) => rule.name === 'head_sha')
|
|
861
|
+
if (headIndex >= 0 && candidate.parameters[headIndex].kind !== 'git_sha') {
|
|
862
|
+
throw new Error('PR Head SHA 与另一项请求信息重名,请调整任务描述后重新分析。')
|
|
863
|
+
}
|
|
864
|
+
const headRule = { name: 'head_sha', kind: 'git_sha', required: false,
|
|
865
|
+
description: 'PR Head SHA(可选;未指定时使用任务开始处理时的最新 Head)',
|
|
866
|
+
min: '', max: '', choices: '', gitea_origin: '', repository: '' }
|
|
867
|
+
if (headIndex >= 0) candidate.parameters[headIndex] = headRule
|
|
868
|
+
else {
|
|
869
|
+
if (candidate.parameters.length >= 12) throw new Error('PR 任务包含的信息项过多,请简化描述后重新分析。')
|
|
870
|
+
candidate.parameters.push(headRule)
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (requestNumber !== recognitionRequestRef.current || requestedDescription !== ruleDescription.trim()) return
|
|
874
|
+
if (!candidate.title || !candidate.intent || !candidate.task_prompt) {
|
|
875
|
+
throw new Error('AI 返回的任务理解不完整。请补充描述后重试。')
|
|
897
876
|
}
|
|
898
877
|
const previous = recognizedBaselineRef.current
|
|
899
878
|
applyRuleDraft(candidate)
|
|
900
879
|
recognizedBaselineRef.current = candidate
|
|
901
|
-
recognizedDescriptionRef.current =
|
|
902
|
-
setRecognitionMessage(
|
|
903
|
-
? '
|
|
904
|
-
:
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}
|
|
908
|
-
|
|
880
|
+
recognizedDescriptionRef.current = requestedDescription
|
|
881
|
+
setRecognitionMessage(previous
|
|
882
|
+
? 'AI 已更新任务理解。请复核必需信息、可选建议和运行方式;任务仍未开放。'
|
|
883
|
+
: 'AI 已生成待复核的任务 Harness。检查摘要和范围后再开放。')
|
|
884
|
+
} catch (error) {
|
|
885
|
+
if (requestNumber === recognitionRequestRef.current) setRecognitionMessage(error.message)
|
|
886
|
+
} finally {
|
|
887
|
+
if (requestNumber === recognitionRequestRef.current) setRecognitionBusy(false)
|
|
888
|
+
}
|
|
909
889
|
}
|
|
890
|
+
useEffect(() => {
|
|
891
|
+
if (autoAnalyzeDescriptionRef.current !== ruleDescription || !ruleDescription.trim()) return undefined
|
|
892
|
+
const description = ruleDescription
|
|
893
|
+
recognitionTimerRef.current = setTimeout(() => {
|
|
894
|
+
if (autoAnalyzeDescriptionRef.current === description) void identifyRule(description)
|
|
895
|
+
}, 900)
|
|
896
|
+
return () => clearTimeout(recognitionTimerRef.current)
|
|
897
|
+
}, [ruleDescription])
|
|
910
898
|
const saveGiteaToken = async () => {
|
|
911
899
|
if (!giteaToken.trim()) return
|
|
912
900
|
setBusy(true)
|
|
@@ -920,12 +908,7 @@ window.__ModuleLoader__.load({
|
|
|
920
908
|
}
|
|
921
909
|
const enrollBot = async () => {
|
|
922
910
|
if (!selectedBot) return
|
|
923
|
-
const
|
|
924
|
-
const origin = requestedOrigin?.trim() ? normalizeServiceUrl(requestedOrigin) : ''
|
|
925
|
-
if (requestedOrigin?.trim() && !origin) {
|
|
926
|
-
setMessage('请填写有效的 HTTPS Gitea 地址后再启用协作。')
|
|
927
|
-
return
|
|
928
|
-
}
|
|
911
|
+
const origin = selectedEnrollment?.gitea_origin || ''
|
|
929
912
|
setBusy(true)
|
|
930
913
|
try {
|
|
931
914
|
const response = await fetch('/api/ant-teams/delegation/enrolled-bots', {
|
|
@@ -936,29 +919,10 @@ window.__ModuleLoader__.load({
|
|
|
936
919
|
const result = await readLocalJsonResponse(response)
|
|
937
920
|
if (!response.ok) throw new Error(result.message || '协作机器人启用失败。')
|
|
938
921
|
await load()
|
|
939
|
-
if (origin) setTrustedOrigin(origin)
|
|
940
922
|
setMessage('协作机器人已启用。你可以分别设置私聊和群聊,再开放需要的能力。')
|
|
941
923
|
} catch (error) { setMessage(error.message) }
|
|
942
924
|
finally { setBusy(false) }
|
|
943
925
|
}
|
|
944
|
-
const saveTrustedOrigin = async () => {
|
|
945
|
-
if (!selectedBot || !selectedEnrollmentBound) return
|
|
946
|
-
const origin = normalizeServiceUrl(trustedOrigin)
|
|
947
|
-
if (!origin) { setMessage('请填写有效的 HTTPS Gitea 地址。'); return }
|
|
948
|
-
if (!window.confirm(`将这个机器人的可信 Gitea 地址设为 ${origin}?已开放能力中的 PR 参数需要重新核对。`)) return
|
|
949
|
-
setBusy(true)
|
|
950
|
-
try {
|
|
951
|
-
const response = await fetch('/api/ant-teams/delegation/enrolled-bots', {
|
|
952
|
-
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
953
|
-
body: JSON.stringify({ bot_id: selectedBot.bot_id, bot_fingerprint: selectedBot.fingerprint,
|
|
954
|
-
gitea_origin: origin }),
|
|
955
|
-
})
|
|
956
|
-
const result = await readLocalJsonResponse(response)
|
|
957
|
-
if (!response.ok) throw new Error(result.message || '可信地址保存失败。')
|
|
958
|
-
await load(); setTrustedOrigin(origin); setMessage('可信 Gitea 地址已保存。')
|
|
959
|
-
} catch (error) { setMessage(error.message) }
|
|
960
|
-
finally { setBusy(false) }
|
|
961
|
-
}
|
|
962
926
|
const saveGroupChat = async (enabled, targetGroupId) => {
|
|
963
927
|
if (!selectedBot || !selectedEnrollmentBound || !targetGroupId.trim()) return
|
|
964
928
|
const targetHasTasks = activeCards.some((card) => card.bot_fingerprint === selectedBot.fingerprint
|
|
@@ -1020,7 +984,7 @@ window.__ModuleLoader__.load({
|
|
|
1020
984
|
if (!response.ok) throw new Error(result.message || '本机处理设置保存失败。')
|
|
1021
985
|
setLocalConcurrency(result.max_concurrency)
|
|
1022
986
|
setRepositoryCacheLimitGib(result.repository_cache_limit_gib)
|
|
1023
|
-
setMessage('
|
|
987
|
+
setMessage('本机并发设置已保存;仓库缓存上限保留原值。')
|
|
1024
988
|
} catch (error) { setMessage(error.message) }
|
|
1025
989
|
finally { setBusy(false) }
|
|
1026
990
|
}
|
|
@@ -1044,7 +1008,7 @@ window.__ModuleLoader__.load({
|
|
|
1044
1008
|
return focusAfterTabChange('collaboration', 'ant-new-task-summary')
|
|
1045
1009
|
}
|
|
1046
1010
|
setCreateOpen(true)
|
|
1047
|
-
focusAfterTabChange('collaboration', 'ant-
|
|
1011
|
+
focusAfterTabChange('collaboration', 'ant-authorization-review')
|
|
1048
1012
|
}
|
|
1049
1013
|
const collaborationGridStyle = {
|
|
1050
1014
|
display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 300px), 1fr))', gap: 12,
|
|
@@ -1089,28 +1053,20 @@ window.__ModuleLoader__.load({
|
|
|
1089
1053
|
const createCard = async (event) => {
|
|
1090
1054
|
event.preventDefault()
|
|
1091
1055
|
setDraftNotice('')
|
|
1092
|
-
|
|
1093
|
-
if (
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1096
|
-
if (crossReviewAlways && defaults?.cross_review_policy_version !== 1) {
|
|
1097
|
-
reportFormError('ant-open-confirm', '当前团队服务端尚不支持“每次都交叉审查”,请先升级 ANT 服务端。'); return
|
|
1056
|
+
if (!selectedBot || !botConnected || !selectedEnrollmentBound) { setMessage('请先在这台 DSH 绑定选中的协作机器人。'); return }
|
|
1057
|
+
if (reviewers.some((item) => item !== 'dsh')) {
|
|
1058
|
+
setMessage('当前无法核验外部子 Agent 的逐任务文件、Git 和网络权限;请将任务执行方设为 DSH。'); return
|
|
1098
1059
|
}
|
|
1099
1060
|
if (editingCardId && (!editingExpiresAt || editingExpiresAt <= Math.floor(Date.now() / 1000))) {
|
|
1100
|
-
|
|
1061
|
+
setMessage('这项能力已到期,无法继续编辑;请返回后重新开放一项能力。'); return
|
|
1101
1062
|
}
|
|
1102
1063
|
if (!ruleDescription.trim() || !title.trim() || !intent.trim() || !taskPrompt.trim()) {
|
|
1103
|
-
|
|
1064
|
+
setMessage('请填写任务描述并点击“识别规则”,检查摘要后再开放。'); return
|
|
1104
1065
|
}
|
|
1105
1066
|
const bytes = (value) => new TextEncoder().encode(value).length
|
|
1106
1067
|
if (bytes(ruleDescription.trim()) > 4000 || bytes(title.trim()) > 100
|
|
1107
1068
|
|| bytes(intent.trim()) > 1000 || bytes(taskPrompt.trim()) > 4000) {
|
|
1108
|
-
|
|
1109
|
-
}
|
|
1110
|
-
const triggerLines = triggerPhrases.split('\n').map((item) => item.normalize('NFKC').trim()).filter(Boolean)
|
|
1111
|
-
if (!triggerLines.length || triggerLines.length > 8 || triggerLines.some((item) => bytes(item) > 120)
|
|
1112
|
-
|| new Set(triggerLines.map((item) => item.toLowerCase())).size !== triggerLines.length) {
|
|
1113
|
-
reportFormError('ant-trigger-phrases', '触发短语需填写 1–8 条,每条不超过 120 字节且不能重复。'); return
|
|
1069
|
+
setMessage('任务描述或识别摘要过长,请缩短描述并重新识别。'); return
|
|
1114
1070
|
}
|
|
1115
1071
|
const normalized = parameters.map((rule) => ({ name: rule.name.trim(), kind: rule.kind,
|
|
1116
1072
|
description: rule.description.trim() || null,
|
|
@@ -1118,21 +1074,25 @@ window.__ModuleLoader__.load({
|
|
|
1118
1074
|
max: rule.max == null || rule.max === '' ? null : Number(rule.max),
|
|
1119
1075
|
choices: rule.kind === 'choice' ? (Array.isArray(rule.choices) ? rule.choices
|
|
1120
1076
|
: String(rule.choices ?? '').split(/[,,\n]/u)).map((item) => item.trim()).filter(Boolean) : null,
|
|
1121
|
-
gitea_origin: rule.kind === 'gitea_pr'
|
|
1077
|
+
gitea_origin: rule.kind === 'gitea_pr'
|
|
1078
|
+
? (typeof rule.gitea_origin === 'string' ? normalizeServiceUrl(rule.gitea_origin) : null) : null,
|
|
1122
1079
|
repository: rule.kind === 'gitea_pr' ? rule.repository.trim().toLowerCase() : null }))
|
|
1123
1080
|
if (normalized.some((rule) => !/^[A-Za-z][A-Za-z0-9_]{0,31}$/u.test(rule.name))) {
|
|
1124
|
-
|
|
1081
|
+
setMessage('没能从描述中整理出有效的请求信息。请说明同事需要提供什么,再重新识别。'); return
|
|
1125
1082
|
}
|
|
1126
1083
|
if (new Set(normalized.map((rule) => rule.name)).size !== normalized.length) {
|
|
1127
|
-
|
|
1084
|
+
setMessage('请求信息识别有重复项。请修改任务描述并重新识别。'); return
|
|
1085
|
+
}
|
|
1086
|
+
if (normalized.some((rule) => rule.name === 'head_sha' && rule.kind !== 'git_sha')) {
|
|
1087
|
+
setMessage('head_sha 是 PR 提交版本的保留字段;请在自然语言描述中使用 PR Head SHA。'); return
|
|
1128
1088
|
}
|
|
1129
1089
|
if (normalized.some((rule) => rule.description && bytes(rule.description) > 100)) {
|
|
1130
|
-
|
|
1090
|
+
setMessage('请求信息说明过长。请缩短任务描述并重新识别。'); return
|
|
1131
1091
|
}
|
|
1132
1092
|
if (normalized.some((rule) => rule.kind === 'choice' && (!rule.choices?.length || rule.choices.length > 32
|
|
1133
1093
|
|| rule.choices.some((choice) => bytes(choice) > 100)
|
|
1134
1094
|
|| new Set(rule.choices).size !== rule.choices.length))) {
|
|
1135
|
-
|
|
1095
|
+
setMessage('描述中的可选范围不清楚或重复。请修改描述并重新识别。'); return
|
|
1136
1096
|
}
|
|
1137
1097
|
if (normalized.some((rule) => (rule.min != null && !Number.isInteger(rule.min))
|
|
1138
1098
|
|| (rule.max != null && !Number.isInteger(rule.max))
|
|
@@ -1140,10 +1100,20 @@ window.__ModuleLoader__.load({
|
|
|
1140
1100
|
|| (rule.kind === 'text' && rule.max != null && (rule.max < 1 || rule.max > 4096))
|
|
1141
1101
|
|| (rule.kind === 'gitea_pr' && ((rule.min != null && rule.min < 1)
|
|
1142
1102
|
|| (rule.max != null && rule.max > 1_000_000_000))))) {
|
|
1143
|
-
|
|
1103
|
+
setMessage('描述中的请求范围无效。请明确范围或提供完整 PR 链接,再重新识别。'); return
|
|
1144
1104
|
}
|
|
1145
1105
|
if (normalized.some((rule) => rule.kind === 'gitea_pr' && (!rule.gitea_origin || !rule.repository))) {
|
|
1146
|
-
|
|
1106
|
+
setMessage('没能从描述中识别 PR 来源或仓库。请补充完整 PR 链接或仓库名,再重新识别。'); return
|
|
1107
|
+
}
|
|
1108
|
+
if (new Set(normalized.filter((rule) => rule.kind === 'gitea_pr')
|
|
1109
|
+
.map((rule) => normalizeServiceUrl(rule.gitea_origin)).filter(Boolean)).size > 1) {
|
|
1110
|
+
setMessage('同一任务中的 PR 资源必须来自同一个 Gitea 主机。')
|
|
1111
|
+
return
|
|
1112
|
+
}
|
|
1113
|
+
if (normalized.some((rule) => rule.kind === 'gitea_pr'
|
|
1114
|
+
&& selectedGiteaOrigin && normalizeServiceUrl(rule.gitea_origin) !== selectedGiteaOrigin)) {
|
|
1115
|
+
setMessage(`任务描述中的 Gitea 来源必须与此机器人的连接器主机一致(${selectedGiteaOrigin});令牌不会发送到其他主机。`)
|
|
1116
|
+
return
|
|
1147
1117
|
}
|
|
1148
1118
|
const validRepository = (value) => {
|
|
1149
1119
|
const parts = value.split('/')
|
|
@@ -1151,30 +1121,30 @@ window.__ModuleLoader__.load({
|
|
|
1151
1121
|
&& /^[A-Za-z0-9_.-]{1,100}$/u.test(part))
|
|
1152
1122
|
}
|
|
1153
1123
|
if (normalized.some((rule) => rule.kind === 'gitea_pr' && !validRepository(rule.repository))) {
|
|
1154
|
-
|
|
1124
|
+
setMessage('仓库名无法识别。请在描述中写明“团队名/仓库名”或粘贴完整 PR 链接,再重新识别。'); return
|
|
1155
1125
|
}
|
|
1156
|
-
if (!reviewers.length || !reviewers.includes(runner)
|
|
1157
|
-
|
|
1158
|
-
|| (!multiAgentMode && reviewers.length !== 1)) {
|
|
1159
|
-
reportFormError('ant-agent-mode', multiAgentMode
|
|
1160
|
-
? '交叉审查至少选择两位可用 Agent。' : '单 Agent 模式请选择一位执行 Agent。'); return
|
|
1126
|
+
if (!reviewers.length || !reviewers.includes(runner)) {
|
|
1127
|
+
setMessage('请至少选择一位任务执行 Agent,并设置普通请求的默认执行方。'); return
|
|
1161
1128
|
}
|
|
1162
|
-
if (nativeReviewerSelected && (normalized.length
|
|
1163
|
-
|
|
1164
|
-
|
|
1129
|
+
if (nativeReviewerSelected && !((normalized.length === 1
|
|
1130
|
+
&& normalized[0].kind === 'gitea_pr' && normalized[0].required)
|
|
1131
|
+
|| (normalized.length === 2 && normalized.some((rule) => rule.kind === 'gitea_pr' && rule.required)
|
|
1132
|
+
&& normalized.some((rule) => rule.kind === 'git_sha' && rule.name === 'head_sha' && !rule.required)))) {
|
|
1133
|
+
setMessage('Codex 和 Claude Code 当前只支持一个必填 Gitea PR 资源连接器;任务行为仍由自然语言描述决定。'); return
|
|
1165
1134
|
}
|
|
1166
1135
|
const scope = conversationKind === 'group' ? `群 ${groupId}` : '私聊'
|
|
1167
1136
|
const resultDestination = conversationKind === 'group' ? '原群(群内可见)' : '发起任务的私聊'
|
|
1168
1137
|
const executionDetails = {
|
|
1169
|
-
dsh: 'DSH
|
|
1170
|
-
codex: 'Codex
|
|
1171
|
-
'claude-code': 'Claude Code
|
|
1138
|
+
dsh: 'DSH 使用获准的团队模型处理对话内容和 ANT 已核验后提供的资源。',
|
|
1139
|
+
codex: 'Codex 当前不能用于协作任务,因为 ANT 无法核验其逐任务文件、Git 和网络权限。',
|
|
1140
|
+
'claude-code': 'Claude Code 当前不能用于协作任务,因为 ANT 无法核验其逐任务文件、Git 和网络权限。',
|
|
1172
1141
|
}
|
|
1173
|
-
const executionInfo =
|
|
1174
|
-
|
|
1142
|
+
const executionInfo = reviewers.length > 1
|
|
1143
|
+
? `普通请求默认由 ${reviewerLabel(runner)} 执行;请求人明确要求多 Agent 分别处理时,会并行启动 ${reviewers.map(reviewerLabel).join('、')},分别返回结果。${reviewers.map((item) => executionDetails[item]).join(' ')}`
|
|
1144
|
+
: executionDetails[reviewers[0]]
|
|
1175
1145
|
const actionDescription = editingCardId
|
|
1176
|
-
? `更新“${title}”后,${requesterSummary}可在${scope}
|
|
1177
|
-
: `开放“${title}”后,${requesterSummary}可在${scope}
|
|
1146
|
+
? `更新“${title}”后,${requesterSummary}可在${scope}按任务描述中的规则发起请求。旧待确认请求将失效,已提交任务继续使用提交时的配置。结果${resultDestination}。到期时间保持为 ${new Date(editingExpiresAt * 1000).toLocaleString('zh-CN')}。`
|
|
1147
|
+
: `开放“${title}”后,${requesterSummary}可在${scope}按任务描述中的规则发起请求,有效期 ${days} 天;结果${resultDestination}。`
|
|
1178
1148
|
if (!window.confirm(`${actionDescription}${executionInfo}请确认。`)) return
|
|
1179
1149
|
setBusy(true)
|
|
1180
1150
|
try {
|
|
@@ -1182,10 +1152,9 @@ window.__ModuleLoader__.load({
|
|
|
1182
1152
|
method: editingCardId ? 'PUT' : 'POST', headers: { 'content-type': 'application/json' },
|
|
1183
1153
|
body: JSON.stringify({ ...(editingCardId ? { id: editingCardId } : {}),
|
|
1184
1154
|
bot_id: selectedBot.bot_id, bot_fingerprint: selectedBot.fingerprint,
|
|
1185
|
-
title, rule_description: ruleDescription.trim(), trigger_phrases:
|
|
1186
|
-
intent, examples:
|
|
1187
|
-
parameters: normalized, task_prompt: taskPrompt, runner, reviewers,
|
|
1188
|
-
cross_review_always: crossReviewAlways, workspace: null,
|
|
1155
|
+
title, rule_description: ruleDescription.trim(), trigger_phrases: [],
|
|
1156
|
+
intent, examples: ruleExamples,
|
|
1157
|
+
parameters: normalized, task_prompt: taskPrompt, runner, reviewers, workspace: null,
|
|
1189
1158
|
sender_scope: senderScope, sender_ids: senderScope === 'allowlist'
|
|
1190
1159
|
? senderIds.split(/[\s,,]+/u).filter(Boolean) : [], conversation_kind: conversationKind,
|
|
1191
1160
|
...(conversationKind === 'group' ? { conversation_id: groupId } : {}),
|
|
@@ -1193,7 +1162,7 @@ window.__ModuleLoader__.load({
|
|
|
1193
1162
|
})
|
|
1194
1163
|
const result = await readLocalJsonResponse(response)
|
|
1195
1164
|
if (!response.ok && result.code === 'IM_TASK_RULE_REQUIRED') {
|
|
1196
|
-
throw new Error('当前 ANT
|
|
1165
|
+
throw new Error('当前 ANT 服务端不支持自然语言任务 Harness,请升级 ANT 服务端后再开放任务。')
|
|
1197
1166
|
}
|
|
1198
1167
|
if (!response.ok && reviewers.includes('claude-code') && result.code === 'INVALID_REQUEST') {
|
|
1199
1168
|
throw new Error('当前团队服务端可能尚不支持 Claude Code 审查配置。请管理员升级服务端后刷新重试。')
|
|
@@ -1201,8 +1170,7 @@ window.__ModuleLoader__.load({
|
|
|
1201
1170
|
if (!response.ok) throw new Error(result.message || '能力保存失败。')
|
|
1202
1171
|
const savedReviewers = Array.isArray(result.reviewers) && result.reviewers.length
|
|
1203
1172
|
? result.reviewers : [result.runner]
|
|
1204
|
-
if ([...savedReviewers].sort().join('\0') !== [...reviewers].sort().join('\0')
|
|
1205
|
-
|| (result.cross_review_always === true) !== crossReviewAlways) {
|
|
1173
|
+
if ([...savedReviewers].sort().join('\0') !== [...reviewers].sort().join('\0')) {
|
|
1206
1174
|
if (!editingCardId && typeof result.id === 'string') {
|
|
1207
1175
|
const cleanup = await fetch('/api/ant-teams/delegation/cards', {
|
|
1208
1176
|
method: 'DELETE', headers: { 'content-type': 'application/json' },
|
|
@@ -1210,25 +1178,25 @@ window.__ModuleLoader__.load({
|
|
|
1210
1178
|
}).catch(() => null)
|
|
1211
1179
|
if (!cleanup?.ok) {
|
|
1212
1180
|
await load().catch(() => undefined)
|
|
1213
|
-
throw new Error('
|
|
1181
|
+
throw new Error('团队服务端尚不支持多 Agent 审查配置;旧服务端可能已保存单 Agent 授权,请刷新列表并停止它,再升级 ANT 服务端。')
|
|
1214
1182
|
}
|
|
1215
1183
|
}
|
|
1216
|
-
throw new Error('
|
|
1184
|
+
throw new Error('当前团队服务端尚不支持多 Agent 审查配置;请管理员升级 ANT 服务端后重试。')
|
|
1217
1185
|
}
|
|
1218
1186
|
recognizedBaselineRef.current = null
|
|
1219
1187
|
recognizedDescriptionRef.current = null
|
|
1220
|
-
|
|
1221
|
-
|
|
1188
|
+
autoAnalyzeDescriptionRef.current = null
|
|
1189
|
+
recognitionRequestRef.current += 1
|
|
1190
|
+
setRuleDescription(''); setMissingRequired([]); setSuggestions([]); setRuleExamples([])
|
|
1222
1191
|
setTitle(''); setIntent(''); setTaskPrompt(''); setParameters([]); setRunner('dsh'); setReviewers(['dsh'])
|
|
1223
|
-
|
|
1224
|
-
setMultiAgentMode(false)
|
|
1192
|
+
setParallelAgentsOpen(false)
|
|
1225
1193
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1226
1194
|
await load()
|
|
1227
1195
|
setCreateOpen(false)
|
|
1228
1196
|
setMessage(editingCardId
|
|
1229
1197
|
? '能力配置已更新。新请求将使用新配置,已提交任务保留原配置。'
|
|
1230
1198
|
: '任务已开放。机器人会按已确认的描述判断请求;缺少必要信息时会向请求人追问。')
|
|
1231
|
-
} catch (error) {
|
|
1199
|
+
} catch (error) { setMessage(error.message) }
|
|
1232
1200
|
finally { setBusy(false) }
|
|
1233
1201
|
}
|
|
1234
1202
|
const revokeCard = async (card) => {
|
|
@@ -1245,31 +1213,27 @@ window.__ModuleLoader__.load({
|
|
|
1245
1213
|
}
|
|
1246
1214
|
const copyCardToDraft = (card) => {
|
|
1247
1215
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1248
|
-
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1216
|
+
setParallelAgentsOpen(false)
|
|
1217
|
+
const copiedDescription = card.rule_description || ''
|
|
1218
|
+
recognizedDescriptionRef.current = copiedDescription.trim() || null
|
|
1219
|
+
autoAnalyzeDescriptionRef.current = null
|
|
1220
|
+
recognitionRequestRef.current += 1
|
|
1221
|
+
setDraftNotice(copiedDescription.trim()
|
|
1253
1222
|
? '已复制到新草稿。复制不保留原有效期;请检查任务描述和请求范围。'
|
|
1254
|
-
: '
|
|
1223
|
+
: '这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。')
|
|
1255
1224
|
const cardBot = bots.find((bot) => bot.fingerprint === card.bot_fingerprint)
|
|
1256
|
-
const cardEnrollment = enrolled.find((bot) => bot.bot_fingerprint === card.bot_fingerprint)
|
|
1257
1225
|
const titleChars = Array.from(`复制:${card.title || '未命名能力'}`)
|
|
1258
1226
|
while (new TextEncoder().encode(titleChars.join('')).length > 100) titleChars.pop()
|
|
1259
1227
|
const taskDraft = taskRuleDraftFromCard(card, titleChars.join(''))
|
|
1260
|
-
recognizedBaselineRef.current =
|
|
1228
|
+
recognizedBaselineRef.current = taskDraft
|
|
1261
1229
|
setBotId(card.bot_id || cardBot?.bot_id || '')
|
|
1262
|
-
setTrustedOrigin(cardEnrollment?.gitea_origin || '')
|
|
1263
1230
|
setRuleDescription(copiedDescription)
|
|
1264
|
-
|
|
1231
|
+
setMissingRequired([]); setSuggestions(taskDraft.suggestions); setRuleExamples(taskDraft.examples)
|
|
1265
1232
|
setTitle(taskDraft.title)
|
|
1266
1233
|
setIntent(taskDraft.intent)
|
|
1267
1234
|
setTaskPrompt(taskDraft.task_prompt)
|
|
1268
1235
|
setRunner(['dsh', 'codex', 'claude-code'].includes(card.runner) ? card.runner : 'dsh')
|
|
1269
|
-
|
|
1270
|
-
setReviewers(copiedReviewers)
|
|
1271
|
-
setMultiAgentMode(copiedReviewers.length > 1)
|
|
1272
|
-
setCrossReviewAlways(card.cross_review_always === true)
|
|
1236
|
+
setReviewers(Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner || 'dsh'])
|
|
1273
1237
|
setParameters(taskDraft.parameters)
|
|
1274
1238
|
setConversationKind(card.conversation_kind === 'group' ? 'group' : 'dm')
|
|
1275
1239
|
setGroupId(card.conversation_id || '')
|
|
@@ -1299,8 +1263,7 @@ window.__ModuleLoader__.load({
|
|
|
1299
1263
|
loading ? '读取中…' : '刷新状态')),
|
|
1300
1264
|
h('p', { role: 'status', style: mutedTextStyle }, collaborationStatus),
|
|
1301
1265
|
bots.length ? h('label', null, '我的机器人', h('select', { id: 'ant-bot-selector', value: botId,
|
|
1302
|
-
onChange: (event) => { setBotId(event.target.value); setGroupId('')
|
|
1303
|
-
setTrustedOrigin(enrolled.find((bot) => bot.bot_id === event.target.value)?.gitea_origin || '') }, style: inputStyle },
|
|
1266
|
+
onChange: (event) => { setBotId(event.target.value); setGroupId('') }, style: inputStyle },
|
|
1304
1267
|
h('option', { value: '' }, '请选择机器人'),
|
|
1305
1268
|
...bots.map((bot) => h('option', { key: bot.bot_id, value: bot.bot_id,
|
|
1306
1269
|
disabled: !bot.available || !bot.connected }, `${bot.name}${bot.available && bot.connected ? '' : '(未连接)'}`)))) : null,
|
|
@@ -1312,23 +1275,6 @@ window.__ModuleLoader__.load({
|
|
|
1312
1275
|
selectedBot && !selectedEnrollmentBound ? h('button', { type: 'button', disabled: busy || !botConnected || enrollmentLoadError,
|
|
1313
1276
|
onClick: () => void enrollBot(), style: primaryButtonStyle },
|
|
1314
1277
|
selectedEnrollment ? '绑定原协作配置到这台 DSH' : '启用并绑定到这台 DSH') : null)
|
|
1315
|
-
const trustedOriginCard = showPrSecuritySettings ? h('section', { style: collaborationCardStyle },
|
|
1316
|
-
h('strong', null, 'PR 读取来源(可选)'),
|
|
1317
|
-
h('p', { style: mutedTextStyle }, '只有任务需要读取 Gitea PR 时才需设置。PR 链接、仓库和编号写在任务描述里;这里仅登记本机允许读取的 Gitea 来源。'),
|
|
1318
|
-
h('label', null, '机器人', h('select', { 'aria-label': 'Gitea 来源对应机器人', value: botId,
|
|
1319
|
-
onChange: (event) => { setBotId(event.target.value); setTrustedOrigin(
|
|
1320
|
-
enrolled.find((bot) => bot.bot_id === event.target.value)?.gitea_origin || '') }, style: inputStyle },
|
|
1321
|
-
h('option', { value: '' }, '请选择机器人'),
|
|
1322
|
-
...bots.map((bot) => h('option', { key: bot.bot_id, value: bot.bot_id,
|
|
1323
|
-
disabled: !bot.available || !bot.connected }, `${bot.name}${bot.available && bot.connected ? '' : '(未连接)'}`)))),
|
|
1324
|
-
selectedBot ? h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
|
|
1325
|
-
h('input', { 'aria-label': '可信 Gitea 地址', type: 'url', value: trustedOrigin,
|
|
1326
|
-
onChange: (event) => setTrustedOrigin(event.target.value), placeholder: 'https://git.example.com',
|
|
1327
|
-
style: { ...inputStyle, flex: '1 1 220px' } }),
|
|
1328
|
-
h('button', { type: 'button', disabled: busy || !trustedOrigin.trim() || (!selectedEnrollmentBound && !botConnected),
|
|
1329
|
-
onClick: () => void (selectedEnrollmentBound ? saveTrustedOrigin() : enrollBot()), style: secondaryButtonStyle },
|
|
1330
|
-
selectedEnrollmentBound ? '保存可信来源' : botConnected ? '启用并保存来源' : '先连接机器人'))
|
|
1331
|
-
: null) : null
|
|
1332
1278
|
const groupTaskStatusPanel = selectedEnrollmentBound && botConnected ? h('section', {
|
|
1333
1279
|
id: 'ant-group-task-status', role: 'region', 'aria-label': '各群聊天与任务状态', tabIndex: -1,
|
|
1334
1280
|
style: { ...collaborationCardStyle, gap: 10 } },
|
|
@@ -1339,7 +1285,7 @@ window.__ModuleLoader__.load({
|
|
|
1339
1285
|
loading ? '读取中…' : '刷新群列表')),
|
|
1340
1286
|
h('p', { style: mutedTextStyle }, groupChatLoadFailed
|
|
1341
1287
|
? '群列表暂不可用,请刷新状态后重试。'
|
|
1342
|
-
: availableGroups.length ? '
|
|
1288
|
+
: availableGroups.length ? '每个群分别控制普通聊天和任务;名称取自 DSH-IM 已保存目标,括号内为识别码末两位。未匹配名称时显示完整识别码。'
|
|
1343
1289
|
: '先在目标群 @机器人,再刷新状态发现群。'),
|
|
1344
1290
|
availableGroups.length ? h('div', { id: 'ant-group-card-grid', style: collaborationGridStyle },
|
|
1345
1291
|
...availableGroups.map((group) => {
|
|
@@ -1347,16 +1293,13 @@ window.__ModuleLoader__.load({
|
|
|
1347
1293
|
&& card.conversation_kind === 'group' && card.conversation_id === group.group_id).length
|
|
1348
1294
|
return h('article', { key: group.group_id, style: { ...collaborationCardStyle, padding: 10, gap: 8 } },
|
|
1349
1295
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
1350
|
-
h('
|
|
1351
|
-
h('strong', { style: { overflowWrap: 'anywhere' } }, group.group_name || group.group_code),
|
|
1352
|
-
group.group_name ? h('small', { style: mutedTextStyle, title: `完整识别码 ${group.group_code}` },
|
|
1353
|
-
`识别码尾号 ${group.group_code.slice(-2)}`) : null),
|
|
1296
|
+
h('strong', { title: `完整识别码 ${group.group_code}` }, groupLabel(group)),
|
|
1354
1297
|
h('span', { style: mutedTextStyle }, `已开放任务 ${taskCount} 项`)),
|
|
1355
1298
|
h('label', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
1356
1299
|
h('input', { type: 'checkbox', checked: group.chat_enabled,
|
|
1357
1300
|
disabled: busy || groupChatLoadFailed,
|
|
1358
1301
|
onChange: (event) => void saveGroupChat(event.target.checked, group.group_id),
|
|
1359
|
-
'aria-label':
|
|
1302
|
+
'aria-label': '允许群 ' + groupLabel(group) + ' 普通聊天' }), '普通聊天'),
|
|
1360
1303
|
h('button', { type: 'button', disabled: busy, style: secondaryButtonStyle,
|
|
1361
1304
|
onClick: () => { startNewCard({ conversationKind: 'group', groupId: group.group_id }); focusAfterTabChange('collaboration', 'ant-new-task-summary') } },
|
|
1362
1305
|
'为此群开放任务'))
|
|
@@ -1375,9 +1318,6 @@ window.__ModuleLoader__.load({
|
|
|
1375
1318
|
h('p', { style: mutedTextStyle }, '选择机器人,设置聊天入口,再按需要开放任务规则。聊天和任务授权分别控制。')),
|
|
1376
1319
|
h('span', { style: { ...mutedTextStyle, padding: '4px 9px', borderRadius: 999,
|
|
1377
1320
|
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, selectedBot ? selectedBot.name : '尚未选择机器人')),
|
|
1378
|
-
legacyTriggerCardCount ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1379
|
-
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1380
|
-
'当前有 ' + legacyTriggerCardCount + ' 项旧任务规则没有明确触发方式;新版插件不会按这些旧规则触发任务。升级 ANT 服务端后,再重新描述并开放需要的规则。') : null,
|
|
1381
1321
|
loadFailures.length ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1382
1322
|
`部分协作信息暂时无法读取:${loadFailures.map((path) => loadNames[path]).join('、')}。已读取的信息仍显示,请稍后刷新状态。`) : null,
|
|
1383
1323
|
h('div', { id: 'ant-collaboration-settings-grid', style: collaborationSettingsGridStyle },
|
|
@@ -1405,9 +1345,11 @@ window.__ModuleLoader__.load({
|
|
|
1405
1345
|
const remainingDays = Math.max(1, Math.ceil((expiresAt - Date.now()) / 86_400_000))
|
|
1406
1346
|
const expiry = remainingDays <= 7 ? `${remainingDays} 天后到期` : `至 ${new Date(expiresAt).toLocaleDateString('zh-CN')}`
|
|
1407
1347
|
const conversation = card.conversation_kind === 'group'
|
|
1408
|
-
? `群 ${
|
|
1348
|
+
? `群 ${groupLabel(groups.find((group) => group.bot_fingerprint === card.bot_fingerprint
|
|
1349
|
+
&& group.group_id === card.conversation_id)) || card.conversation_id}` : '私聊'
|
|
1409
1350
|
const requester = card.sender_scope === 'allowlist' ? '指定成员可请求'
|
|
1410
1351
|
: card.conversation_kind === 'group' ? '群成员可请求' : '可私聊者可请求'
|
|
1352
|
+
const blockedExternalExecutor = card.runner !== 'dsh' || card.cross_review_always === true
|
|
1411
1353
|
const examplesText = Array.isArray(card.examples) ? card.examples.join('、') : ''
|
|
1412
1354
|
const requesterIds = card.sender_scope === 'allowlist' && Array.isArray(card.sender_ids) ? card.sender_ids.join('、') : ''
|
|
1413
1355
|
const remainingDaysLabel = remainingDays <= 7 ? 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))'
|
|
@@ -1419,12 +1361,13 @@ window.__ModuleLoader__.load({
|
|
|
1419
1361
|
h('span', { style: { ...mutedTextStyle, padding: '2px 8px', borderRadius: 999,
|
|
1420
1362
|
backgroundColor: 'var(--dsw-alias-bg-layer-2)', color: remainingDaysLabel, whiteSpace: 'nowrap' } }, expiry)),
|
|
1421
1363
|
h('span', { style: mutedTextStyle }, `${cardReviewerSummary(card)} · ${conversation} · ${requester} · 结果回复原${card.conversation_kind === 'group' ? '群' : '私聊'}`),
|
|
1364
|
+
blockedExternalExecutor ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1365
|
+
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1366
|
+
'此任务暂不可执行:当前无法核验外部 Agent 的逐任务权限。点击“编辑”,改用 DSH 并移除外部 Agent 后可重新开放。') : null,
|
|
1422
1367
|
card.rule_description
|
|
1423
1368
|
? h('span', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, card.rule_description)
|
|
1424
1369
|
: h('span', { role: 'status', style: { ...mutedTextStyle, color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1425
|
-
'
|
|
1426
|
-
Array.isArray(card.trigger_phrases) && card.trigger_phrases.length
|
|
1427
|
-
? h('span', { style: { overflowWrap: 'anywhere' } }, '触发短语:' + card.trigger_phrases.join('、')) : null,
|
|
1370
|
+
'旧任务卡没有自然语言描述。请编辑并补充描述,让 AI 生成 Harness 后再开放。'),
|
|
1428
1371
|
h('details', null,
|
|
1429
1372
|
h('summary', { style: { cursor: 'pointer', color: 'var(--dsw-alias-link)', fontSize: 13 } }, '查看规则说明'),
|
|
1430
1373
|
h('div', { style: { display: 'grid', gap: 4, paddingTop: 6 } },
|
|
@@ -1484,183 +1427,138 @@ window.__ModuleLoader__.load({
|
|
|
1484
1427
|
h('p', { style: mutedTextStyle }, selectedBot
|
|
1485
1428
|
? `当前机器人:${selectedBot.name}${selectedEnrollmentBound ? ' · 已绑定此设备' : ' · 请先绑定到此设备'}`
|
|
1486
1429
|
: '请先在上方选择机器人并启用协作。'),
|
|
1487
|
-
formError?.field === 'ant-bot-selector' ? h('p', { role: 'alert', style: feedbackNoticeStyle }, formError.text) : null,
|
|
1488
1430
|
draftNotice ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1489
1431
|
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } }, draftNotice) : null,
|
|
1490
1432
|
selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
1491
1433
|
'机器人及其任务能力只在这台 DSH 设备接收和执行;同一账号的其他设备不会继承本机授权。') : null,
|
|
1492
1434
|
selectedEnrollment && !selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
1493
|
-
'
|
|
1435
|
+
'这是尚未绑定到设备的机器人。请在要继续使用它的电脑重新启用;原协作配置会保留。若旧设备访问曾被撤销,原任务权限不会恢复,需要重新开放。') : null,
|
|
1494
1436
|
selectedBot && conversationKind === 'group' && !selectedBot.history_available ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1495
1437
|
'这个机器人当前不能读取平台群历史;群聊只能参考实际送达的消息和本机近期记录。请检查 DSH-IM 权限。') : null,
|
|
1496
|
-
h('
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
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,
|
|
1438
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 320px), 1fr))',
|
|
1439
|
+
gap: 12, alignItems: 'start' } },
|
|
1440
|
+
h('div', { style: { display: 'grid', gap: 9, minWidth: 0 } },
|
|
1441
|
+
h('label', null, '描述规则', h('textarea', { required: true, maxLength: 4000, rows: 4, value: ruleDescription,
|
|
1442
|
+
onChange: (event) => {
|
|
1443
|
+
autoAnalyzeDescriptionRef.current = event.target.value
|
|
1444
|
+
recognitionRequestRef.current += 1
|
|
1445
|
+
setRuleDescription(event.target.value)
|
|
1446
|
+
setRecognitionBusy(false)
|
|
1447
|
+
setRecognitionMessage(event.target.value.trim() ? '你暂停输入后,AI 会自动检查任务是否完整。' : '')
|
|
1448
|
+
},
|
|
1449
|
+
placeholder: '用日常语言说明要完成什么、可处理哪些资料和希望怎样返回。例如:审查来自 https://gitea.example.com/team/shop 的 PR,指出可定位的问题,不修改代码;请求人可指定 Head SHA,未指定时用最新 Head。', style: inputStyle })),
|
|
1450
|
+
h('p', { style: mutedTextStyle }, 'AI 会在输入停顿后分析必需范围、缺失信息和可选条件。执行方、请求人、群范围和有效期由下方通用授权控制。'),
|
|
1537
1451
|
h('div', { style: { display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' } },
|
|
1538
1452
|
h('button', { type: 'button', disabled: recognitionBusy || busy || !ruleDescription.trim(),
|
|
1539
|
-
onClick: () => void identifyRule(), style: secondaryButtonStyle }, recognitionBusy ? '
|
|
1540
|
-
h('span', { style: mutedTextStyle }, '
|
|
1541
|
-
recognitionMessage ? h('p', { role: 'status', 'aria-live': 'polite', style: feedbackNoticeStyle }, recognitionMessage) : null,
|
|
1453
|
+
onClick: () => void identifyRule(), style: secondaryButtonStyle }, recognitionBusy ? 'AI 正在分析…' : ruleSummaryReady ? '立即重新分析' : '立即分析'),
|
|
1454
|
+
h('span', { style: mutedTextStyle }, '自动分析和手动分析都会调用当前团队模型;只生成建议和待复核草稿,不会开放任务。')),
|
|
1455
|
+
recognitionMessage ? h('p', { role: 'status', 'aria-live': 'polite', style: feedbackNoticeStyle }, recognitionMessage) : null),
|
|
1456
|
+
h('section', { role: 'region', 'aria-label': 'AI 任务助手', style: { ...panelStyle, padding: 10, gap: 7, minWidth: 0 } },
|
|
1457
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
1458
|
+
h('strong', null, ruleSummaryPending ? 'AI 正在检查当前描述' : title || 'AI 任务助手'),
|
|
1459
|
+
h('span', { style: mutedTextStyle }, ruleSummaryPending ? '描述已更新' : ruleSummaryReady ? '待复核 · 尚未开放' : '填写描述后开始分析')),
|
|
1460
|
+
h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, ruleSummaryPending
|
|
1461
|
+
? 'AI 正在根据当前描述更新任务理解。上一次分析结果暂不用于授权开放。' : ruleSummaryNarrative),
|
|
1462
|
+
ruleSummaryReady && missingHarnessInputs.length ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1463
|
+
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1464
|
+
`还需要明确:${missingHarnessInputs.join(';')}`) : ruleSummaryReady
|
|
1465
|
+
? h('p', { role: 'status', style: mutedTextStyle }, 'AI 暂未发现必需信息缺项;请确认任务来源和处理范围符合预期。') : null,
|
|
1466
|
+
ruleSummaryReady && suggestions.length ? h('p', { style: mutedTextStyle }, `可选补充:${suggestions.join(';')}`) : null,
|
|
1467
|
+
ruleSummaryReady && ruleExamples.length ? h('p', { style: mutedTextStyle }, `自然请求示例:${ruleExamples.join(';')}`) : null,
|
|
1468
|
+
h('p', { style: mutedTextStyle }, ruleSummaryReady
|
|
1469
|
+
? '草稿未开放。检查任务理解、必需范围和建议;有误就改描述,AI 会重新分析。'
|
|
1470
|
+
: ruleSummaryPending ? '分析完成前不能开放任务。' : 'AI 会整理任务理解;草稿不会自动开放。'))),
|
|
1542
1471
|
h('section', { style: { ...panelStyle, padding: 10, gap: 9, display: 'grid',
|
|
1543
1472
|
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 280px), 1fr))' } },
|
|
1544
|
-
h('strong', { style: { gridColumn: '1 / -1' } }, '
|
|
1473
|
+
h('strong', { style: { gridColumn: '1 / -1' } }, '通用授权'),
|
|
1545
1474
|
h('p', { style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1546
|
-
'
|
|
1547
|
-
h('
|
|
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),
|
|
1475
|
+
'上方描述规则任务要做什么;这里限定谁能请求、在哪里请求、由谁执行和授权多久。'),
|
|
1476
|
+
h('label', null, '执行方', h('select', { value: runner, onChange: (event) => selectRunner(event.target.value),
|
|
1558
1477
|
style: inputStyle },
|
|
1559
1478
|
h('option', { value: 'dsh', disabled: !dshAvailable && runner !== 'dsh' }, 'DSH · 当前团队模型'),
|
|
1560
|
-
h('option', { value: 'codex', disabled:
|
|
1561
|
-
h('option', { value: 'claude-code', disabled:
|
|
1479
|
+
h('option', { value: 'codex', disabled: runner !== 'codex' }, 'Codex · DSH 子 Agent'),
|
|
1480
|
+
h('option', { value: 'claude-code', disabled: runner !== 'claude-code' }, 'Claude Code · DSH 子 Agent')),
|
|
1562
1481
|
h('span', { style: mutedTextStyle }, runner === 'codex'
|
|
1563
|
-
? 'Codex
|
|
1564
|
-
: runner === 'claude-code' ? 'Claude Code
|
|
1565
|
-
: 'DSH
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
style: { gridColumn: '1 / -1', display: multiAgentMode ? 'grid' : 'none', gap: 8 } },
|
|
1573
|
-
h('strong', null, '参与交叉审查的 Agent(至少选择两位)'),
|
|
1574
|
-
h('p', { style: mutedTextStyle }, '请求人需要在触发短语开头的任务消息中明确提出“交叉 review”,按需策略才会并行运行。'),
|
|
1482
|
+
? 'Codex 暂不可用于协作任务:ANT 无法核验它的逐任务文件、Git 和网络权限。'
|
|
1483
|
+
: runner === 'claude-code' ? 'Claude Code 暂不可用于协作任务:ANT 无法核验它的逐任务文件、Git 和网络权限。'
|
|
1484
|
+
: 'DSH 使用获准团队模型处理对话内容和 ANT 已核验后提供的资源。')),
|
|
1485
|
+
h('details', { id: 'ant-parallel-agents-details', open: parallelAgentsOpen,
|
|
1486
|
+
onToggle: (event) => setParallelAgentsOpen(event.currentTarget.open),
|
|
1487
|
+
style: { gridColumn: '1 / -1', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: 10 } },
|
|
1488
|
+
h('summary', { style: { cursor: 'pointer', fontWeight: 600 } }, parallelAgentsSummary
|
|
1489
|
+
? `并行执行:${parallelAgentsSummary}` : '多 Agent 并行(可选)'),
|
|
1490
|
+
h('p', { style: mutedTextStyle }, '当前只开放 DSH。外部 Agent 的逐任务文件、Git 和网络权限无法由 ANT 核验;编辑旧任务时可取消其选择并改用 DSH。'),
|
|
1575
1491
|
h('div', { style: { display: 'flex', gap: 14, flexWrap: 'wrap' } },
|
|
1576
|
-
h('label', null, h('input', { type: 'checkbox', 'aria-label': '
|
|
1492
|
+
h('label', null, h('input', { type: 'checkbox', 'aria-label': '任务 Agent DSH', checked: reviewers.includes('dsh'),
|
|
1577
1493
|
disabled: !dshAvailable && !reviewers.includes('dsh'),
|
|
1578
1494
|
onChange: (event) => changeReviewer('dsh', event.target.checked) }), ' DSH'),
|
|
1579
|
-
h('label', null, h('input', { type: 'checkbox', 'aria-label': '
|
|
1580
|
-
disabled:
|
|
1495
|
+
h('label', null, h('input', { type: 'checkbox', 'aria-label': '任务 Agent Codex', checked: reviewers.includes('codex'),
|
|
1496
|
+
disabled: !reviewers.includes('codex'),
|
|
1581
1497
|
onChange: (event) => changeReviewer('codex', event.target.checked) }), ' Codex'),
|
|
1582
|
-
h('label', null, h('input', { type: 'checkbox', 'aria-label': '
|
|
1583
|
-
disabled:
|
|
1498
|
+
h('label', null, h('input', { type: 'checkbox', 'aria-label': '任务 Agent Claude Code', checked: reviewers.includes('claude-code'),
|
|
1499
|
+
disabled: !reviewers.includes('claude-code'),
|
|
1584
1500
|
onChange: (event) => changeReviewer('claude-code', event.target.checked) }), ' Claude Code')),
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
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,
|
|
1594
|
-
reviewers.length > 1 && defaults?.cross_review_policy_version === 1
|
|
1595
|
-
? h('fieldset', { style: { margin: 0, padding: '8px 10px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8 } },
|
|
1596
|
-
h('legend', { style: mutedTextStyle }, '运行策略'),
|
|
1597
|
-
h('label', { style: { display: 'flex', gap: 8, alignItems: 'start', marginBottom: 6 } },
|
|
1598
|
-
h('input', { type: 'radio', name: 'ant-cross-review-policy', checked: !crossReviewAlways,
|
|
1599
|
-
onChange: () => setCrossReviewAlways(false) }),
|
|
1600
|
-
h('span', null, `默认由 ${reviewerLabel(runner)} 执行;请求人可逐次要求交叉审查。`)),
|
|
1601
|
-
h('label', { style: { display: 'flex', gap: 8, alignItems: 'start' } },
|
|
1602
|
-
h('input', { type: 'radio', name: 'ant-cross-review-policy', checked: crossReviewAlways,
|
|
1603
|
-
onChange: () => setCrossReviewAlways(true) }),
|
|
1604
|
-
h('span', null, '每次任务都并行运行全部已选 Agent;请求人无需额外提出交叉审查。')))
|
|
1605
|
-
: reviewers.length > 1 ? h('p', { style: mutedTextStyle }, '每次并行模式需升级 ANT 服务端;当前仍可由请求人逐次提出交叉审查。') : null,
|
|
1606
|
-
!hasPrParameter ? h('p', { style: mutedTextStyle }, '从描述识别出 PR 任务后,才能使用 Codex 或 Claude Code。') : null),
|
|
1607
|
-
h('strong', { style: { gridColumn: '1 / -1', paddingTop: 8, borderTop: '1px solid var(--dsw-alias-border-l2)' } }, '请求范围'),
|
|
1501
|
+
null),
|
|
1502
|
+
nativePrUnavailableReason ? h('p', { role: 'status', style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1503
|
+
`${nativePrUnavailableReason}。当前仍可使用 DSH 团队模型处理任务。`) : null,
|
|
1608
1504
|
h('label', null, '使用场景', h('select', { value: conversationKind,
|
|
1609
1505
|
onChange: (event) => { setConversationKind(event.target.value); setGroupId('') }, style: inputStyle },
|
|
1610
1506
|
h('option', { value: 'dm' }, '私聊'), h('option', { value: 'group' }, '指定群聊'))),
|
|
1611
|
-
conversationKind === 'group' ? h('div', { style: {
|
|
1612
|
-
|
|
1613
|
-
h('label', null, '
|
|
1614
|
-
onChange: (event) =>
|
|
1507
|
+
conversationKind === 'group' ? h('div', { style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) auto',
|
|
1508
|
+
gap: 8, alignItems: 'end' } },
|
|
1509
|
+
h('label', null, '群聊', h('select', { required: true, value: groupId,
|
|
1510
|
+
onChange: (event) => setGroupId(event.target.value), style: inputStyle },
|
|
1615
1511
|
h('option', { value: '' }, '让群成员先 @ 机器人,再刷新列表'),
|
|
1616
|
-
...availableGroups.map((group) => h('option', { key: group.group_id, value: group.group_id
|
|
1617
|
-
|
|
1512
|
+
...availableGroups.map((group) => h('option', { key: group.group_id, value: group.group_id },
|
|
1513
|
+
groupLabel(group))))),
|
|
1618
1514
|
h('button', { type: 'button', disabled: busy,
|
|
1619
|
-
onClick: () => void load().catch((error) =>
|
|
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,
|
|
1515
|
+
onClick: () => void load().catch((error) => setMessage(error.message)), style: secondaryButtonStyle }, '刷新群列表')) : null,
|
|
1623
1516
|
h('label', null, '谁可以请求', h('select', { value: senderScope,
|
|
1624
|
-
onChange: (event) =>
|
|
1517
|
+
onChange: (event) => setSenderScope(event.target.value), style: inputStyle },
|
|
1625
1518
|
h('option', { value: 'any' }, conversationKind === 'group' ? '此群任何成员' : '任何能私聊此机器人者'),
|
|
1626
1519
|
h('option', { value: 'allowlist' }, '指定 IM 用户 ID'))),
|
|
1627
|
-
senderScope === 'allowlist' ? h('label', null, '可请求成员 ID', h('input', {
|
|
1628
|
-
required: true, value: senderIds, onChange: (event) =>
|
|
1520
|
+
senderScope === 'allowlist' ? h('label', null, '可请求成员 ID', h('input', { 'aria-label': '可请求的 IM 用户 ID',
|
|
1521
|
+
required: true, value: senderIds, onChange: (event) => setSenderIds(event.target.value),
|
|
1629
1522
|
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,
|
|
1633
1523
|
editingCardId ? h('p', { style: { ...mutedTextStyle, alignSelf: 'end' } },
|
|
1634
1524
|
`有效期:${new Date(editingExpiresAt * 1000).toLocaleString('zh-CN')}(编辑时保持不变)`)
|
|
1635
1525
|
: h('label', null, '有效期', h('select', { value: days, onChange: (event) => setDays(Number(event.target.value)), style: inputStyle },
|
|
1636
|
-
h('option', { value: 1 }, '1 天'), h('option', { value: 7 }, '7 天'),
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
h('aside', { id: 'ant-
|
|
1526
|
+
h('option', { value: 1 }, '1 天'), h('option', { value: 7 }, '7 天'), h('option', { value: 30 }, '30 天'))),
|
|
1527
|
+
(runner === 'codex' || runner === 'claude-code') ? h('p', { style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1528
|
+
'Provider Profile 的本机权限由 DSH 管理;ANT 当前不会用 Codex 或 Claude Code 执行协作任务,也不会把 Gitea 凭据交给它们。') : null),
|
|
1529
|
+
!hasPrParameter ? h('p', { style: mutedTextStyle },
|
|
1530
|
+
'Codex 和 Claude Code 即使已在 DSH 注册,目前也不用于任何协作任务。') : null),
|
|
1531
|
+
h('aside', { id: 'ant-authorization-review', role: 'region', 'aria-label': '开放前复核', tabIndex: -1,
|
|
1642
1532
|
style: { ...settingsPanelStyle, padding: 14, gap: 10, alignSelf: 'stretch' } },
|
|
1643
1533
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
1644
|
-
h('h4', { style: sectionTitleStyle }, '
|
|
1534
|
+
h('h4', { style: sectionTitleStyle }, '开放前看一眼'),
|
|
1645
1535
|
h('span', { style: { ...mutedTextStyle, padding: '3px 8px', borderRadius: 999,
|
|
1646
1536
|
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, '草稿 · 未开放')),
|
|
1647
|
-
h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } },
|
|
1648
|
-
(title || '未命名任务') + ' · 触发短语“' + (ruleTriggerSummary.join('”或“') || '尚未填写') + '”'),
|
|
1649
1537
|
h('p', { style: mutedTextStyle }, `这项任务将在${conversationKind === 'group'
|
|
1650
|
-
? `群“${selectedGroupSummary || '尚未选择'}”` : '私聊'}开放给${requesterSummary}
|
|
1538
|
+
? `群“${selectedGroupSummary || '尚未选择'}”` : '私聊'}开放给${requesterSummary},由${executorSummary}执行;`
|
|
1651
1539
|
+ `有效期 ${editingCardId ? new Date(editingExpiresAt * 1000).toLocaleString('zh-CN') : `${days} 天`},${resultRouteSummary}。`),
|
|
1540
|
+
hasPrParameter && !selectedGiteaOrigin && taskPrOrigins.length === 1
|
|
1541
|
+
? h('p', { role: 'note', style: mutedTextStyle },
|
|
1542
|
+
`首次确认开放后,这台机器人会绑定到 ${taskPrOrigins[0]};后续 PR 任务只允许使用此主机,令牌不会发往其他主机。`)
|
|
1543
|
+
: null,
|
|
1544
|
+
hasPrParameter && selectedGiteaOrigin
|
|
1545
|
+
? h('p', { role: 'note', style: mutedTextStyle },
|
|
1546
|
+
`这台机器人已绑定 ${selectedGiteaOrigin};任务来源需与此主机一致,令牌不会发往其他主机。`)
|
|
1547
|
+
: null,
|
|
1652
1548
|
cardBlockReason ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1653
1549
|
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,
|
|
1655
1550
|
h('button', { type: 'submit', form: 'ant-capability-create-form', disabled: busy || Boolean(cardBlockReason),
|
|
1656
1551
|
style: { ...primaryButtonStyle, width: '100%' } }, editingCardId ? '保存能力修改' : '确认并开放能力'))))),
|
|
1657
1552
|
h('div', { role: 'region', 'aria-label': '本机处理设置', style: { display: activeTab === 'local' ? 'grid' : 'none', gap: 12 } },
|
|
1658
|
-
trustedOriginCard,
|
|
1659
1553
|
showPrSecuritySettings ? h('section', { style: { ...settingsPanelStyle, padding: 14 } },
|
|
1660
1554
|
h('strong', null, 'PR 只读凭据(本机)'),
|
|
1661
1555
|
h('p', { style: mutedTextStyle }, tokenConfigured
|
|
1662
|
-
? '
|
|
1663
|
-
: '读取 Gitea PR
|
|
1556
|
+
? '令牌由 DSH 本机凭据服务保存。ANT Host 在读取已授权 PR 时会使用它;不会同步到 ANT 服务端或写入审查提示词。'
|
|
1557
|
+
: '读取 Gitea PR 需要只读访问令牌。保存后由 DSH 本机凭据服务管理,ANT Host 只在读取已授权 PR 时使用。'),
|
|
1558
|
+
h('p', { role: 'note', style: mutedTextStyle }, 'DSH 默认将凭据保存在 DSH Home 的私有文件中,环境变量也可作为回退来源。文件权限只隔离其他 OS 用户;同一用户权限下的 Agent 进程可能读取它。ANT 当前只允许无 DSH 工具的任务会话处理对话内容和 Host 核验后提供的资源。Codex 和 Claude Code 子 Agent 暂停用于协作任务,因为 ANT 无法核验其逐任务文件、Git 和网络权限。ANT Gitea 凭据只由 Host Connector 使用,不会放入 Agent 请求。'),
|
|
1559
|
+
h('p', { role: 'note', style: mutedTextStyle }, selectedGiteaOrigin
|
|
1560
|
+
? `这枚只读令牌仅用于已绑定主机 ${selectedGiteaOrigin};任务来源来自自然语言描述,但必须匹配该主机。`
|
|
1561
|
+
: '首次确认并开放包含 PR 来源的任务时,会将该来源绑定到这台机器人;确认前 ANT 不会把只读令牌发送到任务描述中的地址。'),
|
|
1664
1562
|
h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
|
|
1665
1563
|
h('input', { 'aria-label': 'Gitea 只读访问令牌', type: 'password', autoComplete: 'new-password', value: giteaToken,
|
|
1666
1564
|
onChange: (event) => setGiteaToken(event.target.value),
|
|
@@ -1670,7 +1568,7 @@ window.__ModuleLoader__.load({
|
|
|
1670
1568
|
onClick: () => void saveGiteaToken(), style: secondaryButtonStyle }, tokenConfigured ? '替换本机令牌' : '保存本机令牌'))) : null,
|
|
1671
1569
|
h('details', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: 12 } },
|
|
1672
1570
|
h('summary', { style: { cursor: 'pointer', fontWeight: 600 } }, '本机处理设置'),
|
|
1673
|
-
h('p', { style: mutedTextStyle }, '
|
|
1571
|
+
h('p', { style: mutedTextStyle }, '本机并发上限只影响这台电脑接收的新任务。外部代码执行器已暂停,当前不创建 PR 仓库缓存;缓存上限仅保留用于旧缓存回收,暂不可调整。'),
|
|
1674
1572
|
h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' } },
|
|
1675
1573
|
h('span', { style: mutedTextStyle }, loadFailures.includes('settings') ? '团队默认值暂未读取。'
|
|
1676
1574
|
: `团队默认:先看 ${defaults?.context_messages ?? 10} 条前文、最多翻阅 ${defaults?.history_max_records ?? 100} 条平台记录、同时处理 ${defaults?.max_concurrency ?? 3} 项任务。`),
|
|
@@ -1684,26 +1582,28 @@ window.__ModuleLoader__.load({
|
|
|
1684
1582
|
h('option', { value: '' }, '跟随团队默认'),
|
|
1685
1583
|
...Array.from({ length: 8 }, (_, index) => h('option', { key: index + 1, value: index + 1 }, `${index + 1} 项任务`)))),
|
|
1686
1584
|
h('label', { style: { display: 'grid', gap: 5, flex: '0 1 200px', maxWidth: '100%' } },
|
|
1687
|
-
'这台电脑 PR 仓库缓存上限(GiB
|
|
1688
|
-
h('input', { 'aria-label': '这台电脑 PR 仓库缓存上限(GiB)', type: 'number', min: 1, max: 100, step: 1,
|
|
1585
|
+
'这台电脑 PR 仓库缓存上限(GiB,暂不可调整)',
|
|
1586
|
+
h('input', { 'aria-label': '这台电脑 PR 仓库缓存上限(GiB)', type: 'number', min: 1, max: 100, step: 1,
|
|
1587
|
+
disabled: true,
|
|
1689
1588
|
value: repositoryCacheLimitGib,
|
|
1690
|
-
onChange: (event) => setRepositoryCacheLimitGib(event.target.value),
|
|
1691
1589
|
style: { ...inputStyle, width: 128, maxWidth: '100%', boxSizing: 'border-box' } })),
|
|
1692
1590
|
h('button', { disabled: busy, style: secondaryButtonStyle }, '保存本机处理设置'),
|
|
1693
1591
|
h('p', { style: { ...mutedTextStyle, flex: '1 1 100%', margin: 0 } },
|
|
1694
|
-
'
|
|
1592
|
+
'当前不拉取 PR 仓库快照;刷新授权状态时仍会清理已停用或到期能力的旧缓存。')),
|
|
1695
1593
|
),
|
|
1696
|
-
h('section', { style: { ...settingsPanelStyle, padding: 14, gap: 8 } },
|
|
1594
|
+
h('section', { 'aria-label': '执行方状态与权限边界', style: { ...settingsPanelStyle, padding: 14, gap: 8 } },
|
|
1697
1595
|
h('strong', null, '执行方状态与权限边界'),
|
|
1698
|
-
h('p', { style: mutedTextStyle }, '
|
|
1596
|
+
h('p', { style: mutedTextStyle }, '这里显示 DSH 本机的执行器状态;Codex 或 Claude Code 已注册,不代表它们获准执行协作任务。'),
|
|
1699
1597
|
loadFailures.includes('executors') ? h('p', { role: 'status', style: mutedTextStyle }, '暂时无法读取执行方状态,请刷新后重试。') : null,
|
|
1700
|
-
...executors.filter((item) => ['dsh', 'codex
|
|
1598
|
+
...executors.filter((item) => ['dsh', 'codex', 'claude-code'].includes(item.id)).map((item) => h('div', {
|
|
1701
1599
|
key: item.id, style: { display: 'grid', gap: 3, paddingTop: 8, borderTop: '1px solid var(--dsw-alias-border-l2)' } },
|
|
1702
|
-
h('strong', null, item.title + ' · ' + (item.
|
|
1703
|
-
|
|
1704
|
-
? '
|
|
1600
|
+
h('strong', null, item.title + ' · ' + (item.id === 'dsh'
|
|
1601
|
+
? item.available ? '可用' : item.reason || '未就绪'
|
|
1602
|
+
: item.available ? '已注册 · 协作任务暂不可用' : '协作任务暂不可用')),
|
|
1603
|
+
h('span', { style: mutedTextStyle }, item.id === 'codex'
|
|
1604
|
+
? 'ANT 暂不启动 Codex 子 Agent,因为当前无法核验逐任务文件、Git 和网络权限。'
|
|
1705
1605
|
: item.id === 'claude-code'
|
|
1706
|
-
? '
|
|
1606
|
+
? 'ANT 暂不启动 Claude Code 子 Agent,因为当前无法核验逐任务文件、Git 和网络权限。'
|
|
1707
1607
|
: '处理授权任务提供的内容,不调用本机工具。'))))))
|
|
1708
1608
|
}
|
|
1709
1609
|
|