Fix bridge history, profile models, and Windows gateway handling (#845)

* feat: support profile-aware group chat bridge flows

* feat: route cron jobs through hermes cli

* Fix group chat routing and isolate bridge tests

* Add Grok image-to-video media skill

* Default Grok videos to media directory

* Fix bridge profile fallback and cron repeat clearing

* Refine bridge chat and gateway platform handling

* Filter bridge tool-call text deltas

* Preserve structured bridge chat history

* Prepare beta release build artifacts

* Fix Windows run profile resolution

* Fix Windows path compatibility checks

* Fix profile-scoped model page display

* Hide Windows subprocess windows for jobs and updates

* Hide Windows file backend subprocess windows

* Avoid Windows gateway restart lock conflicts

* Treat Windows gateway lock as running on startup

* Force release Windows gateway lock on restart

* Tighten Windows gateway lock cleanup

* Update chat e2e source expectation

* Bump package version to 0.5.30

---------

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
ekko
2026-05-19 16:09:59 +08:00
committed by GitHub
co-authored by Codex
parent 3d74d78698
commit 9a9416c99c
129 changed files with 7016 additions and 1837 deletions
@@ -244,15 +244,6 @@ function openChangelog() {
</svg>
</div>
<div v-show="!isGroupCollapsed('system')" class="nav-group-items">
<button class="nav-item" :class="{ active: selectedKey === 'hermes.gateways' }" @click="handleNav('hermes.gateways')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="2" width="20" height="8" rx="2" ry="2" />
<rect x="2" y="14" width="20" height="8" rx="2" ry="2" />
<line x1="6" y1="6" x2="6.01" y2="6" />
<line x1="6" y1="18" x2="6.01" y2="18" />
</svg>
<span>{{ t("sidebar.gateways") }}</span>
</button>
<button class="nav-item" :class="{ active: selectedKey === 'hermes.profiles' }" @click="handleNav('hermes.profiles')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
@@ -2,10 +2,12 @@
import { ref, computed } from 'vue'
import { NModal, NInput, NSelect } from 'naive-ui'
import { useAppStore } from '@/stores/hermes/app'
import { useProfilesStore } from '@/stores/hermes/profiles'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const appStore = useAppStore()
const profilesStore = useProfilesStore()
const showModal = ref(false)
const searchQuery = ref('')
@@ -14,15 +16,20 @@ const customInput = ref('')
const customProvider = ref('')
const selectedDisplayName = computed(() => appStore.displayModelName(appStore.selectedModel, appStore.selectedProvider))
const activeProfileName = computed(() => profilesStore.activeProfileName || 'default')
const activeModelGroups = computed(() => {
const profileModels = appStore.profileModelGroups.find(entry => entry.profile === activeProfileName.value)
return profileModels?.groups?.length ? profileModels.groups : appStore.modelGroups
})
const providerOptions = computed(() => {
const current = appStore.selectedProvider
customProvider.value = current
return appStore.modelGroups.map(g => ({ label: g.label, value: g.provider }))
return activeModelGroups.value.map(g => ({ label: g.label, value: g.provider }))
})
const modelGroupsWithCustom = computed(() =>
appStore.modelGroups.map(g => ({
activeModelGroups.value.map(g => ({
...g,
models: [
...g.models,
@@ -66,7 +73,7 @@ function isGroupCollapsed(provider: string) {
}
function handleSelect(model: string, provider: string) {
const meta = appStore.modelGroups.find(g => g.provider === provider)?.model_meta?.[model]
const meta = activeModelGroups.value.find(g => g.provider === provider)?.model_meta?.[model]
if (meta?.disabled) return
appStore.switchModel(model, provider)
showModal.value = false
@@ -85,7 +92,7 @@ function handleCustomSubmit() {
const model = customInput.value.trim()
if (!model || !customProvider.value) return
// 拦截 disabled 模型,避免 custom input 绕过列表里的灰显限制
const meta = appStore.modelGroups.find(g => g.provider === customProvider.value)?.model_meta?.[model]
const meta = activeModelGroups.value.find(g => g.provider === customProvider.value)?.model_meta?.[model]
if (meta?.disabled) return
appStore.switchModel(model, customProvider.value)
showModal.value = false