@ai-wendao/ant 0.5.8 → 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 +275 -241
- package/conversation.js +203 -263
- package/cordis.patch.yml +6 -6
- package/delegation.js +55 -101
- package/index.js +4 -19
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -517,23 +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 [
|
|
539
|
+
const [parallelAgentsOpen, setParallelAgentsOpen] = useState(false)
|
|
537
540
|
const [giteaToken, setGiteaToken] = useState('')
|
|
538
541
|
const [tokenConfigured, setTokenConfigured] = useState(false)
|
|
539
542
|
const [senderScope, setSenderScope] = useState('any')
|
|
@@ -589,6 +592,9 @@ window.__ModuleLoader__.load({
|
|
|
589
592
|
const selectedBot = bots.find((bot) => bot.bot_id === botId)
|
|
590
593
|
const botConnected = Boolean(selectedBot?.available && selectedBot?.connected)
|
|
591
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))]
|
|
592
598
|
const selectedEnrollmentBound = selectedEnrollment?.device_bound_to_current === true
|
|
593
599
|
const dmChatSettingAvailable = typeof selectedEnrollment?.dm_chat_enabled === 'boolean'
|
|
594
600
|
const dmChatEnabled = selectedEnrollment?.dm_chat_enabled !== false
|
|
@@ -604,9 +610,12 @@ window.__ModuleLoader__.load({
|
|
|
604
610
|
? '允许此机器人接收私聊;任务仍按已开放的规则执行。'
|
|
605
611
|
: '新私聊消息不会交给 DSH;已提交任务仍会在原私聊回复。已开放能力不撤销,重新开启后未过期能力恢复触发。'
|
|
606
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
|
+
}
|
|
607
618
|
const groupChatLoadFailed = loadFailures.includes('groups')
|
|
608
|
-
const groupSummary = (group) => group?.group_name
|
|
609
|
-
? `${group.group_name} · ${group.group_code.slice(-2)}` : group?.group_code || ''
|
|
610
619
|
const activeCards = cards.filter((card) => !card.revoked_at && card.expires_at * 1000 > Date.now())
|
|
611
620
|
const connectedBots = bots.filter((bot) => bot.available && bot.connected)
|
|
612
621
|
const authorizationLoadError = loadFailures.includes('cards')
|
|
@@ -614,8 +623,6 @@ window.__ModuleLoader__.load({
|
|
|
614
623
|
const enrolledCount = enrolled.filter((item) => item.device_bound_to_current
|
|
615
624
|
&& connectedBots.some((bot) => bot.fingerprint === item.bot_fingerprint)).length
|
|
616
625
|
const selectedTaskCount = activeCards.filter((card) => card.bot_fingerprint === selectedBot?.fingerprint).length
|
|
617
|
-
const legacyTriggerCardCount = activeCards.filter((card) => card.bot_fingerprint === selectedBot?.fingerprint
|
|
618
|
-
&& (!Array.isArray(card.trigger_phrases) || !card.trigger_phrases.length)).length
|
|
619
626
|
const collaborationStatus = loading ? '正在读取机器人与任务状态…'
|
|
620
627
|
: botLoadError ? '暂时无法读取机器人状态。请刷新后再试。'
|
|
621
628
|
: connectedBots.length === 0 ? '尚未发现已连接的机器人。请先在 DSH-IM 中连接飞书机器人。'
|
|
@@ -636,34 +643,42 @@ window.__ModuleLoader__.load({
|
|
|
636
643
|
const hasPrParameter = parameters.some((item) => item.kind === 'gitea_pr')
|
|
637
644
|
const hasSelectedPrRule = activeCards.some((card) => card.bot_fingerprint === selectedBot?.fingerprint
|
|
638
645
|
&& Array.isArray(card.parameters) && card.parameters.some((rule) => rule.kind === 'gitea_pr'))
|
|
639
|
-
const showPrSecuritySettings = Boolean(selectedBot
|
|
640
|
-
|
|
641
|
-
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)
|
|
642
650
|
const claudeCodeAvailable = executors.some((item) => item.id === 'claude-code' && item.available)
|
|
643
651
|
const dshAvailable = executors.some((item) => item.id === 'dsh' && item.available)
|
|
644
652
|
const nativePrUnavailableReason = executors.find((item) => item.reason)?.reason || null
|
|
645
653
|
const reviewerLabel = (id) => ({ dsh: 'DSH', codex: 'Codex', 'claude-code': 'Claude Code' })[id] || id
|
|
646
654
|
const cardReviewerSummary = (card) => {
|
|
647
655
|
const selected = Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner]
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
:
|
|
653
|
-
: card.runner === 'claude-code' ? 'Claude Code PR 快照审查'
|
|
654
|
-
: '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'
|
|
655
661
|
}
|
|
656
662
|
const requesterSummary = senderScope === 'allowlist'
|
|
657
663
|
? `仅指定成员:${senderIds.trim() || '尚未填写成员 ID'}`
|
|
658
664
|
: conversationKind === 'group' ? '群内所有成员' : '任何能私聊此机器人的人'
|
|
659
665
|
const resultRouteSummary = conversationKind === 'group' ? '回复到原群(群内可见)' : '回复到发起任务的私聊'
|
|
660
|
-
const
|
|
661
|
-
const
|
|
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
|
+
])]
|
|
662
675
|
const parameterSummary = (rule) => {
|
|
663
676
|
const parts = [rule.required === false ? '可选' : '必填']
|
|
664
677
|
const choices = Array.isArray(rule.choices) ? rule.choices
|
|
665
678
|
: typeof rule.choices === 'string' ? rule.choices.split(/[,,\n]/u).map((item) => item.trim()).filter(Boolean) : []
|
|
666
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')
|
|
667
682
|
else if (rule.kind === 'choice' && choices.length) parts.push(`选项 ${choices.join('、')}`)
|
|
668
683
|
else if (rule.kind === 'integer') {
|
|
669
684
|
const minValue = rule.min == null || rule.min === '' ? null : Number(rule.min)
|
|
@@ -683,54 +698,49 @@ window.__ModuleLoader__.load({
|
|
|
683
698
|
? `请求时还需提供:${parameters.map(parameterSummary).join(';')}`
|
|
684
699
|
: '无需额外参数,按请求中的内容处理'
|
|
685
700
|
const ruleSummaryNarrative = ruleSummaryReady
|
|
686
|
-
? `${
|
|
701
|
+
? `${intent.trim() || title.trim()}${taskPrompt.trim() ? `;${taskPrompt.trim()}` : ''};${recognizedInputsSummary}`
|
|
687
702
|
: '识别后会用一段摘要呈现系统理解的任务。'
|
|
688
|
-
const executorSummary =
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
: runner === 'codex' ? '本机 Codex · PR 快照'
|
|
692
|
-
: runner === 'claude-code' ? '本机 Claude Code · PR 快照' : 'DSH · 当前授权模型'
|
|
693
|
-
const crossReviewSummary = reviewers.filter((item) => item !== runner).map(reviewerLabel).join('、')
|
|
694
|
-
const crossReviewHeading = crossReviewAlways && reviewers.length > 1
|
|
695
|
-
? `每次交叉审查:${reviewers.map(reviewerLabel).join('、')}`
|
|
696
|
-
: crossReviewSummary ? `交叉审查:${crossReviewSummary}` : '交叉审查(可选)'
|
|
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('、')
|
|
697
706
|
const cardBlockReason = enrollmentLoadError ? '暂时无法核对机器人是否已启用协作,请刷新状态后再试。'
|
|
698
707
|
: !botConnected || !selectedEnrollmentBound ? '先在上方把机器人绑定到这台 DSH。'
|
|
699
|
-
: loadFailures.includes('settings') || defaults?.task_rule_schema_version !==
|
|
700
|
-
? '
|
|
708
|
+
: loadFailures.includes('settings') || defaults?.task_rule_schema_version !== 3
|
|
709
|
+
? '团队服务端尚未支持 AI 任务 Harness 与运行时授权核验;请先升级 ANT 服务端。'
|
|
701
710
|
: !ruleDescription.trim() ? '请先用自然语言描述要做的任务。'
|
|
702
|
-
: recognizedDescriptionRef.current !==
|
|
703
|
-
? '
|
|
704
|
-
: !title.trim() || !intent.trim() || !taskPrompt.trim() ? '
|
|
705
|
-
:
|
|
711
|
+
: recognizedDescriptionRef.current !== ruleDescription.trim()
|
|
712
|
+
? 'AI 正在根据最新描述检查任务 Harness;分析完成后再开放。'
|
|
713
|
+
: !title.trim() || !intent.trim() || !taskPrompt.trim() ? '等待 AI 生成任务理解和处理要求。'
|
|
714
|
+
: missingHarnessInputs.length ? `任务范围还有必需信息未明确:${missingHarnessInputs.join('、')}。请补充描述。`
|
|
706
715
|
: conversationKind === 'group' && loadFailures.includes('groups') ? '暂时无法核对目标群,请刷新状态后再试。'
|
|
707
716
|
: conversationKind === 'group' && !groupId ? '请在“请求范围”中选择目标群;若列表为空,让群成员先 @机器人,再刷新群列表。'
|
|
708
717
|
: senderScope === 'allowlist' && !senderIds.trim() ? '请填写可请求成员的 IM ID。'
|
|
718
|
+
: reviewers.some((item) => item !== 'dsh')
|
|
719
|
+
? '当前只允许 DSH 执行协作任务。请将默认执行方改为 DSH,并移除未获准的外部 Agent。'
|
|
709
720
|
: parameters.some((rule) => rule.kind === 'gitea_pr') && !tokenConfigured ? '请先在“本机与任务”中保存本机 Gitea 只读令牌。'
|
|
710
|
-
: parameters.some((rule) => rule.kind === 'gitea_pr'
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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 尚未准备好任务执行器。'
|
|
725
736
|
: ''
|
|
726
737
|
const loadNames = { bots: '机器人', 'enrolled-bots': '协作启用状态', groups: '群聊列表',
|
|
727
738
|
cards: '任务授权', settings: '团队默认值', executors: '本机执行器',
|
|
728
739
|
'local-settings': '本机设置', status: '最近任务' }
|
|
729
740
|
const changeReviewer = (id, enabled) => {
|
|
730
741
|
const next = enabled ? [...reviewers, id] : reviewers.filter((item) => item !== id)
|
|
731
|
-
if (!next.length) { setMessage('
|
|
742
|
+
if (!next.length) { setMessage('至少保留一位任务执行 Agent。'); return }
|
|
732
743
|
setReviewers([...new Set(next)])
|
|
733
|
-
if (next.length < 2) setCrossReviewAlways(false)
|
|
734
744
|
if (!next.includes(runner)) setRunner(next[0])
|
|
735
745
|
}
|
|
736
746
|
const selectRunner = (id) => {
|
|
@@ -745,48 +755,57 @@ window.__ModuleLoader__.load({
|
|
|
745
755
|
setRecognitionMessage('')
|
|
746
756
|
recognizedBaselineRef.current = null
|
|
747
757
|
recognizedDescriptionRef.current = null
|
|
758
|
+
autoAnalyzeDescriptionRef.current = null
|
|
759
|
+
recognitionRequestRef.current += 1
|
|
748
760
|
setRuleDescription('')
|
|
749
|
-
|
|
761
|
+
setMissingRequired([]); setSuggestions([]); setRuleExamples([])
|
|
750
762
|
setTitle(''); setIntent(''); setTaskPrompt(''); setRunner('dsh')
|
|
751
|
-
setReviewers(['dsh']);
|
|
763
|
+
setReviewers(['dsh']); setParallelAgentsOpen(false)
|
|
752
764
|
setParameters([]); setConversationKind(context.conversationKind || 'dm')
|
|
753
765
|
setGroupId(context.groupId || '')
|
|
754
766
|
setSenderScope('any'); setSenderIds(''); setDays(7); setCreateOpen(true)
|
|
755
767
|
if (context.botId) setBotId(context.botId)
|
|
756
768
|
}
|
|
757
|
-
const taskRuleDraftFromCard = (card, title) =>
|
|
758
|
-
|
|
759
|
-
trigger_phrases: Array.isArray(card.trigger_phrases) ? card.trigger_phrases.map((item) => item.trim()).filter(Boolean) : [],
|
|
760
|
-
parameters: (card.parameters || []).map((rule) => ({
|
|
769
|
+
const taskRuleDraftFromCard = (card, title) => {
|
|
770
|
+
const rules = (card.parameters || []).map((rule) => ({
|
|
761
771
|
name: rule.name, kind: rule.kind, required: rule.required,
|
|
762
772
|
description: rule.description || '', min: rule.min == null ? '' : String(rule.min),
|
|
763
773
|
max: rule.max == null ? '' : String(rule.max),
|
|
764
774
|
choices: Array.isArray(rule.choices) ? rule.choices.join(', ') : String(rule.choices ?? ''),
|
|
765
775
|
gitea_origin: rule.gitea_origin || '', repository: rule.repository || '',
|
|
766
|
-
}))
|
|
767
|
-
|
|
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
|
+
}
|
|
768
789
|
const editCard = (card, copy = false) => {
|
|
769
790
|
setBotId(card.bot_id)
|
|
770
791
|
setRecognitionMessage('')
|
|
771
|
-
|
|
792
|
+
autoAnalyzeDescriptionRef.current = null
|
|
793
|
+
recognitionRequestRef.current += 1
|
|
772
794
|
setEditingCardId(copy ? null : card.id); setEditingExpiresAt(copy ? null : card.expires_at)
|
|
773
795
|
const taskDraft = taskRuleDraftFromCard(card, copy ? `${card.title.slice(0, 94)}(副本)` : card.title)
|
|
774
796
|
recognizedBaselineRef.current = taskDraft
|
|
775
|
-
const existingDescription = card.rule_description ||
|
|
797
|
+
const existingDescription = card.rule_description || ''
|
|
776
798
|
recognizedDescriptionRef.current = existingDescription.trim() || null
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
? '这项旧任务没有明确触发方式;新版插件不会按它启动任务。请升级 ANT 服务端后重新描述、识别并开放规则。'
|
|
780
|
-
: '')
|
|
799
|
+
setDraftNotice(existingDescription.trim() ? ''
|
|
800
|
+
: '这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。')
|
|
781
801
|
setRuleDescription(existingDescription)
|
|
782
|
-
|
|
802
|
+
setMissingRequired([]); setSuggestions(taskDraft.suggestions); setRuleExamples(taskDraft.examples)
|
|
783
803
|
setTitle(taskDraft.title)
|
|
784
804
|
if (copy) setDays(7)
|
|
785
805
|
setIntent(taskDraft.intent)
|
|
786
806
|
setTaskPrompt(taskDraft.task_prompt); setRunner(card.runner)
|
|
787
807
|
setReviewers(Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner])
|
|
788
|
-
|
|
789
|
-
setCrossReviewOpen(false)
|
|
808
|
+
setParallelAgentsOpen(false)
|
|
790
809
|
setParameters(taskDraft.parameters)
|
|
791
810
|
setConversationKind(card.conversation_kind); setGroupId(card.conversation_id || '')
|
|
792
811
|
setSenderScope(card.sender_scope); setSenderIds((card.sender_ids || []).join(', '))
|
|
@@ -795,28 +814,35 @@ window.__ModuleLoader__.load({
|
|
|
795
814
|
const applyRuleDraft = (draft) => {
|
|
796
815
|
setTitle(draft.title)
|
|
797
816
|
setIntent(draft.intent)
|
|
798
|
-
setTriggerPhrases(draft.trigger_phrases.join('\n'))
|
|
799
817
|
setTaskPrompt(draft.task_prompt)
|
|
800
818
|
setParameters(draft.parameters)
|
|
819
|
+
setMissingRequired(draft.missing_required || [])
|
|
820
|
+
setSuggestions(draft.suggestions || [])
|
|
821
|
+
setRuleExamples(draft.examples || [])
|
|
801
822
|
}
|
|
802
|
-
const identifyRule = async () => {
|
|
803
|
-
|
|
823
|
+
const identifyRule = async (description = ruleDescription) => {
|
|
824
|
+
const requestedDescription = description.trim()
|
|
825
|
+
if (!requestedDescription) {
|
|
804
826
|
setRecognitionMessage('先描述这项任务何时触发、要做什么以及涉及哪些资料。')
|
|
805
827
|
return
|
|
806
828
|
}
|
|
807
|
-
|
|
829
|
+
if (recognitionTimerRef.current) clearTimeout(recognitionTimerRef.current)
|
|
830
|
+
const requestNumber = ++recognitionRequestRef.current
|
|
831
|
+
setRecognitionBusy(true); setMessage(''); setRecognitionMessage('AI 正在分析必需范围、可选条件和执行方式…')
|
|
808
832
|
try {
|
|
809
833
|
const response = await fetch('/api/ant-teams/delegation/recognize', {
|
|
810
834
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
811
|
-
body: JSON.stringify({ description:
|
|
835
|
+
body: JSON.stringify({ description: requestedDescription }),
|
|
812
836
|
})
|
|
813
837
|
const result = await readLocalJsonResponse(response)
|
|
814
838
|
if (!response.ok) throw new Error(result.message || '规则识别失败。')
|
|
815
839
|
const candidate = {
|
|
816
840
|
title: typeof result.title === 'string' ? result.title : '',
|
|
817
841
|
intent: typeof result.intent === 'string' ? result.intent : '',
|
|
818
|
-
trigger_phrases: Array.isArray(result.trigger_phrases) ? result.trigger_phrases : [],
|
|
819
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 : [],
|
|
820
846
|
parameters: Array.isArray(result.parameters) ? result.parameters.map((rule) => ({
|
|
821
847
|
name: typeof rule?.name === 'string' ? rule.name : '',
|
|
822
848
|
kind: typeof rule?.kind === 'string' ? rule.kind : 'text',
|
|
@@ -830,19 +856,45 @@ window.__ModuleLoader__.load({
|
|
|
830
856
|
repository: typeof rule?.repository === 'string' ? rule.repository : '',
|
|
831
857
|
})) : [],
|
|
832
858
|
}
|
|
833
|
-
if (
|
|
834
|
-
|
|
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 返回的任务理解不完整。请补充描述后重试。')
|
|
835
876
|
}
|
|
836
877
|
const previous = recognizedBaselineRef.current
|
|
837
878
|
applyRuleDraft(candidate)
|
|
838
879
|
recognizedBaselineRef.current = candidate
|
|
839
|
-
recognizedDescriptionRef.current =
|
|
880
|
+
recognizedDescriptionRef.current = requestedDescription
|
|
840
881
|
setRecognitionMessage(previous
|
|
841
|
-
? '
|
|
842
|
-
: '
|
|
843
|
-
} catch (error) {
|
|
844
|
-
|
|
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
|
+
}
|
|
845
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])
|
|
846
898
|
const saveGiteaToken = async () => {
|
|
847
899
|
if (!giteaToken.trim()) return
|
|
848
900
|
setBusy(true)
|
|
@@ -856,12 +908,7 @@ window.__ModuleLoader__.load({
|
|
|
856
908
|
}
|
|
857
909
|
const enrollBot = async () => {
|
|
858
910
|
if (!selectedBot) return
|
|
859
|
-
const
|
|
860
|
-
const origin = requestedOrigin?.trim() ? normalizeServiceUrl(requestedOrigin) : ''
|
|
861
|
-
if (requestedOrigin?.trim() && !origin) {
|
|
862
|
-
setMessage('请填写有效的 HTTPS Gitea 地址后再启用协作。')
|
|
863
|
-
return
|
|
864
|
-
}
|
|
911
|
+
const origin = selectedEnrollment?.gitea_origin || ''
|
|
865
912
|
setBusy(true)
|
|
866
913
|
try {
|
|
867
914
|
const response = await fetch('/api/ant-teams/delegation/enrolled-bots', {
|
|
@@ -872,29 +919,10 @@ window.__ModuleLoader__.load({
|
|
|
872
919
|
const result = await readLocalJsonResponse(response)
|
|
873
920
|
if (!response.ok) throw new Error(result.message || '协作机器人启用失败。')
|
|
874
921
|
await load()
|
|
875
|
-
if (origin) setTrustedOrigin(origin)
|
|
876
922
|
setMessage('协作机器人已启用。你可以分别设置私聊和群聊,再开放需要的能力。')
|
|
877
923
|
} catch (error) { setMessage(error.message) }
|
|
878
924
|
finally { setBusy(false) }
|
|
879
925
|
}
|
|
880
|
-
const saveTrustedOrigin = async () => {
|
|
881
|
-
if (!selectedBot || !selectedEnrollmentBound) return
|
|
882
|
-
const origin = normalizeServiceUrl(trustedOrigin)
|
|
883
|
-
if (!origin) { setMessage('请填写有效的 HTTPS Gitea 地址。'); return }
|
|
884
|
-
if (!window.confirm(`将这个机器人的可信 Gitea 地址设为 ${origin}?已开放能力中的 PR 参数需要重新核对。`)) return
|
|
885
|
-
setBusy(true)
|
|
886
|
-
try {
|
|
887
|
-
const response = await fetch('/api/ant-teams/delegation/enrolled-bots', {
|
|
888
|
-
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
889
|
-
body: JSON.stringify({ bot_id: selectedBot.bot_id, bot_fingerprint: selectedBot.fingerprint,
|
|
890
|
-
gitea_origin: origin }),
|
|
891
|
-
})
|
|
892
|
-
const result = await readLocalJsonResponse(response)
|
|
893
|
-
if (!response.ok) throw new Error(result.message || '可信地址保存失败。')
|
|
894
|
-
await load(); setTrustedOrigin(origin); setMessage('可信 Gitea 地址已保存。')
|
|
895
|
-
} catch (error) { setMessage(error.message) }
|
|
896
|
-
finally { setBusy(false) }
|
|
897
|
-
}
|
|
898
926
|
const saveGroupChat = async (enabled, targetGroupId) => {
|
|
899
927
|
if (!selectedBot || !selectedEnrollmentBound || !targetGroupId.trim()) return
|
|
900
928
|
const targetHasTasks = activeCards.some((card) => card.bot_fingerprint === selectedBot.fingerprint
|
|
@@ -956,7 +984,7 @@ window.__ModuleLoader__.load({
|
|
|
956
984
|
if (!response.ok) throw new Error(result.message || '本机处理设置保存失败。')
|
|
957
985
|
setLocalConcurrency(result.max_concurrency)
|
|
958
986
|
setRepositoryCacheLimitGib(result.repository_cache_limit_gib)
|
|
959
|
-
setMessage('
|
|
987
|
+
setMessage('本机并发设置已保存;仓库缓存上限保留原值。')
|
|
960
988
|
} catch (error) { setMessage(error.message) }
|
|
961
989
|
finally { setBusy(false) }
|
|
962
990
|
}
|
|
@@ -1026,8 +1054,8 @@ window.__ModuleLoader__.load({
|
|
|
1026
1054
|
event.preventDefault()
|
|
1027
1055
|
setDraftNotice('')
|
|
1028
1056
|
if (!selectedBot || !botConnected || !selectedEnrollmentBound) { setMessage('请先在这台 DSH 绑定选中的协作机器人。'); return }
|
|
1029
|
-
if (
|
|
1030
|
-
setMessage('
|
|
1057
|
+
if (reviewers.some((item) => item !== 'dsh')) {
|
|
1058
|
+
setMessage('当前无法核验外部子 Agent 的逐任务文件、Git 和网络权限;请将任务执行方设为 DSH。'); return
|
|
1031
1059
|
}
|
|
1032
1060
|
if (editingCardId && (!editingExpiresAt || editingExpiresAt <= Math.floor(Date.now() / 1000))) {
|
|
1033
1061
|
setMessage('这项能力已到期,无法继续编辑;请返回后重新开放一项能力。'); return
|
|
@@ -1040,18 +1068,14 @@ window.__ModuleLoader__.load({
|
|
|
1040
1068
|
|| bytes(intent.trim()) > 1000 || bytes(taskPrompt.trim()) > 4000) {
|
|
1041
1069
|
setMessage('任务描述或识别摘要过长,请缩短描述并重新识别。'); return
|
|
1042
1070
|
}
|
|
1043
|
-
const triggerLines = triggerPhrases.split('\n').map((item) => item.normalize('NFKC').trim()).filter(Boolean)
|
|
1044
|
-
if (!triggerLines.length || triggerLines.length > 8 || triggerLines.some((item) => bytes(item) > 120)
|
|
1045
|
-
|| new Set(triggerLines.map((item) => item.toLowerCase())).size !== triggerLines.length) {
|
|
1046
|
-
setMessage('没能从描述中识别出清楚的触发方式。请写明消息以什么开头时执行,再重新识别。'); return
|
|
1047
|
-
}
|
|
1048
1071
|
const normalized = parameters.map((rule) => ({ name: rule.name.trim(), kind: rule.kind,
|
|
1049
1072
|
description: rule.description.trim() || null,
|
|
1050
1073
|
required: rule.required, min: rule.min == null || rule.min === '' ? null : Number(rule.min),
|
|
1051
1074
|
max: rule.max == null || rule.max === '' ? null : Number(rule.max),
|
|
1052
1075
|
choices: rule.kind === 'choice' ? (Array.isArray(rule.choices) ? rule.choices
|
|
1053
1076
|
: String(rule.choices ?? '').split(/[,,\n]/u)).map((item) => item.trim()).filter(Boolean) : null,
|
|
1054
|
-
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,
|
|
1055
1079
|
repository: rule.kind === 'gitea_pr' ? rule.repository.trim().toLowerCase() : null }))
|
|
1056
1080
|
if (normalized.some((rule) => !/^[A-Za-z][A-Za-z0-9_]{0,31}$/u.test(rule.name))) {
|
|
1057
1081
|
setMessage('没能从描述中整理出有效的请求信息。请说明同事需要提供什么,再重新识别。'); return
|
|
@@ -1059,6 +1083,9 @@ window.__ModuleLoader__.load({
|
|
|
1059
1083
|
if (new Set(normalized.map((rule) => rule.name)).size !== normalized.length) {
|
|
1060
1084
|
setMessage('请求信息识别有重复项。请修改任务描述并重新识别。'); return
|
|
1061
1085
|
}
|
|
1086
|
+
if (normalized.some((rule) => rule.name === 'head_sha' && rule.kind !== 'git_sha')) {
|
|
1087
|
+
setMessage('head_sha 是 PR 提交版本的保留字段;请在自然语言描述中使用 PR Head SHA。'); return
|
|
1088
|
+
}
|
|
1062
1089
|
if (normalized.some((rule) => rule.description && bytes(rule.description) > 100)) {
|
|
1063
1090
|
setMessage('请求信息说明过长。请缩短任务描述并重新识别。'); return
|
|
1064
1091
|
}
|
|
@@ -1078,6 +1105,16 @@ window.__ModuleLoader__.load({
|
|
|
1078
1105
|
if (normalized.some((rule) => rule.kind === 'gitea_pr' && (!rule.gitea_origin || !rule.repository))) {
|
|
1079
1106
|
setMessage('没能从描述中识别 PR 来源或仓库。请补充完整 PR 链接或仓库名,再重新识别。'); return
|
|
1080
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
|
|
1117
|
+
}
|
|
1081
1118
|
const validRepository = (value) => {
|
|
1082
1119
|
const parts = value.split('/')
|
|
1083
1120
|
return parts.length === 2 && parts.every((part) => part !== '.' && part !== '..'
|
|
@@ -1087,23 +1124,23 @@ window.__ModuleLoader__.load({
|
|
|
1087
1124
|
setMessage('仓库名无法识别。请在描述中写明“团队名/仓库名”或粘贴完整 PR 链接,再重新识别。'); return
|
|
1088
1125
|
}
|
|
1089
1126
|
if (!reviewers.length || !reviewers.includes(runner)) {
|
|
1090
|
-
setMessage('
|
|
1127
|
+
setMessage('请至少选择一位任务执行 Agent,并设置普通请求的默认执行方。'); return
|
|
1091
1128
|
}
|
|
1092
|
-
if (nativeReviewerSelected && (normalized.length
|
|
1093
|
-
|
|
1094
|
-
|
|
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
|
|
1095
1134
|
}
|
|
1096
1135
|
const scope = conversationKind === 'group' ? `群 ${groupId}` : '私聊'
|
|
1097
1136
|
const resultDestination = conversationKind === 'group' ? '原群(群内可见)' : '发起任务的私聊'
|
|
1098
1137
|
const executionDetails = {
|
|
1099
|
-
dsh: 'DSH
|
|
1100
|
-
codex: 'Codex
|
|
1101
|
-
'claude-code': 'Claude Code
|
|
1138
|
+
dsh: 'DSH 使用获准的团队模型处理对话内容和 ANT 已核验后提供的资源。',
|
|
1139
|
+
codex: 'Codex 当前不能用于协作任务,因为 ANT 无法核验其逐任务文件、Git 和网络权限。',
|
|
1140
|
+
'claude-code': 'Claude Code 当前不能用于协作任务,因为 ANT 无法核验其逐任务文件、Git 和网络权限。',
|
|
1102
1141
|
}
|
|
1103
1142
|
const executionInfo = reviewers.length > 1
|
|
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(' ')}`
|
|
1143
|
+
? `普通请求默认由 ${reviewerLabel(runner)} 执行;请求人明确要求多 Agent 分别处理时,会并行启动 ${reviewers.map(reviewerLabel).join('、')},分别返回结果。${reviewers.map((item) => executionDetails[item]).join(' ')}`
|
|
1107
1144
|
: executionDetails[reviewers[0]]
|
|
1108
1145
|
const actionDescription = editingCardId
|
|
1109
1146
|
? `更新“${title}”后,${requesterSummary}可在${scope}按任务描述中的规则发起请求。旧待确认请求将失效,已提交任务继续使用提交时的配置。结果${resultDestination}。到期时间保持为 ${new Date(editingExpiresAt * 1000).toLocaleString('zh-CN')}。`
|
|
@@ -1115,10 +1152,9 @@ window.__ModuleLoader__.load({
|
|
|
1115
1152
|
method: editingCardId ? 'PUT' : 'POST', headers: { 'content-type': 'application/json' },
|
|
1116
1153
|
body: JSON.stringify({ ...(editingCardId ? { id: editingCardId } : {}),
|
|
1117
1154
|
bot_id: selectedBot.bot_id, bot_fingerprint: selectedBot.fingerprint,
|
|
1118
|
-
title, rule_description: ruleDescription.trim(), trigger_phrases:
|
|
1119
|
-
intent, examples:
|
|
1120
|
-
parameters: normalized, task_prompt: taskPrompt, runner, reviewers,
|
|
1121
|
-
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,
|
|
1122
1158
|
sender_scope: senderScope, sender_ids: senderScope === 'allowlist'
|
|
1123
1159
|
? senderIds.split(/[\s,,]+/u).filter(Boolean) : [], conversation_kind: conversationKind,
|
|
1124
1160
|
...(conversationKind === 'group' ? { conversation_id: groupId } : {}),
|
|
@@ -1126,7 +1162,7 @@ window.__ModuleLoader__.load({
|
|
|
1126
1162
|
})
|
|
1127
1163
|
const result = await readLocalJsonResponse(response)
|
|
1128
1164
|
if (!response.ok && result.code === 'IM_TASK_RULE_REQUIRED') {
|
|
1129
|
-
throw new Error('当前 ANT
|
|
1165
|
+
throw new Error('当前 ANT 服务端不支持自然语言任务 Harness,请升级 ANT 服务端后再开放任务。')
|
|
1130
1166
|
}
|
|
1131
1167
|
if (!response.ok && reviewers.includes('claude-code') && result.code === 'INVALID_REQUEST') {
|
|
1132
1168
|
throw new Error('当前团队服务端可能尚不支持 Claude Code 审查配置。请管理员升级服务端后刷新重试。')
|
|
@@ -1134,8 +1170,7 @@ window.__ModuleLoader__.load({
|
|
|
1134
1170
|
if (!response.ok) throw new Error(result.message || '能力保存失败。')
|
|
1135
1171
|
const savedReviewers = Array.isArray(result.reviewers) && result.reviewers.length
|
|
1136
1172
|
? result.reviewers : [result.runner]
|
|
1137
|
-
if ([...savedReviewers].sort().join('\0') !== [...reviewers].sort().join('\0')
|
|
1138
|
-
|| (result.cross_review_always === true) !== crossReviewAlways) {
|
|
1173
|
+
if ([...savedReviewers].sort().join('\0') !== [...reviewers].sort().join('\0')) {
|
|
1139
1174
|
if (!editingCardId && typeof result.id === 'string') {
|
|
1140
1175
|
const cleanup = await fetch('/api/ant-teams/delegation/cards', {
|
|
1141
1176
|
method: 'DELETE', headers: { 'content-type': 'application/json' },
|
|
@@ -1143,17 +1178,18 @@ window.__ModuleLoader__.load({
|
|
|
1143
1178
|
}).catch(() => null)
|
|
1144
1179
|
if (!cleanup?.ok) {
|
|
1145
1180
|
await load().catch(() => undefined)
|
|
1146
|
-
throw new Error('
|
|
1181
|
+
throw new Error('团队服务端尚不支持多 Agent 审查配置;旧服务端可能已保存单 Agent 授权,请刷新列表并停止它,再升级 ANT 服务端。')
|
|
1147
1182
|
}
|
|
1148
1183
|
}
|
|
1149
|
-
throw new Error('
|
|
1184
|
+
throw new Error('当前团队服务端尚不支持多 Agent 审查配置;请管理员升级 ANT 服务端后重试。')
|
|
1150
1185
|
}
|
|
1151
1186
|
recognizedBaselineRef.current = null
|
|
1152
1187
|
recognizedDescriptionRef.current = null
|
|
1153
|
-
|
|
1188
|
+
autoAnalyzeDescriptionRef.current = null
|
|
1189
|
+
recognitionRequestRef.current += 1
|
|
1190
|
+
setRuleDescription(''); setMissingRequired([]); setSuggestions([]); setRuleExamples([])
|
|
1154
1191
|
setTitle(''); setIntent(''); setTaskPrompt(''); setParameters([]); setRunner('dsh'); setReviewers(['dsh'])
|
|
1155
|
-
|
|
1156
|
-
setCrossReviewOpen(false)
|
|
1192
|
+
setParallelAgentsOpen(false)
|
|
1157
1193
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1158
1194
|
await load()
|
|
1159
1195
|
setCreateOpen(false)
|
|
@@ -1177,29 +1213,27 @@ window.__ModuleLoader__.load({
|
|
|
1177
1213
|
}
|
|
1178
1214
|
const copyCardToDraft = (card) => {
|
|
1179
1215
|
setEditingCardId(null); setEditingExpiresAt(null)
|
|
1180
|
-
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
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()
|
|
1185
1222
|
? '已复制到新草稿。复制不保留原有效期;请检查任务描述和请求范围。'
|
|
1186
|
-
: '
|
|
1223
|
+
: '这项旧任务没有自然语言描述。请补充任务描述,让 AI 生成 Harness 后再开放。')
|
|
1187
1224
|
const cardBot = bots.find((bot) => bot.fingerprint === card.bot_fingerprint)
|
|
1188
|
-
const cardEnrollment = enrolled.find((bot) => bot.bot_fingerprint === card.bot_fingerprint)
|
|
1189
1225
|
const titleChars = Array.from(`复制:${card.title || '未命名能力'}`)
|
|
1190
1226
|
while (new TextEncoder().encode(titleChars.join('')).length > 100) titleChars.pop()
|
|
1191
1227
|
const taskDraft = taskRuleDraftFromCard(card, titleChars.join(''))
|
|
1192
|
-
recognizedBaselineRef.current =
|
|
1228
|
+
recognizedBaselineRef.current = taskDraft
|
|
1193
1229
|
setBotId(card.bot_id || cardBot?.bot_id || '')
|
|
1194
|
-
setTrustedOrigin(cardEnrollment?.gitea_origin || '')
|
|
1195
1230
|
setRuleDescription(copiedDescription)
|
|
1196
|
-
|
|
1231
|
+
setMissingRequired([]); setSuggestions(taskDraft.suggestions); setRuleExamples(taskDraft.examples)
|
|
1197
1232
|
setTitle(taskDraft.title)
|
|
1198
1233
|
setIntent(taskDraft.intent)
|
|
1199
1234
|
setTaskPrompt(taskDraft.task_prompt)
|
|
1200
1235
|
setRunner(['dsh', 'codex', 'claude-code'].includes(card.runner) ? card.runner : 'dsh')
|
|
1201
1236
|
setReviewers(Array.isArray(card.reviewers) && card.reviewers.length ? card.reviewers : [card.runner || 'dsh'])
|
|
1202
|
-
setCrossReviewAlways(card.cross_review_always === true)
|
|
1203
1237
|
setParameters(taskDraft.parameters)
|
|
1204
1238
|
setConversationKind(card.conversation_kind === 'group' ? 'group' : 'dm')
|
|
1205
1239
|
setGroupId(card.conversation_id || '')
|
|
@@ -1229,8 +1263,7 @@ window.__ModuleLoader__.load({
|
|
|
1229
1263
|
loading ? '读取中…' : '刷新状态')),
|
|
1230
1264
|
h('p', { role: 'status', style: mutedTextStyle }, collaborationStatus),
|
|
1231
1265
|
bots.length ? h('label', null, '我的机器人', h('select', { id: 'ant-bot-selector', value: botId,
|
|
1232
|
-
onChange: (event) => { setBotId(event.target.value); setGroupId('')
|
|
1233
|
-
setTrustedOrigin(enrolled.find((bot) => bot.bot_id === event.target.value)?.gitea_origin || '') }, style: inputStyle },
|
|
1266
|
+
onChange: (event) => { setBotId(event.target.value); setGroupId('') }, style: inputStyle },
|
|
1234
1267
|
h('option', { value: '' }, '请选择机器人'),
|
|
1235
1268
|
...bots.map((bot) => h('option', { key: bot.bot_id, value: bot.bot_id,
|
|
1236
1269
|
disabled: !bot.available || !bot.connected }, `${bot.name}${bot.available && bot.connected ? '' : '(未连接)'}`)))) : null,
|
|
@@ -1242,23 +1275,6 @@ window.__ModuleLoader__.load({
|
|
|
1242
1275
|
selectedBot && !selectedEnrollmentBound ? h('button', { type: 'button', disabled: busy || !botConnected || enrollmentLoadError,
|
|
1243
1276
|
onClick: () => void enrollBot(), style: primaryButtonStyle },
|
|
1244
1277
|
selectedEnrollment ? '绑定原协作配置到这台 DSH' : '启用并绑定到这台 DSH') : null)
|
|
1245
|
-
const trustedOriginCard = showPrSecuritySettings ? h('section', { style: collaborationCardStyle },
|
|
1246
|
-
h('strong', null, 'PR 读取来源(可选)'),
|
|
1247
|
-
h('p', { style: mutedTextStyle }, '只有任务需要读取 Gitea PR 时才需设置。PR 链接、仓库和编号写在任务描述里;这里仅登记本机允许读取的 Gitea 来源。'),
|
|
1248
|
-
h('label', null, '机器人', h('select', { 'aria-label': 'Gitea 来源对应机器人', value: botId,
|
|
1249
|
-
onChange: (event) => { setBotId(event.target.value); setTrustedOrigin(
|
|
1250
|
-
enrolled.find((bot) => bot.bot_id === event.target.value)?.gitea_origin || '') }, style: inputStyle },
|
|
1251
|
-
h('option', { value: '' }, '请选择机器人'),
|
|
1252
|
-
...bots.map((bot) => h('option', { key: bot.bot_id, value: bot.bot_id,
|
|
1253
|
-
disabled: !bot.available || !bot.connected }, `${bot.name}${bot.available && bot.connected ? '' : '(未连接)'}`)))),
|
|
1254
|
-
selectedBot ? h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
|
|
1255
|
-
h('input', { 'aria-label': '可信 Gitea 地址', type: 'url', value: trustedOrigin,
|
|
1256
|
-
onChange: (event) => setTrustedOrigin(event.target.value), placeholder: 'https://git.example.com',
|
|
1257
|
-
style: { ...inputStyle, flex: '1 1 220px' } }),
|
|
1258
|
-
h('button', { type: 'button', disabled: busy || !trustedOrigin.trim() || (!selectedEnrollmentBound && !botConnected),
|
|
1259
|
-
onClick: () => void (selectedEnrollmentBound ? saveTrustedOrigin() : enrollBot()), style: secondaryButtonStyle },
|
|
1260
|
-
selectedEnrollmentBound ? '保存可信来源' : botConnected ? '启用并保存来源' : '先连接机器人'))
|
|
1261
|
-
: null) : null
|
|
1262
1278
|
const groupTaskStatusPanel = selectedEnrollmentBound && botConnected ? h('section', {
|
|
1263
1279
|
id: 'ant-group-task-status', role: 'region', 'aria-label': '各群聊天与任务状态', tabIndex: -1,
|
|
1264
1280
|
style: { ...collaborationCardStyle, gap: 10 } },
|
|
@@ -1269,7 +1285,7 @@ window.__ModuleLoader__.load({
|
|
|
1269
1285
|
loading ? '读取中…' : '刷新群列表')),
|
|
1270
1286
|
h('p', { style: mutedTextStyle }, groupChatLoadFailed
|
|
1271
1287
|
? '群列表暂不可用,请刷新状态后重试。'
|
|
1272
|
-
: availableGroups.length ? '
|
|
1288
|
+
: availableGroups.length ? '每个群分别控制普通聊天和任务;名称取自 DSH-IM 已保存目标,括号内为识别码末两位。未匹配名称时显示完整识别码。'
|
|
1273
1289
|
: '先在目标群 @机器人,再刷新状态发现群。'),
|
|
1274
1290
|
availableGroups.length ? h('div', { id: 'ant-group-card-grid', style: collaborationGridStyle },
|
|
1275
1291
|
...availableGroups.map((group) => {
|
|
@@ -1277,16 +1293,13 @@ window.__ModuleLoader__.load({
|
|
|
1277
1293
|
&& card.conversation_kind === 'group' && card.conversation_id === group.group_id).length
|
|
1278
1294
|
return h('article', { key: group.group_id, style: { ...collaborationCardStyle, padding: 10, gap: 8 } },
|
|
1279
1295
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
1280
|
-
h('
|
|
1281
|
-
h('strong', { style: { overflowWrap: 'anywhere' } }, group.group_name || group.group_code),
|
|
1282
|
-
group.group_name ? h('small', { style: mutedTextStyle, title: `完整识别码 ${group.group_code}` },
|
|
1283
|
-
`识别码尾号 ${group.group_code.slice(-2)}`) : null),
|
|
1296
|
+
h('strong', { title: `完整识别码 ${group.group_code}` }, groupLabel(group)),
|
|
1284
1297
|
h('span', { style: mutedTextStyle }, `已开放任务 ${taskCount} 项`)),
|
|
1285
1298
|
h('label', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
1286
1299
|
h('input', { type: 'checkbox', checked: group.chat_enabled,
|
|
1287
1300
|
disabled: busy || groupChatLoadFailed,
|
|
1288
1301
|
onChange: (event) => void saveGroupChat(event.target.checked, group.group_id),
|
|
1289
|
-
'aria-label':
|
|
1302
|
+
'aria-label': '允许群 ' + groupLabel(group) + ' 普通聊天' }), '普通聊天'),
|
|
1290
1303
|
h('button', { type: 'button', disabled: busy, style: secondaryButtonStyle,
|
|
1291
1304
|
onClick: () => { startNewCard({ conversationKind: 'group', groupId: group.group_id }); focusAfterTabChange('collaboration', 'ant-new-task-summary') } },
|
|
1292
1305
|
'为此群开放任务'))
|
|
@@ -1305,9 +1318,6 @@ window.__ModuleLoader__.load({
|
|
|
1305
1318
|
h('p', { style: mutedTextStyle }, '选择机器人,设置聊天入口,再按需要开放任务规则。聊天和任务授权分别控制。')),
|
|
1306
1319
|
h('span', { style: { ...mutedTextStyle, padding: '4px 9px', borderRadius: 999,
|
|
1307
1320
|
backgroundColor: 'var(--dsw-alias-bg-layer-2)' } }, selectedBot ? selectedBot.name : '尚未选择机器人')),
|
|
1308
|
-
legacyTriggerCardCount ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1309
|
-
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1310
|
-
'当前有 ' + legacyTriggerCardCount + ' 项旧任务规则没有明确触发方式;新版插件不会按这些旧规则触发任务。升级 ANT 服务端后,再重新描述并开放需要的规则。') : null,
|
|
1311
1321
|
loadFailures.length ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1312
1322
|
`部分协作信息暂时无法读取:${loadFailures.map((path) => loadNames[path]).join('、')}。已读取的信息仍显示,请稍后刷新状态。`) : null,
|
|
1313
1323
|
h('div', { id: 'ant-collaboration-settings-grid', style: collaborationSettingsGridStyle },
|
|
@@ -1335,9 +1345,11 @@ window.__ModuleLoader__.load({
|
|
|
1335
1345
|
const remainingDays = Math.max(1, Math.ceil((expiresAt - Date.now()) / 86_400_000))
|
|
1336
1346
|
const expiry = remainingDays <= 7 ? `${remainingDays} 天后到期` : `至 ${new Date(expiresAt).toLocaleDateString('zh-CN')}`
|
|
1337
1347
|
const conversation = card.conversation_kind === 'group'
|
|
1338
|
-
? `群 ${
|
|
1348
|
+
? `群 ${groupLabel(groups.find((group) => group.bot_fingerprint === card.bot_fingerprint
|
|
1349
|
+
&& group.group_id === card.conversation_id)) || card.conversation_id}` : '私聊'
|
|
1339
1350
|
const requester = card.sender_scope === 'allowlist' ? '指定成员可请求'
|
|
1340
1351
|
: card.conversation_kind === 'group' ? '群成员可请求' : '可私聊者可请求'
|
|
1352
|
+
const blockedExternalExecutor = card.runner !== 'dsh' || card.cross_review_always === true
|
|
1341
1353
|
const examplesText = Array.isArray(card.examples) ? card.examples.join('、') : ''
|
|
1342
1354
|
const requesterIds = card.sender_scope === 'allowlist' && Array.isArray(card.sender_ids) ? card.sender_ids.join('、') : ''
|
|
1343
1355
|
const remainingDaysLabel = remainingDays <= 7 ? 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))'
|
|
@@ -1349,10 +1361,13 @@ window.__ModuleLoader__.load({
|
|
|
1349
1361
|
h('span', { style: { ...mutedTextStyle, padding: '2px 8px', borderRadius: 999,
|
|
1350
1362
|
backgroundColor: 'var(--dsw-alias-bg-layer-2)', color: remainingDaysLabel, whiteSpace: 'nowrap' } }, expiry)),
|
|
1351
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,
|
|
1352
1367
|
card.rule_description
|
|
1353
1368
|
? h('span', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } }, card.rule_description)
|
|
1354
1369
|
: h('span', { role: 'status', style: { ...mutedTextStyle, color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } },
|
|
1355
|
-
'
|
|
1370
|
+
'旧任务卡没有自然语言描述。请编辑并补充描述,让 AI 生成 Harness 后再开放。'),
|
|
1356
1371
|
h('details', null,
|
|
1357
1372
|
h('summary', { style: { cursor: 'pointer', color: 'var(--dsw-alias-link)', fontSize: 13 } }, '查看规则说明'),
|
|
1358
1373
|
h('div', { style: { display: 'grid', gap: 4, paddingTop: 6 } },
|
|
@@ -1417,26 +1432,42 @@ window.__ModuleLoader__.load({
|
|
|
1417
1432
|
selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
1418
1433
|
'机器人及其任务能力只在这台 DSH 设备接收和执行;同一账号的其他设备不会继承本机授权。') : null,
|
|
1419
1434
|
selectedEnrollment && !selectedEnrollmentBound ? h('p', { style: mutedTextStyle },
|
|
1420
|
-
'
|
|
1435
|
+
'这是尚未绑定到设备的机器人。请在要继续使用它的电脑重新启用;原协作配置会保留。若旧设备访问曾被撤销,原任务权限不会恢复,需要重新开放。') : null,
|
|
1421
1436
|
selectedBot && conversationKind === 'group' && !selectedBot.history_available ? h('p', { role: 'status', style: mutedTextStyle },
|
|
1422
1437
|
'这个机器人当前不能读取平台群历史;群聊只能参考实际送达的消息和本机近期记录。请检查 DSH-IM 权限。') : 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 } },
|
|
1423
1441
|
h('label', null, '描述规则', h('textarea', { required: true, maxLength: 4000, rows: 4, value: ruleDescription,
|
|
1424
|
-
onChange: (event) => {
|
|
1425
|
-
|
|
1426
|
-
|
|
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 会在输入停顿后分析必需范围、缺失信息和可选条件。执行方、请求人、群范围和有效期由下方通用授权控制。'),
|
|
1427
1451
|
h('div', { style: { display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' } },
|
|
1428
1452
|
h('button', { type: 'button', disabled: recognitionBusy || busy || !ruleDescription.trim(),
|
|
1429
|
-
onClick: () => void identifyRule(), style: secondaryButtonStyle }, recognitionBusy ? '
|
|
1430
|
-
h('span', { style: mutedTextStyle }, '
|
|
1431
|
-
recognitionMessage ? h('p', { role: 'status', 'aria-live': 'polite', style: feedbackNoticeStyle }, recognitionMessage) : null,
|
|
1432
|
-
h('section', { role: 'region', 'aria-label': '
|
|
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 } },
|
|
1433
1457
|
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
1434
|
-
h('strong', null,
|
|
1435
|
-
h('span', { style: mutedTextStyle }, ruleSummaryReady ? '
|
|
1436
|
-
h('p', { style: { ...mutedTextStyle, overflowWrap: 'anywhere' } },
|
|
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,
|
|
1437
1468
|
h('p', { style: mutedTextStyle }, ruleSummaryReady
|
|
1438
|
-
? '
|
|
1439
|
-
: '
|
|
1469
|
+
? '草稿未开放。检查任务理解、必需范围和建议;有误就改描述,AI 会重新分析。'
|
|
1470
|
+
: ruleSummaryPending ? '分析完成前不能开放任务。' : 'AI 会整理任务理解;草稿不会自动开放。'))),
|
|
1440
1471
|
h('section', { style: { ...panelStyle, padding: 10, gap: 9, display: 'grid',
|
|
1441
1472
|
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 280px), 1fr))' } },
|
|
1442
1473
|
h('strong', { style: { gridColumn: '1 / -1' } }, '通用授权'),
|
|
@@ -1445,42 +1476,31 @@ window.__ModuleLoader__.load({
|
|
|
1445
1476
|
h('label', null, '执行方', h('select', { value: runner, onChange: (event) => selectRunner(event.target.value),
|
|
1446
1477
|
style: inputStyle },
|
|
1447
1478
|
h('option', { value: 'dsh', disabled: !dshAvailable && runner !== 'dsh' }, 'DSH · 当前团队模型'),
|
|
1448
|
-
h('option', { value: 'codex', disabled:
|
|
1449
|
-
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')),
|
|
1450
1481
|
h('span', { style: mutedTextStyle }, runner === 'codex'
|
|
1451
|
-
? 'Codex
|
|
1452
|
-
: runner === 'claude-code' ? 'Claude Code
|
|
1453
|
-
: 'DSH
|
|
1454
|
-
h('details', { id: 'ant-
|
|
1455
|
-
onToggle: (event) =>
|
|
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),
|
|
1456
1487
|
style: { gridColumn: '1 / -1', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: 10 } },
|
|
1457
|
-
h('summary', { style: { cursor: 'pointer', fontWeight: 600 } },
|
|
1458
|
-
|
|
1488
|
+
h('summary', { style: { cursor: 'pointer', fontWeight: 600 } }, parallelAgentsSummary
|
|
1489
|
+
? `并行执行:${parallelAgentsSummary}` : '多 Agent 并行(可选)'),
|
|
1490
|
+
h('p', { style: mutedTextStyle }, '当前只开放 DSH。外部 Agent 的逐任务文件、Git 和网络权限无法由 ANT 核验;编辑旧任务时可取消其选择并改用 DSH。'),
|
|
1459
1491
|
h('div', { style: { display: 'flex', gap: 14, flexWrap: 'wrap' } },
|
|
1460
|
-
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'),
|
|
1461
1493
|
disabled: !dshAvailable && !reviewers.includes('dsh'),
|
|
1462
1494
|
onChange: (event) => changeReviewer('dsh', event.target.checked) }), ' DSH'),
|
|
1463
|
-
h('label', null, h('input', { type: 'checkbox', 'aria-label': '
|
|
1464
|
-
disabled:
|
|
1495
|
+
h('label', null, h('input', { type: 'checkbox', 'aria-label': '任务 Agent Codex', checked: reviewers.includes('codex'),
|
|
1496
|
+
disabled: !reviewers.includes('codex'),
|
|
1465
1497
|
onChange: (event) => changeReviewer('codex', event.target.checked) }), ' Codex'),
|
|
1466
|
-
h('label', null, h('input', { type: 'checkbox', 'aria-label': '
|
|
1467
|
-
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'),
|
|
1468
1500
|
onChange: (event) => changeReviewer('claude-code', event.target.checked) }), ' Claude Code')),
|
|
1469
|
-
|
|
1470
|
-
? h('fieldset', { style: { margin: 0, padding: '8px 10px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8 } },
|
|
1471
|
-
h('legend', { style: mutedTextStyle }, '运行策略'),
|
|
1472
|
-
h('label', { style: { display: 'flex', gap: 8, alignItems: 'start', marginBottom: 6 } },
|
|
1473
|
-
h('input', { type: 'radio', name: 'ant-cross-review-policy', checked: !crossReviewAlways,
|
|
1474
|
-
onChange: () => setCrossReviewAlways(false) }),
|
|
1475
|
-
h('span', null, `默认由 ${reviewerLabel(runner)} 执行;请求人可逐次要求交叉审查。`)),
|
|
1476
|
-
h('label', { style: { display: 'flex', gap: 8, alignItems: 'start' } },
|
|
1477
|
-
h('input', { type: 'radio', name: 'ant-cross-review-policy', checked: crossReviewAlways,
|
|
1478
|
-
onChange: () => setCrossReviewAlways(true) }),
|
|
1479
|
-
h('span', null, '每次任务都并行运行全部已选 Agent;请求人无需额外提出交叉审查。')))
|
|
1480
|
-
: reviewers.length > 1 ? h('p', { style: mutedTextStyle }, '每次并行模式需升级 ANT 服务端;当前仍可由请求人逐次提出交叉审查。') : null,
|
|
1481
|
-
!hasPrParameter ? h('p', { style: mutedTextStyle }, '从描述识别出 PR 任务后,才能使用 Codex 或 Claude Code。') : null),
|
|
1501
|
+
null),
|
|
1482
1502
|
nativePrUnavailableReason ? h('p', { role: 'status', style: { ...mutedTextStyle, gridColumn: '1 / -1' } },
|
|
1483
|
-
`${nativePrUnavailableReason}
|
|
1503
|
+
`${nativePrUnavailableReason}。当前仍可使用 DSH 团队模型处理任务。`) : null,
|
|
1484
1504
|
h('label', null, '使用场景', h('select', { value: conversationKind,
|
|
1485
1505
|
onChange: (event) => { setConversationKind(event.target.value); setGroupId('') }, style: inputStyle },
|
|
1486
1506
|
h('option', { value: 'dm' }, '私聊'), h('option', { value: 'group' }, '指定群聊'))),
|
|
@@ -1489,8 +1509,8 @@ window.__ModuleLoader__.load({
|
|
|
1489
1509
|
h('label', null, '群聊', h('select', { required: true, value: groupId,
|
|
1490
1510
|
onChange: (event) => setGroupId(event.target.value), style: inputStyle },
|
|
1491
1511
|
h('option', { value: '' }, '让群成员先 @ 机器人,再刷新列表'),
|
|
1492
|
-
...availableGroups.map((group) => h('option', { key: group.group_id, value: group.group_id
|
|
1493
|
-
|
|
1512
|
+
...availableGroups.map((group) => h('option', { key: group.group_id, value: group.group_id },
|
|
1513
|
+
groupLabel(group))))),
|
|
1494
1514
|
h('button', { type: 'button', disabled: busy,
|
|
1495
1515
|
onClick: () => void load().catch((error) => setMessage(error.message)), style: secondaryButtonStyle }, '刷新群列表')) : null,
|
|
1496
1516
|
h('label', null, '谁可以请求', h('select', { value: senderScope,
|
|
@@ -1504,9 +1524,10 @@ window.__ModuleLoader__.load({
|
|
|
1504
1524
|
`有效期:${new Date(editingExpiresAt * 1000).toLocaleString('zh-CN')}(编辑时保持不变)`)
|
|
1505
1525
|
: h('label', null, '有效期', h('select', { value: days, onChange: (event) => setDays(Number(event.target.value)), style: inputStyle },
|
|
1506
1526
|
h('option', { value: 1 }, '1 天'), h('option', { value: 7 }, '7 天'), h('option', { value: 30 }, '30 天'))),
|
|
1507
|
-
|
|
1508
|
-
'
|
|
1509
|
-
|
|
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),
|
|
1510
1531
|
h('aside', { id: 'ant-authorization-review', role: 'region', 'aria-label': '开放前复核', tabIndex: -1,
|
|
1511
1532
|
style: { ...settingsPanelStyle, padding: 14, gap: 10, alignSelf: 'stretch' } },
|
|
1512
1533
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 } },
|
|
@@ -1516,17 +1537,28 @@ window.__ModuleLoader__.load({
|
|
|
1516
1537
|
h('p', { style: mutedTextStyle }, `这项任务将在${conversationKind === 'group'
|
|
1517
1538
|
? `群“${selectedGroupSummary || '尚未选择'}”` : '私聊'}开放给${requesterSummary},由${executorSummary}执行;`
|
|
1518
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,
|
|
1519
1548
|
cardBlockReason ? h('p', { role: 'status', style: { ...mutedTextStyle,
|
|
1520
1549
|
color: 'var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary))' } }, cardBlockReason) : null,
|
|
1521
1550
|
h('button', { type: 'submit', form: 'ant-capability-create-form', disabled: busy || Boolean(cardBlockReason),
|
|
1522
1551
|
style: { ...primaryButtonStyle, width: '100%' } }, editingCardId ? '保存能力修改' : '确认并开放能力'))))),
|
|
1523
1552
|
h('div', { role: 'region', 'aria-label': '本机处理设置', style: { display: activeTab === 'local' ? 'grid' : 'none', gap: 12 } },
|
|
1524
|
-
trustedOriginCard,
|
|
1525
1553
|
showPrSecuritySettings ? h('section', { style: { ...settingsPanelStyle, padding: 14 } },
|
|
1526
1554
|
h('strong', null, 'PR 只读凭据(本机)'),
|
|
1527
1555
|
h('p', { style: mutedTextStyle }, tokenConfigured
|
|
1528
|
-
? '
|
|
1529
|
-
: '读取 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 不会把只读令牌发送到任务描述中的地址。'),
|
|
1530
1562
|
h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
|
|
1531
1563
|
h('input', { 'aria-label': 'Gitea 只读访问令牌', type: 'password', autoComplete: 'new-password', value: giteaToken,
|
|
1532
1564
|
onChange: (event) => setGiteaToken(event.target.value),
|
|
@@ -1536,7 +1568,7 @@ window.__ModuleLoader__.load({
|
|
|
1536
1568
|
onClick: () => void saveGiteaToken(), style: secondaryButtonStyle }, tokenConfigured ? '替换本机令牌' : '保存本机令牌'))) : null,
|
|
1537
1569
|
h('details', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, padding: 12 } },
|
|
1538
1570
|
h('summary', { style: { cursor: 'pointer', fontWeight: 600 } }, '本机处理设置'),
|
|
1539
|
-
h('p', { style: mutedTextStyle }, '
|
|
1571
|
+
h('p', { style: mutedTextStyle }, '本机并发上限只影响这台电脑接收的新任务。外部代码执行器已暂停,当前不创建 PR 仓库缓存;缓存上限仅保留用于旧缓存回收,暂不可调整。'),
|
|
1540
1572
|
h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' } },
|
|
1541
1573
|
h('span', { style: mutedTextStyle }, loadFailures.includes('settings') ? '团队默认值暂未读取。'
|
|
1542
1574
|
: `团队默认:先看 ${defaults?.context_messages ?? 10} 条前文、最多翻阅 ${defaults?.history_max_records ?? 100} 条平台记录、同时处理 ${defaults?.max_concurrency ?? 3} 项任务。`),
|
|
@@ -1550,26 +1582,28 @@ window.__ModuleLoader__.load({
|
|
|
1550
1582
|
h('option', { value: '' }, '跟随团队默认'),
|
|
1551
1583
|
...Array.from({ length: 8 }, (_, index) => h('option', { key: index + 1, value: index + 1 }, `${index + 1} 项任务`)))),
|
|
1552
1584
|
h('label', { style: { display: 'grid', gap: 5, flex: '0 1 200px', maxWidth: '100%' } },
|
|
1553
|
-
'这台电脑 PR 仓库缓存上限(GiB
|
|
1554
|
-
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,
|
|
1555
1588
|
value: repositoryCacheLimitGib,
|
|
1556
|
-
onChange: (event) => setRepositoryCacheLimitGib(event.target.value),
|
|
1557
1589
|
style: { ...inputStyle, width: 128, maxWidth: '100%', boxSizing: 'border-box' } })),
|
|
1558
1590
|
h('button', { disabled: busy, style: secondaryButtonStyle }, '保存本机处理设置'),
|
|
1559
1591
|
h('p', { style: { ...mutedTextStyle, flex: '1 1 100%', margin: 0 } },
|
|
1560
|
-
'
|
|
1592
|
+
'当前不拉取 PR 仓库快照;刷新授权状态时仍会清理已停用或到期能力的旧缓存。')),
|
|
1561
1593
|
),
|
|
1562
|
-
h('section', { style: { ...settingsPanelStyle, padding: 14, gap: 8 } },
|
|
1594
|
+
h('section', { 'aria-label': '执行方状态与权限边界', style: { ...settingsPanelStyle, padding: 14, gap: 8 } },
|
|
1563
1595
|
h('strong', null, '执行方状态与权限边界'),
|
|
1564
|
-
h('p', { style: mutedTextStyle }, '
|
|
1596
|
+
h('p', { style: mutedTextStyle }, '这里显示 DSH 本机的执行器状态;Codex 或 Claude Code 已注册,不代表它们获准执行协作任务。'),
|
|
1565
1597
|
loadFailures.includes('executors') ? h('p', { role: 'status', style: mutedTextStyle }, '暂时无法读取执行方状态,请刷新后重试。') : null,
|
|
1566
|
-
...executors.filter((item) => ['dsh', 'codex
|
|
1598
|
+
...executors.filter((item) => ['dsh', 'codex', 'claude-code'].includes(item.id)).map((item) => h('div', {
|
|
1567
1599
|
key: item.id, style: { display: 'grid', gap: 3, paddingTop: 8, borderTop: '1px solid var(--dsw-alias-border-l2)' } },
|
|
1568
|
-
h('strong', null, item.title + ' · ' + (item.
|
|
1569
|
-
|
|
1570
|
-
? '
|
|
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 和网络权限。'
|
|
1571
1605
|
: item.id === 'claude-code'
|
|
1572
|
-
? '
|
|
1606
|
+
? 'ANT 暂不启动 Claude Code 子 Agent,因为当前无法核验逐任务文件、Git 和网络权限。'
|
|
1573
1607
|
: '处理授权任务提供的内容,不调用本机工具。'))))))
|
|
1574
1608
|
}
|
|
1575
1609
|
|