mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 03:55:09 +08:00
fix(goal): expose JSON acceptance to conversation owners
This commit is contained in:
parent
978ec99a93
commit
dec4c509c4
@ -1,6 +1,6 @@
|
||||
# Managed JSON acceptance for Goals
|
||||
|
||||
Expand JSON acceptance requirements in the Goal panel. The conversation owner or an administrator explicitly saves up to eight requirements, each mapping an artifact slot to 1–16 top-level fields. Fields must exist and be non-null; false, zero and empty strings are allowed. This is a presence check, not a quality judgment. Opt-in is durable: requirements can be revised using their current revision, but required mode cannot be disabled. Stale revisions produce a conflict.
|
||||
Open a conversation with an existing Goal, click the Goals button in its header, and expand Managed JSON acceptance. Ordinary workspace members can manage requirements for their own conversations without access to the administrator plan board. Administrators can also use the Goal panel on that board. The conversation owner or an administrator explicitly saves up to eight requirements, each mapping an artifact slot to 1–16 top-level fields. Fields must exist and be non-null; false, zero and empty strings are allowed. This is a presence check, not a quality judgment. Opt-in is durable: requirements can be revised using their current revision, but required mode cannot be disabled. Stale revisions produce a conflict.
|
||||
|
||||
User configuration, independent managed versions, binding checks and the shared completion gate are connected. Every current requirement needs a matching valid binding before a selected goal can complete under its existing completion rules. Automatic evaluation, explicit completeGoal and retries share that gate. Unselected goals retain existing behavior.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Goal 受管 JSON 验收
|
||||
|
||||
在 Goal 面板展开“JSON 验收要求”,由对话所有者或管理员显式保存要求。每个 Goal 最多 8 条要求,每条绑定一个产物槽和 1–16 个顶层字段。字段检查表示字段存在且不为 null;false、0 和空字符串允许,不等于内容质量判断。保存后不可关闭强验收模式,可以带当前 revision 修改要求;旧修订会返回冲突。
|
||||
打开已有目标的对话,点击顶部“目标”按钮,再展开“受管 JSON 验收”。普通工作区成员可管理自己对话中的要求,无需进入管理员计划看板;管理员也可从计划看板的 Goal 面板进入。由对话所有者或管理员显式保存要求。每个 Goal 最多 8 条要求,每条绑定一个产物槽和 1–16 个顶层字段。字段检查表示字段存在且不为 null;false、0 和空字符串允许,不等于内容质量判断。保存后不可关闭强验收模式,可以带当前 revision 修改要求;旧修订会返回冲突。
|
||||
|
||||
当前已接通用户配置、独立受管版本、绑定检查及共享完成检查。选中模式后,所有当前要求必须具有匹配的有效绑定,才可在既有完成规则满足时完成;自动评估、显式 completeGoal 和重试均使用同一完成检查。未选中的 Goal 保持既有行为。
|
||||
|
||||
|
||||
@ -76,6 +76,11 @@
|
||||
<div v-else class="no-agent-hint">{{ $t('chat.selectAgent') }}</div>
|
||||
</div>
|
||||
<div class="chat-header-right">
|
||||
<button v-if="currentConversationGoal" class="header-btn" type="button"
|
||||
:title="$t('plans.goals')" :aria-label="$t('plans.goals')"
|
||||
:aria-expanded="conversationGoalOpen" @click="conversationGoalOpen = true">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>
|
||||
</button>
|
||||
<!-- Model selector — Issue #81 v2 R3: always pass full providers + show-all-states
|
||||
so unhealthy rows render as dimmed entries with status chips and a Fix
|
||||
button instead of disappearing entirely. -->
|
||||
@ -111,6 +116,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<GoalsPanel v-if="currentConversationGoal && conversationGoalOpen" :key="currentConversationGoal.id"
|
||||
:open="conversationGoalOpen" :goals="[currentConversationGoal]" :loading="false"
|
||||
@close="conversationGoalOpen = false" />
|
||||
|
||||
<TeamWorkerBanner
|
||||
v-if="workerRunContext"
|
||||
:run-id="workerRunContext.runId"
|
||||
@ -302,7 +311,7 @@ let cachedAgents: import('@/types').Agent[] = []
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus/es/components/message/index'
|
||||
import { ElMessageBox } from 'element-plus/es/components/message-box/index'
|
||||
import { ref, computed, onMounted, onBeforeUnmount, onActivated, onDeactivated, watch, nextTick } from 'vue'
|
||||
import { defineAsyncComponent, ref, computed, onMounted, onBeforeUnmount, onActivated, onDeactivated, watch, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
@ -351,6 +360,8 @@ import { buildViewerModelProviders } from '@/utils/viewerModelProviders'
|
||||
import GoalSetInlinePrompt from '@/components/goal/GoalSetInlinePrompt.vue'
|
||||
import GoalSystemLine from '@/components/goal/GoalSystemLine.vue'
|
||||
|
||||
const GoalsPanel = defineAsyncComponent(() => import('@/components/agents/GoalsPanel.vue'))
|
||||
|
||||
// ============ Talk Mode ============
|
||||
const showTalkMode = ref(false)
|
||||
|
||||
@ -1392,19 +1403,22 @@ watch([selectedAgentId, currentConversationId], () => {
|
||||
// avatar ring listens on goalStore.activeGoalByConv[cid]; without this
|
||||
// fetch the ring would only appear after an SSE event mutated the store.
|
||||
const goalStore = useGoalStore()
|
||||
const conversationGoalOpen = ref(false)
|
||||
const currentConversationGoal = computed(() => currentConversationId.value
|
||||
? goalStore.activeGoal(currentConversationId.value) : null)
|
||||
watch(() => currentConversationGoal.value?.id, () => { conversationGoalOpen.value = false })
|
||||
watch(currentConversationId, () => { conversationGoalOpen.value = false })
|
||||
const workspaceStore = useWorkspaceStore()
|
||||
const currentWorkspaceId = computed(() => workspaceStore.currentWorkspaceId ?? '1')
|
||||
const canConfigureModels = computed(() => workspaceStore.isGlobalAdmin)
|
||||
const modelSelectorEmptyHint = computed(() => canConfigureModels.value
|
||||
? undefined
|
||||
: t('chat.noModelsAvailableContactAdmin'))
|
||||
watch(currentConversationId, async (cid) => {
|
||||
// Skip un-persisted conversations: a brand-new empty chat has no goal yet
|
||||
// and the lookup would only 403 (Not the owner). The ring is hydrated by the
|
||||
// goal_created SSE event once the first turn lands.
|
||||
if (cid && !isEphemeralConversation(cid)) {
|
||||
await goalStore.loadActiveForConversation(cid)
|
||||
}
|
||||
// The route may be restored before the conversation list finishes loading.
|
||||
// Observe persistence becoming known as well as the selected conversation id.
|
||||
watch(() => currentConversationId.value && !isEphemeralConversation(currentConversationId.value)
|
||||
? currentConversationId.value : null, async (cid) => {
|
||||
if (cid) await goalStore.loadActiveForConversation(cid)
|
||||
}, { immediate: true })
|
||||
|
||||
// Re-fetch the active goal when a turn finishes. A goal can be created or
|
||||
|
||||
Loading…
Reference in New Issue
Block a user