From ef51690b5a71501a2ce76296d6979f4ea09f6779 Mon Sep 17 00:00:00 2001 From: Achord Chan Date: Sun, 20 Sep 2026 10:46:09 +0800 Subject: [PATCH 01/38] fix(antigravity): preserve string const constraints in tool schemas --- .../pkg/antigravity/schema_cleaner.go | 21 +++++++++ .../pkg/antigravity/schema_const_test.go | 44 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 backend/internal/pkg/antigravity/schema_const_test.go diff --git a/backend/internal/pkg/antigravity/schema_cleaner.go b/backend/internal/pkg/antigravity/schema_cleaner.go index 0ee746aa3781..3cbd0fcae669 100644 --- a/backend/internal/pkg/antigravity/schema_cleaner.go +++ b/backend/internal/pkg/antigravity/schema_cleaner.go @@ -119,6 +119,27 @@ func cleanJSONSchemaRecursive(value any) any { // 0. [NEW] 合并 allOf mergeAllOf(schemaMap) + // Gemini's enum representation supports strings. Preserve string constants + // before the allowlist removes const, including schemas with no explicit type. + if constant, ok := schemaMap["const"].(string); ok { + values := []any{constant} + if existing, ok := schemaMap["enum"].([]any); ok { + // Both constraints apply: retain their intersection, not a wider enum. + values = []any{} + for _, value := range existing { + if text, ok := value.(string); ok && text == constant { + values = append(values, constant) + break + } + } + } + schemaMap["enum"] = values + if _, exists := schemaMap["type"]; !exists { + schemaMap["type"] = "string" + } + delete(schemaMap, "const") + } + // 1. [CRITICAL] 深度递归处理子项 if props, ok := schemaMap["properties"].(map[string]any); ok { for _, v := range props { diff --git a/backend/internal/pkg/antigravity/schema_const_test.go b/backend/internal/pkg/antigravity/schema_const_test.go new file mode 100644 index 000000000000..ac83a4026c2e --- /dev/null +++ b/backend/internal/pkg/antigravity/schema_const_test.go @@ -0,0 +1,44 @@ +package antigravity + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestBuildToolsPreservesStringConst(t *testing.T) { + for _, tc := range []struct { + name string + schema string + want string + }{ + {"typed", `{"type":"string","const":"browser"}`, `{"type":"string","enum":["browser"]}`}, + {"inferred", `{"const":"browser"}`, `{"type":"string","enum":["browser"]}`}, + {"empty", `{"type":"string","const":""}`, `{"type":"string","enum":[""]}`}, + {"existing enum", `{"type":"string","const":"browser","enum":["browser","shell"]}`, `{"type":"string","enum":["browser"]}`}, + {"conflicting enum", `{"type":"string","const":"browser","enum":["shell"]}`, `{"type":"string","enum":[]}`}, + {"ordinary enum", `{"type":"string","enum":["browser","shell"]}`, `{"type":"string","enum":["browser","shell"]}`}, + {"array items", `{"type":"array","items":{"type":"string","const":"browser"}}`, `{"type":"array","items":{"type":"string","enum":["browser"]}}`}, + {"nested property named const", `{"type":"object","properties":{"const":{"const":"browser"}}}`, `{"type":"object","properties":{"const":{"type":"string","enum":["browser"]}}}`}, + {"schema metadata", `{"type":"string","const":"browser","$schema":"https://json-schema.org/draft/2020-12/schema","description":"Tool kind"}`, `{"type":"string","enum":["browser"],"description":"Tool kind"}`}, + } { + t.Run(tc.name, func(t *testing.T) { + var property map[string]any + require.NoError(t, json.Unmarshal([]byte(tc.schema), &property)) + tools := buildTools([]ClaudeTool{{ + Name: "dispatch", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{"action": property}, + }, + }}) + require.Len(t, tools, 1) + require.Len(t, tools[0].FunctionDeclarations, 1) + properties := tools[0].FunctionDeclarations[0].Parameters["properties"].(map[string]any) + got, err := json.Marshal(properties["action"]) + require.NoError(t, err) + require.JSONEq(t, tc.want, string(got)) + }) + } +} From f867e3e2ece67781d23cff532c64fcd8bbc90c73 Mon Sep 17 00:00:00 2001 From: Achord Chan Date: Tue, 22 Sep 2026 18:34:36 +0800 Subject: [PATCH 02/38] test(antigravity): check schema properties type assertion --- backend/internal/pkg/antigravity/schema_const_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/internal/pkg/antigravity/schema_const_test.go b/backend/internal/pkg/antigravity/schema_const_test.go index ac83a4026c2e..05551fbd8338 100644 --- a/backend/internal/pkg/antigravity/schema_const_test.go +++ b/backend/internal/pkg/antigravity/schema_const_test.go @@ -35,7 +35,8 @@ func TestBuildToolsPreservesStringConst(t *testing.T) { }}) require.Len(t, tools, 1) require.Len(t, tools[0].FunctionDeclarations, 1) - properties := tools[0].FunctionDeclarations[0].Parameters["properties"].(map[string]any) + properties, ok := tools[0].FunctionDeclarations[0].Parameters["properties"].(map[string]any) + require.True(t, ok, "tool parameters must contain a properties object") got, err := json.Marshal(properties["action"]) require.NoError(t, err) require.JSONEq(t, tc.want, string(got)) From 1af3269adb1bceea4e8d362ed5e55438f081ece8 Mon Sep 17 00:00:00 2001 From: "chihao.ou" Date: Tue, 22 Sep 2026 21:43:13 +0800 Subject: [PATCH 03/38] fix(groups): clean up modal listeners and pending searches --- .../admin/group/GroupRPMOverridesModal.vue | 6 +++- .../admin/group/GroupRateMultipliersModal.vue | 6 +++- .../__tests__/GroupModal.cleanup.spec.ts | 30 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/admin/group/__tests__/GroupModal.cleanup.spec.ts diff --git a/frontend/src/components/admin/group/GroupRPMOverridesModal.vue b/frontend/src/components/admin/group/GroupRPMOverridesModal.vue index 025054162fdf..43987801439f 100644 --- a/frontend/src/components/admin/group/GroupRPMOverridesModal.vue +++ b/frontend/src/components/admin/group/GroupRPMOverridesModal.vue @@ -206,7 +206,7 @@