diff --git a/mateclaw-server/src/main/java/vip/mate/tool/builtin/GoalManagementTool.java b/mateclaw-server/src/main/java/vip/mate/tool/builtin/GoalManagementTool.java index e9e93e40..6a0fcdd0 100644 --- a/mateclaw-server/src/main/java/vip/mate/tool/builtin/GoalManagementTool.java +++ b/mateclaw-server/src/main/java/vip/mate/tool/builtin/GoalManagementTool.java @@ -222,6 +222,7 @@ public class GoalManagementTool { out.put("evalLlmCallsUsed", goal.getEvalLlmCallsUsed()); out.put("totalLlmCallsUsed", goal.totalLlmCallsUsed()); out.put("llmCallBudget", goal.getLlmCallBudget()); + out.put("jsonAcceptanceRequired", goal.isJsonAcceptanceRequired()); out.put("completionScore", goal.getCompletionScore()); out.put("progressSummary", goal.getProgressSummary()); out.put("autoFollowupEnabled", goal.getAutoFollowupEnabled()); diff --git a/mateclaw-server/src/test/java/vip/mate/tool/builtin/GoalManagementToolTest.java b/mateclaw-server/src/test/java/vip/mate/tool/builtin/GoalManagementToolTest.java index 95aa98a6..15f381a2 100644 --- a/mateclaw-server/src/test/java/vip/mate/tool/builtin/GoalManagementToolTest.java +++ b/mateclaw-server/src/test/java/vip/mate/tool/builtin/GoalManagementToolTest.java @@ -195,10 +195,12 @@ class GoalManagementToolTest { @Test void getGoalStatus_active_carriesProgressSummary() { GoalEntity g = goal(GoalStatus.ACTIVE); + g.setJsonAcceptanceRequired(true); g.setProgressSummary("missing DNS"); g.setCompletionScore(0.62); when(goalService.findActiveByConversation("conv-1")).thenReturn(g); String result = tool.getGoalStatus(ctxWith("conv-1", 10L, "alice")); + assertTrue(result.contains("\"jsonAcceptanceRequired\":true")); assertTrue(result.contains("\"goalId\":\"123\"")); assertTrue(result.contains("\"completionScore\":0.62")); assertTrue(result.contains("missing DNS")); diff --git a/mateclaw-ui/src/api/__tests__/goalJsonAcceptance.test.ts b/mateclaw-ui/src/api/__tests__/goalJsonAcceptance.test.ts new file mode 100644 index 00000000..9693ec65 --- /dev/null +++ b/mateclaw-ui/src/api/__tests__/goalJsonAcceptance.test.ts @@ -0,0 +1,13 @@ +import { afterEach, expect, it, vi } from 'vitest' +import { http } from '@/api/index' +import { goalJsonAcceptanceApi } from '@/api/goalJsonAcceptance' +afterEach(() => vi.restoreAllMocks()) +it('keeps goal IDs and optimistic revisions as exact strings', () => { + const get = vi.spyOn(http, 'get').mockResolvedValue({} as never) + const put = vi.spyOn(http, 'put').mockResolvedValue({} as never) + const goal = '9223372036854775801' + const data = { expectedRevision: '9223372036854775802', artifactSlot: 'report', requiredFields: ['summary'] } + goalJsonAcceptanceApi.get(goal); goalJsonAcceptanceApi.configure(goal, 'report-fields', data) + expect(get).toHaveBeenCalledWith(`/goals/${goal}/json-acceptance`) + expect(put).toHaveBeenCalledWith(`/goals/${goal}/json-acceptance/requirements/report-fields`, data) +}) diff --git a/mateclaw-ui/src/api/goalJsonAcceptance.ts b/mateclaw-ui/src/api/goalJsonAcceptance.ts new file mode 100644 index 00000000..fed6d6f1 --- /dev/null +++ b/mateclaw-ui/src/api/goalJsonAcceptance.ts @@ -0,0 +1,16 @@ +import { http } from './index' + +export interface GoalJsonRequirement { + criterionKey: string + artifactSlot: string + revision: string + requiredFields: string[] + configuredBy: string +} +export interface GoalJsonAcceptanceView { required: boolean; requirements: GoalJsonRequirement[] } +export interface ConfigureJsonRequirement { expectedRevision: string; artifactSlot: string; requiredFields: string[] } +export const goalJsonAcceptanceApi = { + get: (goalId: string) => http.get(`/goals/${encodeURIComponent(goalId)}/json-acceptance`), + configure: (goalId: string, key: string, data: ConfigureJsonRequirement) => + http.put(`/goals/${encodeURIComponent(goalId)}/json-acceptance/requirements/${encodeURIComponent(key)}`, data), +} diff --git a/mateclaw-ui/src/api/index.ts b/mateclaw-ui/src/api/index.ts index 770a3dec..72492677 100644 --- a/mateclaw-ui/src/api/index.ts +++ b/mateclaw-ui/src/api/index.ts @@ -1786,6 +1786,7 @@ export interface GoalCriterion { } export interface Goal { + jsonAcceptanceRequired?: boolean id: string conversationId: string agentId: string diff --git a/mateclaw-ui/src/components/agents/GoalsPanel.vue b/mateclaw-ui/src/components/agents/GoalsPanel.vue index 21ea8b7d..e292c711 100644 --- a/mateclaw-ui/src/components/agents/GoalsPanel.vue +++ b/mateclaw-ui/src/components/agents/GoalsPanel.vue @@ -42,6 +42,7 @@

{{ goal.progressSummary }}

+ @@ -56,6 +57,7 @@ import { watch, onMounted, onBeforeUnmount } from 'vue' import { useI18n } from 'vue-i18n' import type { Goal } from '@/api' import ExecutionEvidenceList from '@/components/execution/ExecutionEvidenceList.vue' +import GoalJsonAcceptancePanel from '@/components/goal/GoalJsonAcceptancePanel.vue' const props = defineProps<{ open: boolean diff --git a/mateclaw-ui/src/components/goal/GoalJsonAcceptancePanel.vue b/mateclaw-ui/src/components/goal/GoalJsonAcceptancePanel.vue new file mode 100644 index 00000000..3ea7c69c --- /dev/null +++ b/mateclaw-ui/src/components/goal/GoalJsonAcceptancePanel.vue @@ -0,0 +1,144 @@ + + +