No STIX selected. Selecting STIX automatically enables CTI RAG.
@@ -345,8 +207,8 @@
@@ -393,18 +255,16 @@ import {
faFolderOpen,
faTimes,
} from '@fortawesome/free-solid-svg-icons'
-import ChatRagPanel from './ChatRagPanel.vue'
const props = defineProps({
workflow: { type: Object, required: true },
capabilities: { type: Array, default: () => [] },
availableServers: { type: Array, default: () => [] },
globalConfig: { type: Object, required: true },
- selectedRag: { type: Array, default: () => [] },
- workflowContext: { type: Object, default: () => ({}) },
+ attachedIntel: { type: Array, default: () => [] },
collapsed: { type: Boolean, default: false },
})
-const emit = defineEmits(['toggle', 'update:selectedRag', 'update:workflowContext'])
+const emit = defineEmits(['toggle', 'update:attachedIntel'])
const $api = inject('$api', null)
// Tiny inline section component to avoid yet another file for a label+slot.
@@ -429,14 +289,6 @@ const enabledServers = computed({
props.globalConfig.serversByWorkflow[props.workflow.id] = v
},
})
-const enabledCapabilities = computed({
- get: () => props.globalConfig.capabilitiesByWorkflow?.[props.workflow.id] || [],
- set: (v) => {
- if (!props.globalConfig.capabilitiesByWorkflow) props.globalConfig.capabilitiesByWorkflow = {}
- props.globalConfig.capabilitiesByWorkflow[props.workflow.id] = v
- },
-})
-
const serverChoices = computed(() => {
const wf = props.workflow || {}
const required = new Set(wf.required_servers || [])
@@ -446,53 +298,29 @@ const serverChoices = computed(() => {
.map(s => ({ ...s, required: required.has(s.name) }))
})
-const capabilityChoices = computed(() => {
- const accepted = new Set(props.workflow?.accepted_capabilities || [])
- return (props.capabilities || []).filter(c => accepted.has(c.id))
-})
-
function toggleServer(name, checked) {
const cur = new Set(enabledServers.value)
if (checked) cur.add(name); else cur.delete(name)
enabledServers.value = [...cur]
}
-function toggleCapability(id, checked) {
- const cur = new Set(enabledCapabilities.value)
- if (checked) cur.add(id); else cur.delete(id)
- enabledCapabilities.value = [...cur]
-}
-
-// --- RAG plumbing -----------------------------------------------------------
-// We don't own RAG state; ChatRagPanel does. We just gate-mount it on the
-// workflow's capability list and forward the selectedRag v-model.
-const workflowAcceptsRag = computed(() =>
- (props.workflow?.accepted_capabilities || []).includes('rag')
+// --- Attached intel ---------------------------------------------------------
+// Both workflows declare the cti capability, so both get the picker. Attaching
+// is the only control: it is what enables the capability for the run.
+const workflowAcceptsCti = computed(() =>
+ (props.workflow?.accepted_capabilities || []).includes('cti')
)
-const selectedRagLocal = ref([...props.selectedRag])
-watch(selectedRagLocal, (v) => emit('update:selectedRag', v))
-watch(() => props.selectedRag, (v) => {
- if (JSON.stringify(v) !== JSON.stringify(selectedRagLocal.value)) {
- selectedRagLocal.value = [...v]
- }
+const attachedIntel = computed({
+ get: () => props.attachedIntel || [],
+ set: (v) => emit('update:attachedIntel', v),
})
-// --- Plan and Execute CTI context ------------------------------------------
const isPlanExecute = computed(() => props.workflow?.id === 'plan_execute')
const modelConfigOpen = ref(false)
const showStixModal = ref(false)
const stixFiles = ref([])
-const selectedPlanStix = ref([])
-function ensureRagSettings() {
- if (!props.globalConfig.capabilitySettings) props.globalConfig.capabilitySettings = {}
- if (!props.globalConfig.capabilitySettings.rag) props.globalConfig.capabilitySettings.rag = {}
- return props.globalConfig.capabilitySettings.rag
-}
-
-const planRagSettings = computed(() => ensureRagSettings())
const endpointProfileDraftName = ref(props.globalConfig.selectedEndpointProfile || '')
-const ctiRagProfileDraftName = ref('')
const endpointProfiles = computed(() => {
if (!Array.isArray(props.globalConfig.endpointProfiles)) props.globalConfig.endpointProfiles = []
return props.globalConfig.endpointProfiles
@@ -504,76 +332,30 @@ const selectedEndpointProfileName = computed({
if (value) endpointProfileDraftName.value = value
},
})
-const ctiRagProfile = computed({
- get: () => planRagSettings.value.plan_profile || '',
- set: (value) => {
- planRagSettings.value.plan_profile = value || ''
- if (value) ctiRagProfileDraftName.value = value
- },
-})
-const ctiRagModel = computed({
- get: () => planRagSettings.value.plan_embed_model || '',
- set: (value) => {
- planRagSettings.value.plan_embed_model = value
- },
-})
-const ctiRagApiBase = computed({
- get: () => planRagSettings.value.plan_api_base || '',
- set: (value) => {
- planRagSettings.value.plan_api_base = value
- },
-})
-const ctiRagApiKey = computed({
- get: () => planRagSettings.value.plan_api_key || '',
- set: (value) => {
- planRagSettings.value.plan_api_key = value
- },
-})
-const ctiRagSslVerify = computed({
- get: () => planRagSettings.value.plan_ssl_verify ?? props.globalConfig.sslVerify ?? true,
- set: (value) => {
- planRagSettings.value.plan_ssl_verify = value
- },
-})
-const ctiRagTopk = computed({
- get: () => planRagSettings.value.plan_topk ?? planRagSettings.value.topk ?? 5,
- set: (value) => {
- planRagSettings.value.plan_topk = value
- },
-})
-const ctiRagTemperature = computed({
- get: () => planRagSettings.value.plan_temperature ?? '',
- set: (value) => {
- planRagSettings.value.plan_temperature = value
- },
-})
-const ctiRagMaxToolCalls = computed({
- get: () => planRagSettings.value.plan_max_tool_calls ?? '',
- set: (value) => {
- planRagSettings.value.plan_max_tool_calls = value
- },
-})
-const ctiRagMaxTokens = computed({
- get: () => planRagSettings.value.plan_max_tokens ?? '',
- set: (value) => {
- planRagSettings.value.plan_max_tokens = value
- },
-})
const modelConfigSummary = computed(() =>
props.globalConfig?.modelName || 'server default'
)
+
const collapseIcon = computed(() => props.collapsed ? faAngleLeft : faAngleRight)
const modelToggleIcon = computed(() => modelConfigOpen.value ? faAngleDown : faAngleRight)
const folderOpenIcon = faFolderOpen
const timesIcon = faTimes
-
-const selectedStixFiles = computed(() =>
- selectedPlanStix.value.map(name =>
+const attachedIntelFiles = computed(() =>
+ attachedIntel.value.map(name =>
stixFiles.value.find(file => file.name === name) || { name }
)
)
+// What the agent will actually read this turn. Null while the listing has not
+// loaded, so the readout stays silent rather than claiming zero.
+const attachedObjectCount = computed(() => {
+ const counts = attachedIntelFiles.value.map(f => f.objects)
+ if (!counts.length || counts.some(n => typeof n !== 'number')) return null
+ const total = counts.reduce((a, b) => a + b, 0)
+ return `${total} object${total === 1 ? '' : 's'}`
+})
+
@@ -627,26 +409,6 @@ function currentGlobalEndpointProfile(name) {
}
}
-function currentCtiRagProfile(name) {
- return {
- name,
- modelName: ctiRagModel.value || props.globalConfig.modelName || '',
- temperature: ctiRagTemperature.value !== ''
- ? ctiRagTemperature.value
- : props.globalConfig.temperature,
- apiBase: ctiRagApiBase.value || props.globalConfig.apiBase || '',
- apiKey: ctiRagApiKey.value || props.globalConfig.apiKey || '',
- sslVerify: ctiRagSslVerify.value,
- maxToolCalls: ctiRagMaxToolCalls.value !== ''
- ? ctiRagMaxToolCalls.value
- : props.globalConfig.maxToolCalls,
- maxTokens: ctiRagMaxTokens.value !== ''
- ? ctiRagMaxTokens.value
- : props.globalConfig.maxTokens,
- topk: ctiRagTopk.value,
- }
-}
-
function applyProfileToGlobal(profile) {
if (!profile) return
props.globalConfig.modelName = profile.modelName || profile.model || ''
@@ -659,29 +421,11 @@ function applyProfileToGlobal(profile) {
endpointProfileDraftName.value = profile.name || ''
}
-function applyProfileToCtiRag(profile) {
- if (!profile) return
- ctiRagModel.value = profile.modelName || profile.model || ''
- ctiRagApiBase.value = profile.apiBase || profile.api_base || ''
- ctiRagApiKey.value = profile.apiKey || profile.api_key || ''
- ctiRagSslVerify.value = profile.sslVerify ?? profile.ssl_verify ?? props.globalConfig.sslVerify ?? true
- ctiRagTemperature.value = profile.temperature ?? ''
- ctiRagMaxToolCalls.value = profile.maxToolCalls ?? profile.max_tool_calls ?? ''
- ctiRagMaxTokens.value = profile.maxTokens ?? profile.max_tokens ?? ''
- if (profile.topk != null) ctiRagTopk.value = Number(profile.topk)
- ctiRagProfileDraftName.value = profile.name || ''
-}
-
function applySelectedEndpointProfile() {
const profile = endpointProfiles.value.find(p => p.name === selectedEndpointProfileName.value)
if (profile) applyProfileToGlobal(profile)
}
-function applyCtiRagProfile() {
- const profile = endpointProfiles.value.find(p => p.name === ctiRagProfile.value)
- if (profile) applyProfileToCtiRag(profile)
-}
-
function saveGlobalEndpointProfile() {
const name = trimmedProfileName(endpointProfileDraftName.value || selectedEndpointProfileName.value)
if (!name) return
@@ -689,47 +433,39 @@ function saveGlobalEndpointProfile() {
selectedEndpointProfileName.value = name
}
-function saveCtiRagProfile() {
- const name = trimmedProfileName(ctiRagProfileDraftName.value || ctiRagProfile.value)
- if (!name) return
- upsertEndpointProfile(currentCtiRagProfile(name))
- ctiRagProfile.value = name
-}
-
function deleteSelectedEndpointProfile() {
const name = selectedEndpointProfileName.value
if (!name) return
props.globalConfig.endpointProfiles = endpointProfiles.value.filter(p => p.name !== name)
selectedEndpointProfileName.value = ''
endpointProfileDraftName.value = ''
- if (ctiRagProfile.value === name) ctiRagProfile.value = ''
}
function openStixModal() {
showStixModal.value = true
- loadPlanStixFiles()
+ loadIntelFiles()
}
function closeStixModal() {
showStixModal.value = false
}
-function selectAllStix() {
- selectedPlanStix.value = stixFiles.value.map(file => file.name)
+function attachAllIntel() {
+ attachedIntel.value = stixFiles.value.map(file => file.name)
}
-function clearPlanStix() {
- selectedPlanStix.value = []
+function detachAllIntel() {
+ attachedIntel.value = []
}
-function togglePlanStix(name, checked) {
- const cur = new Set(selectedPlanStix.value)
+function toggleIntel(name, checked) {
+ const cur = new Set(attachedIntel.value)
if (checked) cur.add(name); else cur.delete(name)
- selectedPlanStix.value = [...cur]
+ attachedIntel.value = [...cur]
}
-async function loadPlanStixFiles() {
+async function loadIntelFiles() {
try {
const data = await apiGet('/plugin/mcp/stix/list')
stixFiles.value = (data.files || [])
@@ -739,7 +475,13 @@ async function loadPlanStixFiles() {
size: f.size,
model: f.model,
provider: f.provider,
+ objects: f.objects,
}))
+ const available = new Set(stixFiles.value.map(f => f.name))
+ const stillThere = attachedIntel.value.filter(name => available.has(name))
+ if (stillThere.length !== attachedIntel.value.length) {
+ attachedIntel.value = stillThere
+ }
} catch (e) {
stixFiles.value = []
}
@@ -749,40 +491,12 @@ async function loadPlanStixFiles() {
-async function loadPlanExecuteContextSources() {
- if (!isPlanExecute.value) return
- await loadPlanStixFiles()
- emitPlanContext()
+async function loadIntel() {
+ if (!workflowAcceptsCti.value) return
+ await loadIntelFiles()
}
-function emitPlanContext() {
- if (!isPlanExecute.value) {
- emit('update:workflowContext', {})
- return
- }
- emit('update:workflowContext', {
- selected_stix_files: selectedPlanStix.value,
- cti_uses_rag: selectedPlanStix.value.length > 0,
- cti_rag_model: selectedPlanStix.value.length ? ctiRagModel.value.trim() : '',
- cti_rag_api_base: selectedPlanStix.value.length ? ctiRagApiBase.value.trim() : '',
- cti_rag_api_key: selectedPlanStix.value.length ? ctiRagApiKey.value : '',
- cti_rag_ssl_verify: selectedPlanStix.value.length ? ctiRagSslVerify.value : null,
- cti_rag_topk: selectedPlanStix.value.length ? ctiRagTopk.value : null,
- cti_rag_temperature: selectedPlanStix.value.length ? ctiRagTemperature.value : null,
- cti_rag_max_tool_calls: selectedPlanStix.value.length ? ctiRagMaxToolCalls.value : null,
- cti_rag_max_tokens: selectedPlanStix.value.length ? ctiRagMaxTokens.value : null,
- })
-}
-
-onMounted(loadPlanExecuteContextSources)
-watch(isPlanExecute, loadPlanExecuteContextSources)
-watch(
- [
- selectedPlanStix, ctiRagModel,
- ],
- emitPlanContext,
- { deep: true },
-)
+onMounted(loadIntel)