@josephyan/qingflow-cli 0.2.0-beta.71 → 0.2.0-beta.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1079,7 +1079,7 @@ class AiBuilderFacade:
1079
1079
  api_error,
1080
1080
  normalized_args=normalized_args,
1081
1081
  details={"app_key": app_key},
1082
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
1082
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
1083
1083
  )
1084
1084
  edit_version_no = _coerce_positive_int(version_result.get("editVersionNo") or version_result.get("versionNo")) or 1
1085
1085
  try:
@@ -1114,7 +1114,7 @@ class AiBuilderFacade:
1114
1114
  "edit_version_no": edit_version_no,
1115
1115
  },
1116
1116
  "request_id": None,
1117
- "suggested_next_call": {"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
1117
+ "suggested_next_call": {"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
1118
1118
  "noop": False,
1119
1119
  "verification": {"released": True},
1120
1120
  "app_key": app_key,
@@ -1154,7 +1154,7 @@ class AiBuilderFacade:
1154
1154
  "permission_check_skipped": True,
1155
1155
  },
1156
1156
  "request_id": api_error.request_id,
1157
- "suggested_next_call": {"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
1157
+ "suggested_next_call": {"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
1158
1158
  "noop": False,
1159
1159
  "warnings": [
1160
1160
  _warning(
@@ -1174,7 +1174,7 @@ class AiBuilderFacade:
1174
1174
  "APP_NOT_FOUND" if api_error.http_status == 404 else "APP_RESOLVE_FAILED",
1175
1175
  api_error,
1176
1176
  details={"app_key": app_key},
1177
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
1177
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
1178
1178
  )
1179
1179
  result = base.get("result") if isinstance(base.get("result"), dict) else {}
1180
1180
  return {
@@ -1852,7 +1852,7 @@ class AiBuilderFacade:
1852
1852
  "category": api_error.category,
1853
1853
  },
1854
1854
  },
1855
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
1855
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
1856
1856
  request_id=api_error.request_id,
1857
1857
  backend_code=api_error.backend_code,
1858
1858
  http_status=None if api_error.http_status == 404 else api_error.http_status,
@@ -1890,7 +1890,7 @@ class AiBuilderFacade:
1890
1890
  "required_permission": required_permission,
1891
1891
  "permission_summary": permission_summary,
1892
1892
  },
1893
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
1893
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
1894
1894
  )
1895
1895
  )
1896
1896
 
@@ -2003,7 +2003,7 @@ class AiBuilderFacade:
2003
2003
  "current user does not have builder edit permission on this portal",
2004
2004
  normalized_args=normalized_args,
2005
2005
  details={"dash_key": dash_key, "permission_summary": permission_summary},
2006
- suggested_next_call={"tool_name": "portal_read_summary", "arguments": {"profile": profile, "dash_key": dash_key}},
2006
+ suggested_next_call={"tool_name": "portal_get", "arguments": {"profile": profile, "dash_key": dash_key}},
2007
2007
  )
2008
2008
  )
2009
2009
 
@@ -2066,6 +2066,52 @@ class AiBuilderFacade:
2066
2066
  **response.model_dump(mode="json"),
2067
2067
  }
2068
2068
 
2069
+ def app_get(self, *, profile: str, app_key: str) -> JSONObject:
2070
+ result = self.app_read_summary(profile=profile, app_key=app_key)
2071
+ if result.get("status") != "success":
2072
+ if not result.get("suggested_next_call"):
2073
+ result["suggested_next_call"] = {"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}}
2074
+ return result
2075
+ permission_summary = self._read_app_permission_summary(profile=profile, app_key=app_key)
2076
+ result["message"] = "read app config summary"
2077
+ result["editability"] = {
2078
+ "can_edit_form": permission_summary.get("can_edit_app"),
2079
+ "can_edit_flow": permission_summary.get("can_edit_app"),
2080
+ "can_edit_views": permission_summary.get("can_manage_data"),
2081
+ "can_edit_charts": permission_summary.get("can_manage_data"),
2082
+ }
2083
+ return result
2084
+
2085
+ def app_get_fields(self, *, profile: str, app_key: str) -> JSONObject:
2086
+ result = self.app_read_fields(profile=profile, app_key=app_key)
2087
+ if result.get("status") == "success":
2088
+ result["message"] = "read app field config"
2089
+ return result
2090
+
2091
+ def app_get_layout(self, *, profile: str, app_key: str) -> JSONObject:
2092
+ result = self.app_read_layout_summary(profile=profile, app_key=app_key)
2093
+ if result.get("status") == "success":
2094
+ result["message"] = "read app layout config"
2095
+ return result
2096
+
2097
+ def app_get_views(self, *, profile: str, app_key: str) -> JSONObject:
2098
+ result = self.app_read_views_summary(profile=profile, app_key=app_key)
2099
+ if result.get("status") == "success":
2100
+ result["message"] = "read app view config"
2101
+ return result
2102
+
2103
+ def app_get_flow(self, *, profile: str, app_key: str) -> JSONObject:
2104
+ result = self.app_read_flow_summary(profile=profile, app_key=app_key)
2105
+ if result.get("status") == "success":
2106
+ result["message"] = "read app flow config"
2107
+ return result
2108
+
2109
+ def app_get_charts(self, *, profile: str, app_key: str) -> JSONObject:
2110
+ result = self.app_read_charts_summary(profile=profile, app_key=app_key)
2111
+ if result.get("status") == "success":
2112
+ result["message"] = "read app chart config"
2113
+ return result
2114
+
2069
2115
  def app_read_fields(self, *, profile: str, app_key: str) -> JSONObject:
2070
2116
  try:
2071
2117
  state = self._load_base_schema_state(profile=profile, app_key=app_key)
@@ -2076,7 +2122,7 @@ class AiBuilderFacade:
2076
2122
  api_error,
2077
2123
  normalized_args={"app_key": app_key},
2078
2124
  details={"app_key": app_key},
2079
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
2125
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
2080
2126
  )
2081
2127
  parsed = state["parsed"]
2082
2128
  response = AppFieldsReadResponse(
@@ -2112,7 +2158,7 @@ class AiBuilderFacade:
2112
2158
  api_error,
2113
2159
  normalized_args={"app_key": app_key},
2114
2160
  details={"app_key": app_key},
2115
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
2161
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
2116
2162
  )
2117
2163
  parsed = state["parsed"]
2118
2164
  layout = parsed["layout"]
@@ -2157,7 +2203,7 @@ class AiBuilderFacade:
2157
2203
  api_error,
2158
2204
  normalized_args={"app_key": app_key},
2159
2205
  details={"app_key": app_key},
2160
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
2206
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
2161
2207
  )
2162
2208
  summarized_views, config_read_errors = _summarize_views_with_config(self.views, profile=profile, views=views)
2163
2209
  response = AppViewsReadResponse(
@@ -2211,7 +2257,7 @@ class AiBuilderFacade:
2211
2257
  api_error,
2212
2258
  normalized_args={"app_key": app_key},
2213
2259
  details={"app_key": app_key},
2214
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
2260
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
2215
2261
  )
2216
2262
  response = AppFlowReadResponse(
2217
2263
  app_key=app_key,
@@ -2251,7 +2297,7 @@ class AiBuilderFacade:
2251
2297
  api_error,
2252
2298
  normalized_args={"app_key": app_key},
2253
2299
  details={"app_key": app_key},
2254
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
2300
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
2255
2301
  )
2256
2302
  charts = _summarize_charts(items)
2257
2303
  response = AppChartsReadResponse(
@@ -2289,13 +2335,54 @@ class AiBuilderFacade:
2289
2335
  details={},
2290
2336
  suggested_next_call=None,
2291
2337
  )
2292
- items = _normalize_portal_list_items(raw_items)
2338
+ warnings: list[dict[str, Any]] = []
2339
+ items: list[dict[str, Any]] = []
2340
+ permission_verified = True
2341
+ for raw_item in raw_items if isinstance(raw_items, list) else []:
2342
+ if not isinstance(raw_item, dict):
2343
+ continue
2344
+ dash_key = str(raw_item.get("dashKey") or "").strip()
2345
+ if not dash_key:
2346
+ continue
2347
+ try:
2348
+ portal_result = self.portals.portal_get(profile=profile, dash_key=dash_key, being_draft=True).get("result") or {}
2349
+ except (QingflowApiError, RuntimeError):
2350
+ permission_verified = False
2351
+ warnings.append(
2352
+ _warning(
2353
+ "PORTAL_PERMISSION_READ_UNAVAILABLE",
2354
+ f"builder portal_list skipped `{dash_key}` because portal detail readback was unavailable during permission verification",
2355
+ dash_key=dash_key,
2356
+ )
2357
+ )
2358
+ continue
2359
+ permission_outcome = self._guard_portal_permission(
2360
+ profile=profile,
2361
+ dash_key=dash_key,
2362
+ normalized_args={"dash_key": dash_key},
2363
+ portal_result=portal_result if isinstance(portal_result, dict) else {},
2364
+ )
2365
+ if permission_outcome.block is not None:
2366
+ error_code = str(permission_outcome.block.get("error_code") or "")
2367
+ if error_code != "PORTAL_EDIT_UNAUTHORIZED":
2368
+ permission_verified = False
2369
+ warnings.append(
2370
+ _warning(
2371
+ "PORTAL_PERMISSION_UNVERIFIED",
2372
+ f"builder portal_list skipped `{dash_key}` because builder edit permission could not be verified",
2373
+ dash_key=dash_key,
2374
+ )
2375
+ )
2376
+ continue
2377
+ normalized = _normalize_portal_list_items([raw_item])
2378
+ if normalized:
2379
+ items.extend(normalized)
2293
2380
  response = PortalListResponse(items=items, total=len(items))
2294
2381
  return {
2295
2382
  "status": "success",
2296
2383
  "error_code": None,
2297
2384
  "recoverable": False,
2298
- "message": "list accessible portals",
2385
+ "message": "list builder-configurable portals",
2299
2386
  "normalized_args": {},
2300
2387
  "missing_fields": [],
2301
2388
  "allowed_values": {},
@@ -2303,9 +2390,12 @@ class AiBuilderFacade:
2303
2390
  "request_id": None,
2304
2391
  "suggested_next_call": None,
2305
2392
  "noop": False,
2306
- "warnings": [],
2307
- "verification": {"portal_list_loaded": True},
2308
- "verified": True,
2393
+ "warnings": warnings,
2394
+ "verification": {
2395
+ "portal_list_loaded": True,
2396
+ "portal_permissions_verified": permission_verified,
2397
+ },
2398
+ "verified": permission_verified,
2309
2399
  **response.model_dump(mode="json"),
2310
2400
  }
2311
2401
 
@@ -2380,7 +2470,7 @@ class AiBuilderFacade:
2380
2470
  api_error,
2381
2471
  normalized_args={"dash_key": dash_key, "being_draft": being_draft},
2382
2472
  details={"dash_key": dash_key, "being_draft": being_draft},
2383
- suggested_next_call={"tool_name": "portal_read_summary", "arguments": {"profile": profile, "dash_key": dash_key, "being_draft": being_draft}},
2473
+ suggested_next_call={"tool_name": "portal_get", "arguments": {"profile": profile, "dash_key": dash_key, "being_draft": being_draft}},
2384
2474
  )
2385
2475
  response = PortalReadSummaryResponse(
2386
2476
  dash_key=dash_key,
@@ -2420,17 +2510,17 @@ class AiBuilderFacade:
2420
2510
  **response.model_dump(mode="json"),
2421
2511
  }
2422
2512
 
2423
- def view_get(self, *, profile: str, viewgraph_key: str) -> JSONObject:
2513
+ def view_get(self, *, profile: str, view_key: str) -> JSONObject:
2424
2514
  try:
2425
- config = self.views.view_get_config(profile=profile, viewgraph_key=viewgraph_key).get("result") or {}
2515
+ config = self.views.view_get_config(profile=profile, viewgraph_key=view_key).get("result") or {}
2426
2516
  except (QingflowApiError, RuntimeError) as error:
2427
2517
  api_error = _coerce_api_error(error)
2428
2518
  return _failed_from_api_error(
2429
2519
  "VIEW_GET_FAILED",
2430
2520
  api_error,
2431
- normalized_args={"viewgraph_key": viewgraph_key},
2432
- details={"viewgraph_key": viewgraph_key},
2433
- suggested_next_call={"tool_name": "view_get", "arguments": {"profile": profile, "viewgraph_key": viewgraph_key}},
2521
+ normalized_args={"view_key": view_key},
2522
+ details={"view_key": view_key},
2523
+ suggested_next_call={"tool_name": "view_get", "arguments": {"profile": profile, "view_key": view_key}},
2434
2524
  )
2435
2525
 
2436
2526
  warnings: list[dict[str, Any]] = []
@@ -2443,7 +2533,7 @@ class AiBuilderFacade:
2443
2533
 
2444
2534
  base_info: dict[str, Any] = {}
2445
2535
  try:
2446
- base_info_payload = self.views.view_get_base_info(profile=profile, viewgraph_key=viewgraph_key, passcode=None).get("result") or {}
2536
+ base_info_payload = self.views.view_get_base_info(profile=profile, viewgraph_key=view_key, passcode=None).get("result") or {}
2447
2537
  if isinstance(base_info_payload, dict):
2448
2538
  base_info = deepcopy(base_info_payload)
2449
2539
  except (QingflowApiError, RuntimeError):
@@ -2452,7 +2542,7 @@ class AiBuilderFacade:
2452
2542
 
2453
2543
  questions: list[dict[str, Any]] = []
2454
2544
  try:
2455
- questions_payload = self.views.view_list_questions(profile=profile, viewgraph_key=viewgraph_key).get("result") or []
2545
+ questions_payload = self.views.view_list_questions(profile=profile, viewgraph_key=view_key).get("result") or []
2456
2546
  if isinstance(questions_payload, list):
2457
2547
  questions = [deepcopy(item) for item in questions_payload if isinstance(item, dict)]
2458
2548
  except (QingflowApiError, RuntimeError):
@@ -2461,7 +2551,7 @@ class AiBuilderFacade:
2461
2551
 
2462
2552
  associations: list[dict[str, Any]] = []
2463
2553
  try:
2464
- associations_payload = self.views.view_list_associations(profile=profile, viewgraph_key=viewgraph_key).get("result") or []
2554
+ associations_payload = self.views.view_list_associations(profile=profile, viewgraph_key=view_key).get("result") or []
2465
2555
  if isinstance(associations_payload, list):
2466
2556
  associations = [deepcopy(item) for item in associations_payload if isinstance(item, dict)]
2467
2557
  except (QingflowApiError, RuntimeError):
@@ -2469,7 +2559,7 @@ class AiBuilderFacade:
2469
2559
  warnings.append(_warning("VIEW_ASSOCIATIONS_UNAVAILABLE", "view association list readback is unavailable"))
2470
2560
 
2471
2561
  response = ViewGetResponse(
2472
- viewgraph_key=viewgraph_key,
2562
+ view_key=view_key,
2473
2563
  base_info=base_info,
2474
2564
  config=deepcopy(config) if isinstance(config, dict) else {},
2475
2565
  questions=questions,
@@ -2480,7 +2570,7 @@ class AiBuilderFacade:
2480
2570
  "error_code": None,
2481
2571
  "recoverable": False,
2482
2572
  "message": "read view detail",
2483
- "normalized_args": {"viewgraph_key": viewgraph_key},
2573
+ "normalized_args": {"view_key": view_key},
2484
2574
  "missing_fields": [],
2485
2575
  "allowed_values": {},
2486
2576
  "details": {},
@@ -2498,43 +2588,20 @@ class AiBuilderFacade:
2498
2588
  *,
2499
2589
  profile: str,
2500
2590
  chart_id: str,
2501
- data_payload: dict[str, Any] | None = None,
2502
- page_num: int | None = None,
2503
- page_size: int | None = None,
2504
- page_num_y: int | None = None,
2505
- page_size_y: int | None = None,
2506
2591
  ) -> JSONObject:
2507
- normalized_payload = deepcopy(data_payload) if isinstance(data_payload, dict) else {}
2508
2592
  warnings: list[dict[str, Any]] = []
2509
2593
  verification = {
2510
2594
  "chart_exists": True,
2511
- "chart_data_loaded": True,
2512
2595
  "chart_config_loaded": True,
2513
2596
  }
2514
2597
  try:
2515
2598
  base = self.charts.qingbi_report_get_base(profile=profile, chart_id=chart_id).get("result") or {}
2516
- data = self.charts.qingbi_report_get_data(
2517
- profile=profile,
2518
- chart_id=chart_id,
2519
- payload=normalized_payload,
2520
- page_num=page_num,
2521
- page_size=page_size,
2522
- page_num_y=page_num_y,
2523
- page_size_y=page_size_y,
2524
- ).get("result") or {}
2525
2599
  except (QingflowApiError, RuntimeError) as error:
2526
2600
  api_error = _coerce_api_error(error)
2527
2601
  return _failed_from_api_error(
2528
2602
  "CHART_GET_FAILED",
2529
2603
  api_error,
2530
- normalized_args={
2531
- "chart_id": chart_id,
2532
- "data_payload": normalized_payload,
2533
- "page_num": page_num,
2534
- "page_size": page_size,
2535
- "page_num_y": page_num_y,
2536
- "page_size_y": page_size_y,
2537
- },
2604
+ normalized_args={"chart_id": chart_id},
2538
2605
  details={"chart_id": chart_id},
2539
2606
  suggested_next_call={"tool_name": "chart_get", "arguments": {"profile": profile, "chart_id": chart_id}},
2540
2607
  )
@@ -2542,10 +2609,16 @@ class AiBuilderFacade:
2542
2609
  try:
2543
2610
  config = self.charts.qingbi_report_get_config(profile=profile, chart_id=chart_id).get("result") or {}
2544
2611
  except (QingflowApiError, RuntimeError) as error:
2545
- config_from_data = data.get("config") if isinstance(data, dict) else None
2546
- if isinstance(config_from_data, dict):
2547
- config = deepcopy(config_from_data)
2548
- verification["chart_config_loaded"] = True
2612
+ fallback_config: dict[str, Any] | None = None
2613
+ try:
2614
+ data_fallback = self.charts.qingbi_report_get_data(profile=profile, chart_id=chart_id, payload={}).get("result") or {}
2615
+ config_from_data = data_fallback.get("config") if isinstance(data_fallback, dict) else None
2616
+ if isinstance(config_from_data, dict):
2617
+ fallback_config = deepcopy(config_from_data)
2618
+ except (QingflowApiError, RuntimeError):
2619
+ fallback_config = None
2620
+ if isinstance(fallback_config, dict):
2621
+ config = fallback_config
2549
2622
  warnings.append(
2550
2623
  _warning(
2551
2624
  "CHART_CONFIG_FALLBACK_FROM_DATA",
@@ -2557,14 +2630,7 @@ class AiBuilderFacade:
2557
2630
  return _failed_from_api_error(
2558
2631
  "CHART_GET_FAILED",
2559
2632
  api_error,
2560
- normalized_args={
2561
- "chart_id": chart_id,
2562
- "data_payload": normalized_payload,
2563
- "page_num": page_num,
2564
- "page_size": page_size,
2565
- "page_num_y": page_num_y,
2566
- "page_size_y": page_size_y,
2567
- },
2633
+ normalized_args={"chart_id": chart_id},
2568
2634
  details={"chart_id": chart_id},
2569
2635
  suggested_next_call={"tool_name": "chart_get", "arguments": {"profile": profile, "chart_id": chart_id}},
2570
2636
  )
@@ -2573,21 +2639,13 @@ class AiBuilderFacade:
2573
2639
  chart_id=chart_id,
2574
2640
  base=deepcopy(base) if isinstance(base, dict) else {},
2575
2641
  config=deepcopy(config) if isinstance(config, dict) else {},
2576
- data=deepcopy(data) if isinstance(data, dict) else {"value": data},
2577
2642
  )
2578
2643
  return {
2579
2644
  "status": "success",
2580
2645
  "error_code": None,
2581
2646
  "recoverable": False,
2582
- "message": "read chart detail",
2583
- "normalized_args": {
2584
- "chart_id": chart_id,
2585
- "data_payload": normalized_payload,
2586
- "page_num": page_num,
2587
- "page_size": page_size,
2588
- "page_num_y": page_num_y,
2589
- "page_size_y": page_size_y,
2590
- },
2647
+ "message": "read chart config detail",
2648
+ "normalized_args": {"chart_id": chart_id},
2591
2649
  "missing_fields": [],
2592
2650
  "allowed_values": {},
2593
2651
  "details": {},
@@ -2699,7 +2757,7 @@ class AiBuilderFacade:
2699
2757
  },
2700
2758
  details={"unknown_selectors": missing_selectors},
2701
2759
  missing_fields=[str(item) for item in missing_selectors],
2702
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": request.app_key}},
2760
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": request.app_key}},
2703
2761
  )
2704
2762
  merged = _merge_layout(
2705
2763
  current_layout={
@@ -2799,7 +2857,7 @@ class AiBuilderFacade:
2799
2857
  elif first_issue.get("kind", "").startswith("member"):
2800
2858
  suggested_call = {"tool_name": "member_search", "arguments": {"profile": profile, "query": first_issue.get("value") or ""}}
2801
2859
  elif first_issue.get("kind") in {"editable_fields", "condition_fields"}:
2802
- suggested_call = {"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": request.app_key}}
2860
+ suggested_call = {"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": request.app_key}}
2803
2861
  return _failed(
2804
2862
  first_issue.get("error_code") or "FLOW_ASSIGNEE_UNRESOLVED",
2805
2863
  "workflow contains unresolved assignees or field permissions",
@@ -3187,7 +3245,7 @@ class AiBuilderFacade:
3187
3245
  normalized_args=normalized_args,
3188
3246
  allowed_values={"field_types": [item.value for item in PublicFieldType]},
3189
3247
  details=_with_state_read_blocked_details({"app_key": target.app_key}, resource="schema", error=api_error),
3190
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": target.app_key}},
3248
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": target.app_key}},
3191
3249
  ))
3192
3250
  schema_result = _empty_schema_result(target.app_name)
3193
3251
  _schema_source = "synthetic_new_app"
@@ -3212,7 +3270,7 @@ class AiBuilderFacade:
3212
3270
  f"field '{patch.name}' already exists",
3213
3271
  normalized_args=normalized_args,
3214
3272
  details={"field_name": patch.name},
3215
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3273
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3216
3274
  )
3217
3275
  current_fields.append(field_dict)
3218
3276
  existing_index[field_dict["field_id"]] = len(current_fields) - 1
@@ -3228,7 +3286,7 @@ class AiBuilderFacade:
3228
3286
  "field selector did not match any existing field",
3229
3287
  normalized_args=normalized_args,
3230
3288
  details={"selector": patch.selector.model_dump(mode="json")},
3231
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3289
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3232
3290
  )
3233
3291
  field = current_fields[matched]
3234
3292
  previous_name = field["name"]
@@ -3246,7 +3304,7 @@ class AiBuilderFacade:
3246
3304
  "remove selector did not match any existing field",
3247
3305
  normalized_args=normalized_args,
3248
3306
  details={"selector": patch.model_dump(mode="json")},
3249
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3307
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3250
3308
  )
3251
3309
  field = current_fields.pop(matched)
3252
3310
  layout = _remove_field_from_layout(layout, field["name"])
@@ -3269,7 +3327,7 @@ class AiBuilderFacade:
3269
3327
  api_error,
3270
3328
  normalized_args=normalized_args,
3271
3329
  details={"app_key": target.app_key},
3272
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3330
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3273
3331
  )
3274
3332
  except ValueError as error:
3275
3333
  return _failed(
@@ -3277,7 +3335,7 @@ class AiBuilderFacade:
3277
3335
  str(error),
3278
3336
  normalized_args=normalized_args,
3279
3337
  details={"app_key": target.app_key},
3280
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3338
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3281
3339
  )
3282
3340
 
3283
3341
  try:
@@ -3291,7 +3349,7 @@ class AiBuilderFacade:
3291
3349
  str(error),
3292
3350
  normalized_args=normalized_args,
3293
3351
  details={"app_key": target.app_key},
3294
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3352
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3295
3353
  )
3296
3354
 
3297
3355
  q_linker_schema_context = deepcopy(schema_result)
@@ -3307,7 +3365,7 @@ class AiBuilderFacade:
3307
3365
  str(error),
3308
3366
  normalized_args=normalized_args,
3309
3367
  details={"app_key": target.app_key},
3310
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3368
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3311
3369
  )
3312
3370
 
3313
3371
  relation_field_count = _count_relation_fields(current_fields)
@@ -3386,7 +3444,7 @@ class AiBuilderFacade:
3386
3444
  request_id=api_error.request_id,
3387
3445
  backend_code=api_error.backend_code,
3388
3446
  http_status=None if api_error.http_status == 404 else api_error.http_status,
3389
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3447
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3390
3448
  )
3391
3449
  return _failed_from_api_error(
3392
3450
  "SCHEMA_APPLY_FAILED",
@@ -3397,7 +3455,7 @@ class AiBuilderFacade:
3397
3455
  "app_key": target.app_key,
3398
3456
  "field_diff": {"added": added, "updated": updated, "removed": removed},
3399
3457
  },
3400
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3458
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3401
3459
  )
3402
3460
  if _code_block_relations_need_source_rebind(compiled_question_relations) or _q_linker_relations_need_source_rebind(compiled_question_relations):
3403
3461
  try:
@@ -3431,7 +3489,7 @@ class AiBuilderFacade:
3431
3489
  str(error),
3432
3490
  normalized_args=normalized_args,
3433
3491
  details={"app_key": target.app_key},
3434
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3492
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3435
3493
  )
3436
3494
  except (QingflowApiError, RuntimeError) as error:
3437
3495
  api_error = _coerce_api_error(error)
@@ -3444,7 +3502,7 @@ class AiBuilderFacade:
3444
3502
  "app_key": target.app_key,
3445
3503
  "field_diff": {"added": added, "updated": updated, "removed": removed},
3446
3504
  },
3447
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3505
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3448
3506
  )
3449
3507
  rebound_payload = _build_form_payload_from_fields(
3450
3508
  title=rebound_schema.get("formTitle") or target.app_name,
@@ -3471,7 +3529,7 @@ class AiBuilderFacade:
3471
3529
  "app_key": target.app_key,
3472
3530
  "field_diff": {"added": added, "updated": updated, "removed": removed},
3473
3531
  },
3474
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3532
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
3475
3533
  )
3476
3534
  current_fields = rebound_fields
3477
3535
  response = {
@@ -3623,7 +3681,7 @@ class AiBuilderFacade:
3623
3681
  api_error,
3624
3682
  normalized_args=normalized_args,
3625
3683
  details=_with_state_read_blocked_details({"app_key": app_key}, resource="schema", error=api_error),
3626
- suggested_next_call={"tool_name": "app_read_layout_summary", "arguments": {"profile": profile, "app_key": app_key}},
3684
+ suggested_next_call={"tool_name": "app_get_layout", "arguments": {"profile": profile, "app_key": app_key}},
3627
3685
  ))
3628
3686
  parsed = _parse_schema(schema_result)
3629
3687
  current_fields = parsed["fields"]
@@ -3636,7 +3694,7 @@ class AiBuilderFacade:
3636
3694
  normalized_args=normalized_args,
3637
3695
  details={"unknown_selectors": missing_selectors},
3638
3696
  missing_fields=[str(item) for item in missing_selectors],
3639
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": app_key}},
3697
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
3640
3698
  )
3641
3699
  fields_by_name = {field["name"]: field for field in current_fields}
3642
3700
  seen: list[str] = []
@@ -3649,7 +3707,7 @@ class AiBuilderFacade:
3649
3707
  f"layout references unknown field '{field_name}'",
3650
3708
  normalized_args=normalized_args,
3651
3709
  details={"field_name": field_name},
3652
- suggested_next_call={"tool_name": "app_read_layout_summary", "arguments": {"profile": profile, "app_key": app_key}},
3710
+ suggested_next_call={"tool_name": "app_get_layout", "arguments": {"profile": profile, "app_key": app_key}},
3653
3711
  )
3654
3712
  if field_name in seen:
3655
3713
  return _failed(
@@ -3790,7 +3848,7 @@ class AiBuilderFacade:
3790
3848
  "allowed_values": {"modes": ["merge", "replace"]},
3791
3849
  "details": {},
3792
3850
  "request_id": None,
3793
- "suggested_next_call": {"tool_name": "app_read_layout_summary", "arguments": {"profile": profile, "app_key": app_key}},
3851
+ "suggested_next_call": {"tool_name": "app_get_layout", "arguments": {"profile": profile, "app_key": app_key}},
3794
3852
  "noop": False,
3795
3853
  "warnings": [],
3796
3854
  "verification": {"layout_verified": False, "layout_summary_verified": False, "layout_read_unavailable": True},
@@ -3911,7 +3969,7 @@ class AiBuilderFacade:
3911
3969
  api_error,
3912
3970
  normalized_args=normalized_args,
3913
3971
  details=_with_state_read_blocked_details({"app_key": app_key}, resource="workflow", error=api_error),
3914
- suggested_next_call={"tool_name": "app_read_flow_summary", "arguments": {"profile": profile, "app_key": app_key}},
3972
+ suggested_next_call={"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}},
3915
3973
  ))
3916
3974
  entity = _entity_spec_from_app(base_info=base, schema=schema, views=None)
3917
3975
  current_fields = _parse_schema(schema)["fields"]
@@ -3939,7 +3997,7 @@ class AiBuilderFacade:
3939
3997
  elif first_issue.get("kind", "").startswith("member"):
3940
3998
  suggested_call = {"tool_name": "member_search", "arguments": {"profile": profile, "query": first_issue.get("value") or ""}}
3941
3999
  elif first_issue.get("kind") in {"editable_fields", "condition_fields"}:
3942
- suggested_call = {"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": app_key}}
4000
+ suggested_call = {"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}}
3943
4001
  return _failed(
3944
4002
  first_issue.get("error_code") or "FLOW_ASSIGNEE_UNRESOLVED",
3945
4003
  "workflow contains unresolved assignees or field permissions",
@@ -4041,7 +4099,7 @@ class AiBuilderFacade:
4041
4099
  error_code = "FLOW_READBACK_PENDING"
4042
4100
  recoverable = True
4043
4101
  message = "applied workflow patch; flow readback pending"
4044
- suggested_next_call = {"tool_name": "app_read_flow_summary", "arguments": {"profile": profile, "app_key": app_key}}
4102
+ suggested_next_call = {"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}}
4045
4103
  elif workflow_verified:
4046
4104
  status = "success"
4047
4105
  error_code = None
@@ -4057,7 +4115,7 @@ class AiBuilderFacade:
4057
4115
  if workflow_structure_verified and not branch_structure_verified
4058
4116
  else "applied workflow patch; flow readback did not confirm the requested workflow"
4059
4117
  )
4060
- suggested_next_call = {"tool_name": "app_read_flow_summary", "arguments": {"profile": profile, "app_key": app_key}}
4118
+ suggested_next_call = {"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}}
4061
4119
  if not branch_structure_verified:
4062
4120
  warnings.append(_warning("WORKFLOW_BRANCH_STRUCTURE_UNVERIFIED", "branch or condition structure was written, but MCP could not fully verify downstream lane structure"))
4063
4121
  response = {
@@ -4145,7 +4203,7 @@ class AiBuilderFacade:
4145
4203
  api_error,
4146
4204
  normalized_args=normalized_args,
4147
4205
  details=_with_state_read_blocked_details({"app_key": app_key}, resource="views", error=api_error),
4148
- suggested_next_call={"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
4206
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
4149
4207
  ))
4150
4208
  existing_views = existing_views or []
4151
4209
  existing_by_key: dict[str, dict[str, Any]] = {}
@@ -4222,7 +4280,7 @@ class AiBuilderFacade:
4222
4280
  if len(matches) > 1:
4223
4281
  return _failed(
4224
4282
  "AMBIGUOUS_VIEW",
4225
- "multiple views matched remove request; use app_read_views_summary and resolve duplicates before removing by name",
4283
+ "multiple views matched remove request; use app_get_views and resolve duplicates before removing by name",
4226
4284
  normalized_args=normalized_args,
4227
4285
  details={
4228
4286
  "app_key": app_key,
@@ -4232,7 +4290,7 @@ class AiBuilderFacade:
4232
4290
  for view in matches
4233
4291
  ],
4234
4292
  },
4235
- suggested_next_call={"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
4293
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
4236
4294
  )
4237
4295
  if len(matches) == 1:
4238
4296
  key = _extract_view_key(matches[0])
@@ -4281,7 +4339,7 @@ class AiBuilderFacade:
4281
4339
  "ignored_system_columns": ignored_system_columns,
4282
4340
  },
4283
4341
  missing_fields=missing_columns,
4284
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": app_key}},
4342
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
4285
4343
  )
4286
4344
  if patch.group_by and patch.group_by not in field_names:
4287
4345
  return _failed(
@@ -4294,7 +4352,7 @@ class AiBuilderFacade:
4294
4352
  "missing_fields": [patch.group_by],
4295
4353
  },
4296
4354
  missing_fields=[patch.group_by],
4297
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": app_key}},
4355
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
4298
4356
  )
4299
4357
  for gantt_field_name in (patch.start_field, patch.end_field, patch.title_field):
4300
4358
  if gantt_field_name and gantt_field_name not in field_names:
@@ -4308,7 +4366,7 @@ class AiBuilderFacade:
4308
4366
  "missing_fields": [gantt_field_name],
4309
4367
  },
4310
4368
  missing_fields=[gantt_field_name],
4311
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": app_key}},
4369
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
4312
4370
  )
4313
4371
  translated_filters, filter_issues = _build_view_filter_groups(current_fields_by_name=current_fields_by_name, filters=patch.filters)
4314
4372
  if filter_issues:
@@ -4324,7 +4382,7 @@ class AiBuilderFacade:
4324
4382
  },
4325
4383
  missing_fields=list(first_issue.get("missing_fields") or []),
4326
4384
  allowed_values=first_issue.get("allowed_values") or {"view_types": [member.value for member in PublicViewType], "view.filter.operator": [member.value for member in ViewFilterOperator]},
4327
- suggested_next_call={"tool_name": "app_read_fields", "arguments": {"profile": profile, "app_key": app_key}},
4385
+ suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
4328
4386
  )
4329
4387
  explicit_button_dtos: list[dict[str, Any]] | None = None
4330
4388
  expected_button_summary: list[dict[str, Any]] | None = None
@@ -4363,7 +4421,7 @@ class AiBuilderFacade:
4363
4421
  f"view_key '{patch.view_key}' does not exist on this app",
4364
4422
  normalized_args=normalized_args,
4365
4423
  details={"app_key": app_key, "view_key": patch.view_key, "view_name": patch.name},
4366
- suggested_next_call={"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
4424
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
4367
4425
  )
4368
4426
  existing_key = patch.view_key
4369
4427
  else:
@@ -4381,7 +4439,7 @@ class AiBuilderFacade:
4381
4439
  for view in name_matches
4382
4440
  ],
4383
4441
  },
4384
- suggested_next_call={"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
4442
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
4385
4443
  )
4386
4444
  if len(name_matches) == 1:
4387
4445
  matched_existing_view = name_matches[0]
@@ -4673,7 +4731,7 @@ class AiBuilderFacade:
4673
4731
  api_error,
4674
4732
  normalized_args=normalized_args,
4675
4733
  details=_with_state_read_blocked_details({"app_key": app_key}, resource="views", error=api_error),
4676
- suggested_next_call={"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
4734
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
4677
4735
  ))
4678
4736
  verified_names = {
4679
4737
  _extract_view_name(item)
@@ -4893,7 +4951,7 @@ class AiBuilderFacade:
4893
4951
  ),
4894
4952
  },
4895
4953
  "request_id": first_failure.get("request_id"),
4896
- "suggested_next_call": {"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
4954
+ "suggested_next_call": {"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
4897
4955
  "backend_code": first_failure.get("backend_code"),
4898
4956
  "http_status": first_failure.get("http_status"),
4899
4957
  "noop": noop,
@@ -4966,7 +5024,7 @@ class AiBuilderFacade:
4966
5024
  ),
4967
5025
  },
4968
5026
  "request_id": None,
4969
- "suggested_next_call": None if verified and view_filters_verified and view_buttons_verified else {"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
5027
+ "suggested_next_call": None if verified and view_filters_verified and view_buttons_verified else {"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
4970
5028
  "noop": noop,
4971
5029
  "warnings": warnings,
4972
5030
  "verification": {
@@ -5003,7 +5061,7 @@ class AiBuilderFacade:
5003
5061
  api_error,
5004
5062
  normalized_args=normalized_args,
5005
5063
  details={"app_key": app_key},
5006
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
5064
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
5007
5065
  )
5008
5066
  tag_ids_before = _coerce_int_list(base_before.get("tagIds"))
5009
5067
  already_published = bool(base_before.get("appPublishStatus") in {1, 2})
@@ -5017,7 +5075,7 @@ class AiBuilderFacade:
5017
5075
  api_error,
5018
5076
  normalized_args=normalized_args,
5019
5077
  details={"app_key": app_key},
5020
- suggested_next_call={"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
5078
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
5021
5079
  )
5022
5080
  views_before = views_before or []
5023
5081
  if already_published and package_already_attached is not False and isinstance(views_before, list) and not views_before_unavailable:
@@ -5054,7 +5112,7 @@ class AiBuilderFacade:
5054
5112
  api_error,
5055
5113
  normalized_args=normalized_args,
5056
5114
  details={"app_key": app_key, "edit_version_no": edit_version_no},
5057
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
5115
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
5058
5116
  )
5059
5117
  try:
5060
5118
  base = self.apps.app_get_base(profile=profile, app_key=app_key, include_raw=True).get("result") or {}
@@ -5065,7 +5123,7 @@ class AiBuilderFacade:
5065
5123
  api_error,
5066
5124
  normalized_args=normalized_args,
5067
5125
  details={"app_key": app_key},
5068
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
5126
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
5069
5127
  )
5070
5128
  tag_ids_after = _coerce_int_list(base.get("tagIds"))
5071
5129
  package_attached = None if not expected_package_tag_id else expected_package_tag_id in tag_ids_after
@@ -5078,7 +5136,7 @@ class AiBuilderFacade:
5078
5136
  api_error,
5079
5137
  normalized_args=normalized_args,
5080
5138
  details={"app_key": app_key},
5081
- suggested_next_call={"tool_name": "app_read_views_summary", "arguments": {"profile": profile, "app_key": app_key}},
5139
+ suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
5082
5140
  )
5083
5141
  views = views or []
5084
5142
  views_ok = isinstance(views, list) and not views_unavailable
@@ -5741,7 +5799,7 @@ class AiBuilderFacade:
5741
5799
  api_error,
5742
5800
  normalized_args=normalized_args,
5743
5801
  details={"app_key": app_key, "phase": "prepare_edit_context"},
5744
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
5802
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
5745
5803
  )
5746
5804
  edit_version_no = _coerce_positive_int(version_result.get("editVersionNo") or version_result.get("versionNo")) or 1
5747
5805
  return edit_version_no, None
@@ -6022,7 +6080,7 @@ class AiBuilderFacade:
6022
6080
  "APP_CREATE_READBACK_FAILED",
6023
6081
  api_error,
6024
6082
  details={"app_key": new_app_key, "app_name": app_name, "package_tag_id": package_tag_id},
6025
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": new_app_key}},
6083
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": new_app_key}},
6026
6084
  )
6027
6085
  return {
6028
6086
  "status": "success",
@@ -6095,7 +6153,7 @@ class AiBuilderFacade:
6095
6153
  api_error,
6096
6154
  normalized_args=normalized_args,
6097
6155
  details={"app_key": app_key},
6098
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
6156
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
6099
6157
  )
6100
6158
  raw_base = base_result.get("result") if isinstance(base_result.get("result"), dict) else {}
6101
6159
  effective_title = str(raw_base.get("formTitle") or fallback_title or "未命名应用").strip() or "未命名应用"
@@ -6121,7 +6179,7 @@ class AiBuilderFacade:
6121
6179
  "app base info did not include editable auth payload required for icon update",
6122
6180
  normalized_args=normalized_args,
6123
6181
  details={"app_key": app_key},
6124
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
6182
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
6125
6183
  )
6126
6184
  try:
6127
6185
  update_result = self.apps.app_update_base(profile=profile, app_key=app_key, payload=payload)
@@ -6132,7 +6190,7 @@ class AiBuilderFacade:
6132
6190
  api_error,
6133
6191
  normalized_args=normalized_args,
6134
6192
  details={"app_key": app_key, "app_icon": desired_icon},
6135
- suggested_next_call={"tool_name": "app_read_summary", "arguments": {"profile": profile, "app_key": app_key}},
6193
+ suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
6136
6194
  )
6137
6195
  return {
6138
6196
  "status": "success",
@@ -8110,11 +8168,15 @@ def _parse_schema(schema: dict[str, Any]) -> dict[str, Any]:
8110
8168
  section_rows.append(labels)
8111
8169
  if section_rows:
8112
8170
  parsed_section_id = _coerce_positive_int(section_question.get("queId"))
8171
+ raw_section_id = None
8113
8172
  if parsed_section_id is None:
8114
- parsed_section_id = _coerce_positive_int(section_question.get("sectionId"))
8173
+ raw_section_id = str(section_question.get("sectionId") or "").strip() or None
8174
+ if raw_section_id is None:
8175
+ parsed_section_id = _coerce_positive_int(section_question.get("sectionId"))
8115
8176
  sections.append(
8116
8177
  {
8117
- "section_id": str(parsed_section_id or _slugify(section_question.get("queTitle") or "section", default="section")),
8178
+ "section_id": raw_section_id
8179
+ or str(parsed_section_id or _slugify(section_question.get("queTitle") or "section", default="section")),
8118
8180
  "title": section_question.get("queTitle") or "未命名分组",
8119
8181
  "rows": section_rows,
8120
8182
  }