Merge pull request #2807 from divakantpandey/fix/2787-pi-extension-string-enum

fix(cli): emit string enums as Type.String({ enum }) in Pi adapter (#2787)
This commit is contained in:
Martin Vogel authored and GitHub committed 2026-10-28 18:10:35 +01:00
commit dca2a44ef0
2 files changed
+41 -21

No files matched your search

+19 -21
View File
@@ -148,38 +148,36 @@ static void append_typebox_type(adapter_sb_t *sb, yyjson_val *val, const char *d
if (type_str && strcmp(type_str, "string") == 0 && enum_val && yyjson_is_arr(enum_val) &&
yyjson_arr_size(enum_val) > 0) {
sb_append(sb, "Type.Union([");
size_t enum_idx = 0;
size_t enum_max = yyjson_arr_size(enum_val);
bool first_enum = true;
size_t valid_count = 0;
for (enum_idx = 0; enum_idx < enum_max; enum_idx++) {
yyjson_val *enum_item = yyjson_arr_get(enum_val, enum_idx);
const char *enum_str = yyjson_get_str(enum_item);
if (!enum_str) {
continue;
if (yyjson_get_str(yyjson_arr_get(enum_val, enum_idx))) {
valid_count++;
}
if (!first_enum) {
sb_append(sb, ", ");
}
first_enum = false;
sb_append(sb, "Type.Literal(");
sb_append_js_string(sb, enum_str);
sb_append(sb, ")");
}
if (first_enum) {
sb_append(sb, "Type.String()");
} else {
if (valid_count > 0) {
sb_append(sb, "Type.String({ enum: [");
bool first_enum = true;
for (enum_idx = 0; enum_idx < enum_max; enum_idx++) {
const char *enum_str = yyjson_get_str(yyjson_arr_get(enum_val, enum_idx));
if (!enum_str) {
continue;
}
if (!first_enum) {
sb_append(sb, ", ");
}
first_enum = false;
sb_append_js_string(sb, enum_str);
}
sb_append(sb, "]");
if (include_description && description) {
sb_append(sb, ", { description: ");
sb_append(sb, ", description: ");
sb_append_js_string(sb, description);
sb_append(sb, " }");
}
sb_append(sb, ")");
sb_append(sb, " })");
return;
}
sb_append(sb, ")");
return;
}
const char *typebox_name = "Any";
+22
View File
@@ -1177,6 +1177,10 @@ TEST(client_adapter_pi_emits_parameters_and_execute) {
/* TypeBox parameters schema is embedded with Type.Object syntax. */
ASSERT_NOT_NULL(strstr(js, "Type.Object("));
ASSERT_NOT_NULL(strstr(js, "Type.String("));
/* #2787: String enums must use Type.String({ enum: [...] }) without anyOf/const */
ASSERT_NOT_NULL(strstr(js, "Type.String({ enum: ["));
ASSERT_NULL(strstr(js, "Type.Union([Type.Literal"));
ASSERT_NULL(strstr(js, "Type.Literal("));
/* A numeric registry property must remain numeric in the generated schema. */
ASSERT_NOT_NULL(strstr(js, "depth: Type.Optional(Type.Integer("));
/* TypeBox import should be present. */
@@ -1244,6 +1248,23 @@ TEST(client_adapter_pi_wraps_result_and_honors_abort) {
PASS();
}
/* #2787: Pi extension must emit string enums as Type.String({ enum: [...] })
* rather than Type.Union([Type.Literal(...)]) which TypeBox serializes as
* anyOf/const, breaking Gemini / Antigravity function declaration validation. */
TEST(client_adapter_pi_emits_string_enum_issue2787) {
char *js = cbm_client_adapter_pi("/usr/local/bin/codebase-memory-mcp");
ASSERT_NOT_NULL(js);
ASSERT_NULL(strstr(js, "Type.Union([Type.Literal"));
ASSERT_NULL(strstr(js, "Type.Literal("));
ASSERT_NOT_NULL(strstr(js, "Type.String({ enum: ["));
ASSERT_NOT_NULL(strstr(js, "'full'"));
ASSERT_NOT_NULL(strstr(js, "'moderate'"));
ASSERT_NOT_NULL(strstr(js, "'fast'"));
ASSERT_NOT_NULL(strstr(js, "'cross-repo-intelligence'"));
free(js);
PASS();
}
/* #616 rejected `"` in its path template but not `\`, so a Windows home like
* C:\Users\urs\bin produced `\u` — an invalid unicode escape — and the whole
* auto-loaded plugin failed to parse. A broken plugin in an auto-load directory
@@ -1405,6 +1426,7 @@ SUITE(agent_clients) {
RUN_TEST(client_adapter_pi_emits_parameters_and_execute);
RUN_TEST(client_adapter_pi_sends_arguments_over_stdin_issue1834);
RUN_TEST(client_adapter_pi_wraps_result_and_honors_abort);
RUN_TEST(client_adapter_pi_emits_string_enum_issue2787);
RUN_TEST(client_adapter_escapes_windows_paths_and_quotes);
RUN_TEST(client_adapter_opencode_sends_the_required_hook_event);
RUN_TEST(client_adapter_opencode_covers_lifecycle_read_and_compaction);