@josephyan/qingflow-cli 0.2.0-beta.70 → 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,69 +2588,72 @@ 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 {}
2592
+ warnings: list[dict[str, Any]] = []
2593
+ verification = {
2594
+ "chart_exists": True,
2595
+ "chart_config_loaded": True,
2596
+ }
2508
2597
  try:
2509
2598
  base = self.charts.qingbi_report_get_base(profile=profile, chart_id=chart_id).get("result") or {}
2510
- config = self.charts.qingbi_report_get_config(profile=profile, chart_id=chart_id).get("result") or {}
2511
- data = self.charts.qingbi_report_get_data(
2512
- profile=profile,
2513
- chart_id=chart_id,
2514
- payload=normalized_payload,
2515
- page_num=page_num,
2516
- page_size=page_size,
2517
- page_num_y=page_num_y,
2518
- page_size_y=page_size_y,
2519
- ).get("result") or {}
2520
2599
  except (QingflowApiError, RuntimeError) as error:
2521
2600
  api_error = _coerce_api_error(error)
2522
2601
  return _failed_from_api_error(
2523
2602
  "CHART_GET_FAILED",
2524
2603
  api_error,
2525
- normalized_args={
2526
- "chart_id": chart_id,
2527
- "data_payload": normalized_payload,
2528
- "page_num": page_num,
2529
- "page_size": page_size,
2530
- "page_num_y": page_num_y,
2531
- "page_size_y": page_size_y,
2532
- },
2604
+ normalized_args={"chart_id": chart_id},
2533
2605
  details={"chart_id": chart_id},
2534
2606
  suggested_next_call={"tool_name": "chart_get", "arguments": {"profile": profile, "chart_id": chart_id}},
2535
2607
  )
2536
2608
 
2609
+ try:
2610
+ config = self.charts.qingbi_report_get_config(profile=profile, chart_id=chart_id).get("result") or {}
2611
+ except (QingflowApiError, RuntimeError) as error:
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
2622
+ warnings.append(
2623
+ _warning(
2624
+ "CHART_CONFIG_FALLBACK_FROM_DATA",
2625
+ "chart config endpoint is unavailable for this chart id; using config embedded in chart data instead",
2626
+ )
2627
+ )
2628
+ else:
2629
+ api_error = _coerce_api_error(error)
2630
+ return _failed_from_api_error(
2631
+ "CHART_GET_FAILED",
2632
+ api_error,
2633
+ normalized_args={"chart_id": chart_id},
2634
+ details={"chart_id": chart_id},
2635
+ suggested_next_call={"tool_name": "chart_get", "arguments": {"profile": profile, "chart_id": chart_id}},
2636
+ )
2637
+
2537
2638
  response = ChartGetResponse(
2538
2639
  chart_id=chart_id,
2539
2640
  base=deepcopy(base) if isinstance(base, dict) else {},
2540
2641
  config=deepcopy(config) if isinstance(config, dict) else {},
2541
- data=deepcopy(data) if isinstance(data, dict) else {"value": data},
2542
2642
  )
2543
2643
  return {
2544
2644
  "status": "success",
2545
2645
  "error_code": None,
2546
2646
  "recoverable": False,
2547
- "message": "read chart detail",
2548
- "normalized_args": {
2549
- "chart_id": chart_id,
2550
- "data_payload": normalized_payload,
2551
- "page_num": page_num,
2552
- "page_size": page_size,
2553
- "page_num_y": page_num_y,
2554
- "page_size_y": page_size_y,
2555
- },
2647
+ "message": "read chart config detail",
2648
+ "normalized_args": {"chart_id": chart_id},
2556
2649
  "missing_fields": [],
2557
2650
  "allowed_values": {},
2558
2651
  "details": {},
2559
2652
  "request_id": None,
2560
2653
  "suggested_next_call": None,
2561
2654
  "noop": False,
2562
- "warnings": [],
2563
- "verification": {"chart_exists": True, "chart_data_loaded": True},
2655
+ "warnings": warnings,
2656
+ "verification": verification,
2564
2657
  "verified": True,
2565
2658
  **response.model_dump(mode="json"),
2566
2659
  }
@@ -2664,7 +2757,7 @@ class AiBuilderFacade:
2664
2757
  },
2665
2758
  details={"unknown_selectors": missing_selectors},
2666
2759
  missing_fields=[str(item) for item in missing_selectors],
2667
- 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}},
2668
2761
  )
2669
2762
  merged = _merge_layout(
2670
2763
  current_layout={
@@ -2764,7 +2857,7 @@ class AiBuilderFacade:
2764
2857
  elif first_issue.get("kind", "").startswith("member"):
2765
2858
  suggested_call = {"tool_name": "member_search", "arguments": {"profile": profile, "query": first_issue.get("value") or ""}}
2766
2859
  elif first_issue.get("kind") in {"editable_fields", "condition_fields"}:
2767
- 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}}
2768
2861
  return _failed(
2769
2862
  first_issue.get("error_code") or "FLOW_ASSIGNEE_UNRESOLVED",
2770
2863
  "workflow contains unresolved assignees or field permissions",
@@ -3152,7 +3245,7 @@ class AiBuilderFacade:
3152
3245
  normalized_args=normalized_args,
3153
3246
  allowed_values={"field_types": [item.value for item in PublicFieldType]},
3154
3247
  details=_with_state_read_blocked_details({"app_key": target.app_key}, resource="schema", error=api_error),
3155
- 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}},
3156
3249
  ))
3157
3250
  schema_result = _empty_schema_result(target.app_name)
3158
3251
  _schema_source = "synthetic_new_app"
@@ -3177,7 +3270,7 @@ class AiBuilderFacade:
3177
3270
  f"field '{patch.name}' already exists",
3178
3271
  normalized_args=normalized_args,
3179
3272
  details={"field_name": patch.name},
3180
- 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}},
3181
3274
  )
3182
3275
  current_fields.append(field_dict)
3183
3276
  existing_index[field_dict["field_id"]] = len(current_fields) - 1
@@ -3193,7 +3286,7 @@ class AiBuilderFacade:
3193
3286
  "field selector did not match any existing field",
3194
3287
  normalized_args=normalized_args,
3195
3288
  details={"selector": patch.selector.model_dump(mode="json")},
3196
- 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}},
3197
3290
  )
3198
3291
  field = current_fields[matched]
3199
3292
  previous_name = field["name"]
@@ -3211,7 +3304,7 @@ class AiBuilderFacade:
3211
3304
  "remove selector did not match any existing field",
3212
3305
  normalized_args=normalized_args,
3213
3306
  details={"selector": patch.model_dump(mode="json")},
3214
- 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}},
3215
3308
  )
3216
3309
  field = current_fields.pop(matched)
3217
3310
  layout = _remove_field_from_layout(layout, field["name"])
@@ -3234,7 +3327,7 @@ class AiBuilderFacade:
3234
3327
  api_error,
3235
3328
  normalized_args=normalized_args,
3236
3329
  details={"app_key": target.app_key},
3237
- 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}},
3238
3331
  )
3239
3332
  except ValueError as error:
3240
3333
  return _failed(
@@ -3242,7 +3335,7 @@ class AiBuilderFacade:
3242
3335
  str(error),
3243
3336
  normalized_args=normalized_args,
3244
3337
  details={"app_key": target.app_key},
3245
- 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}},
3246
3339
  )
3247
3340
 
3248
3341
  try:
@@ -3256,7 +3349,7 @@ class AiBuilderFacade:
3256
3349
  str(error),
3257
3350
  normalized_args=normalized_args,
3258
3351
  details={"app_key": target.app_key},
3259
- 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}},
3260
3353
  )
3261
3354
 
3262
3355
  q_linker_schema_context = deepcopy(schema_result)
@@ -3272,7 +3365,7 @@ class AiBuilderFacade:
3272
3365
  str(error),
3273
3366
  normalized_args=normalized_args,
3274
3367
  details={"app_key": target.app_key},
3275
- 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}},
3276
3369
  )
3277
3370
 
3278
3371
  relation_field_count = _count_relation_fields(current_fields)
@@ -3351,7 +3444,7 @@ class AiBuilderFacade:
3351
3444
  request_id=api_error.request_id,
3352
3445
  backend_code=api_error.backend_code,
3353
3446
  http_status=None if api_error.http_status == 404 else api_error.http_status,
3354
- 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}},
3355
3448
  )
3356
3449
  return _failed_from_api_error(
3357
3450
  "SCHEMA_APPLY_FAILED",
@@ -3362,7 +3455,7 @@ class AiBuilderFacade:
3362
3455
  "app_key": target.app_key,
3363
3456
  "field_diff": {"added": added, "updated": updated, "removed": removed},
3364
3457
  },
3365
- 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}},
3366
3459
  )
3367
3460
  if _code_block_relations_need_source_rebind(compiled_question_relations) or _q_linker_relations_need_source_rebind(compiled_question_relations):
3368
3461
  try:
@@ -3396,7 +3489,7 @@ class AiBuilderFacade:
3396
3489
  str(error),
3397
3490
  normalized_args=normalized_args,
3398
3491
  details={"app_key": target.app_key},
3399
- 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}},
3400
3493
  )
3401
3494
  except (QingflowApiError, RuntimeError) as error:
3402
3495
  api_error = _coerce_api_error(error)
@@ -3409,7 +3502,7 @@ class AiBuilderFacade:
3409
3502
  "app_key": target.app_key,
3410
3503
  "field_diff": {"added": added, "updated": updated, "removed": removed},
3411
3504
  },
3412
- 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}},
3413
3506
  )
3414
3507
  rebound_payload = _build_form_payload_from_fields(
3415
3508
  title=rebound_schema.get("formTitle") or target.app_name,
@@ -3436,7 +3529,7 @@ class AiBuilderFacade:
3436
3529
  "app_key": target.app_key,
3437
3530
  "field_diff": {"added": added, "updated": updated, "removed": removed},
3438
3531
  },
3439
- 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}},
3440
3533
  )
3441
3534
  current_fields = rebound_fields
3442
3535
  response = {
@@ -3588,7 +3681,7 @@ class AiBuilderFacade:
3588
3681
  api_error,
3589
3682
  normalized_args=normalized_args,
3590
3683
  details=_with_state_read_blocked_details({"app_key": app_key}, resource="schema", error=api_error),
3591
- 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}},
3592
3685
  ))
3593
3686
  parsed = _parse_schema(schema_result)
3594
3687
  current_fields = parsed["fields"]
@@ -3601,7 +3694,7 @@ class AiBuilderFacade:
3601
3694
  normalized_args=normalized_args,
3602
3695
  details={"unknown_selectors": missing_selectors},
3603
3696
  missing_fields=[str(item) for item in missing_selectors],
3604
- 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}},
3605
3698
  )
3606
3699
  fields_by_name = {field["name"]: field for field in current_fields}
3607
3700
  seen: list[str] = []
@@ -3614,7 +3707,7 @@ class AiBuilderFacade:
3614
3707
  f"layout references unknown field '{field_name}'",
3615
3708
  normalized_args=normalized_args,
3616
3709
  details={"field_name": field_name},
3617
- 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}},
3618
3711
  )
3619
3712
  if field_name in seen:
3620
3713
  return _failed(
@@ -3755,7 +3848,7 @@ class AiBuilderFacade:
3755
3848
  "allowed_values": {"modes": ["merge", "replace"]},
3756
3849
  "details": {},
3757
3850
  "request_id": None,
3758
- "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}},
3759
3852
  "noop": False,
3760
3853
  "warnings": [],
3761
3854
  "verification": {"layout_verified": False, "layout_summary_verified": False, "layout_read_unavailable": True},
@@ -3876,7 +3969,7 @@ class AiBuilderFacade:
3876
3969
  api_error,
3877
3970
  normalized_args=normalized_args,
3878
3971
  details=_with_state_read_blocked_details({"app_key": app_key}, resource="workflow", error=api_error),
3879
- 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}},
3880
3973
  ))
3881
3974
  entity = _entity_spec_from_app(base_info=base, schema=schema, views=None)
3882
3975
  current_fields = _parse_schema(schema)["fields"]
@@ -3904,7 +3997,7 @@ class AiBuilderFacade:
3904
3997
  elif first_issue.get("kind", "").startswith("member"):
3905
3998
  suggested_call = {"tool_name": "member_search", "arguments": {"profile": profile, "query": first_issue.get("value") or ""}}
3906
3999
  elif first_issue.get("kind") in {"editable_fields", "condition_fields"}:
3907
- 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}}
3908
4001
  return _failed(
3909
4002
  first_issue.get("error_code") or "FLOW_ASSIGNEE_UNRESOLVED",
3910
4003
  "workflow contains unresolved assignees or field permissions",
@@ -4006,7 +4099,7 @@ class AiBuilderFacade:
4006
4099
  error_code = "FLOW_READBACK_PENDING"
4007
4100
  recoverable = True
4008
4101
  message = "applied workflow patch; flow readback pending"
4009
- 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}}
4010
4103
  elif workflow_verified:
4011
4104
  status = "success"
4012
4105
  error_code = None
@@ -4022,7 +4115,7 @@ class AiBuilderFacade:
4022
4115
  if workflow_structure_verified and not branch_structure_verified
4023
4116
  else "applied workflow patch; flow readback did not confirm the requested workflow"
4024
4117
  )
4025
- 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}}
4026
4119
  if not branch_structure_verified:
4027
4120
  warnings.append(_warning("WORKFLOW_BRANCH_STRUCTURE_UNVERIFIED", "branch or condition structure was written, but MCP could not fully verify downstream lane structure"))
4028
4121
  response = {
@@ -4110,7 +4203,7 @@ class AiBuilderFacade:
4110
4203
  api_error,
4111
4204
  normalized_args=normalized_args,
4112
4205
  details=_with_state_read_blocked_details({"app_key": app_key}, resource="views", error=api_error),
4113
- 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}},
4114
4207
  ))
4115
4208
  existing_views = existing_views or []
4116
4209
  existing_by_key: dict[str, dict[str, Any]] = {}
@@ -4187,7 +4280,7 @@ class AiBuilderFacade:
4187
4280
  if len(matches) > 1:
4188
4281
  return _failed(
4189
4282
  "AMBIGUOUS_VIEW",
4190
- "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",
4191
4284
  normalized_args=normalized_args,
4192
4285
  details={
4193
4286
  "app_key": app_key,
@@ -4197,7 +4290,7 @@ class AiBuilderFacade:
4197
4290
  for view in matches
4198
4291
  ],
4199
4292
  },
4200
- 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}},
4201
4294
  )
4202
4295
  if len(matches) == 1:
4203
4296
  key = _extract_view_key(matches[0])
@@ -4246,7 +4339,7 @@ class AiBuilderFacade:
4246
4339
  "ignored_system_columns": ignored_system_columns,
4247
4340
  },
4248
4341
  missing_fields=missing_columns,
4249
- 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}},
4250
4343
  )
4251
4344
  if patch.group_by and patch.group_by not in field_names:
4252
4345
  return _failed(
@@ -4259,7 +4352,7 @@ class AiBuilderFacade:
4259
4352
  "missing_fields": [patch.group_by],
4260
4353
  },
4261
4354
  missing_fields=[patch.group_by],
4262
- 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}},
4263
4356
  )
4264
4357
  for gantt_field_name in (patch.start_field, patch.end_field, patch.title_field):
4265
4358
  if gantt_field_name and gantt_field_name not in field_names:
@@ -4273,7 +4366,7 @@ class AiBuilderFacade:
4273
4366
  "missing_fields": [gantt_field_name],
4274
4367
  },
4275
4368
  missing_fields=[gantt_field_name],
4276
- 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}},
4277
4370
  )
4278
4371
  translated_filters, filter_issues = _build_view_filter_groups(current_fields_by_name=current_fields_by_name, filters=patch.filters)
4279
4372
  if filter_issues:
@@ -4289,7 +4382,7 @@ class AiBuilderFacade:
4289
4382
  },
4290
4383
  missing_fields=list(first_issue.get("missing_fields") or []),
4291
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]},
4292
- 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}},
4293
4386
  )
4294
4387
  explicit_button_dtos: list[dict[str, Any]] | None = None
4295
4388
  expected_button_summary: list[dict[str, Any]] | None = None
@@ -4328,7 +4421,7 @@ class AiBuilderFacade:
4328
4421
  f"view_key '{patch.view_key}' does not exist on this app",
4329
4422
  normalized_args=normalized_args,
4330
4423
  details={"app_key": app_key, "view_key": patch.view_key, "view_name": patch.name},
4331
- 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}},
4332
4425
  )
4333
4426
  existing_key = patch.view_key
4334
4427
  else:
@@ -4346,7 +4439,7 @@ class AiBuilderFacade:
4346
4439
  for view in name_matches
4347
4440
  ],
4348
4441
  },
4349
- 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}},
4350
4443
  )
4351
4444
  if len(name_matches) == 1:
4352
4445
  matched_existing_view = name_matches[0]
@@ -4638,7 +4731,7 @@ class AiBuilderFacade:
4638
4731
  api_error,
4639
4732
  normalized_args=normalized_args,
4640
4733
  details=_with_state_read_blocked_details({"app_key": app_key}, resource="views", error=api_error),
4641
- 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}},
4642
4735
  ))
4643
4736
  verified_names = {
4644
4737
  _extract_view_name(item)
@@ -4858,7 +4951,7 @@ class AiBuilderFacade:
4858
4951
  ),
4859
4952
  },
4860
4953
  "request_id": first_failure.get("request_id"),
4861
- "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}},
4862
4955
  "backend_code": first_failure.get("backend_code"),
4863
4956
  "http_status": first_failure.get("http_status"),
4864
4957
  "noop": noop,
@@ -4931,7 +5024,7 @@ class AiBuilderFacade:
4931
5024
  ),
4932
5025
  },
4933
5026
  "request_id": None,
4934
- "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}},
4935
5028
  "noop": noop,
4936
5029
  "warnings": warnings,
4937
5030
  "verification": {
@@ -4968,7 +5061,7 @@ class AiBuilderFacade:
4968
5061
  api_error,
4969
5062
  normalized_args=normalized_args,
4970
5063
  details={"app_key": app_key},
4971
- 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}},
4972
5065
  )
4973
5066
  tag_ids_before = _coerce_int_list(base_before.get("tagIds"))
4974
5067
  already_published = bool(base_before.get("appPublishStatus") in {1, 2})
@@ -4982,7 +5075,7 @@ class AiBuilderFacade:
4982
5075
  api_error,
4983
5076
  normalized_args=normalized_args,
4984
5077
  details={"app_key": app_key},
4985
- 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}},
4986
5079
  )
4987
5080
  views_before = views_before or []
4988
5081
  if already_published and package_already_attached is not False and isinstance(views_before, list) and not views_before_unavailable:
@@ -5019,7 +5112,7 @@ class AiBuilderFacade:
5019
5112
  api_error,
5020
5113
  normalized_args=normalized_args,
5021
5114
  details={"app_key": app_key, "edit_version_no": edit_version_no},
5022
- 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}},
5023
5116
  )
5024
5117
  try:
5025
5118
  base = self.apps.app_get_base(profile=profile, app_key=app_key, include_raw=True).get("result") or {}
@@ -5030,7 +5123,7 @@ class AiBuilderFacade:
5030
5123
  api_error,
5031
5124
  normalized_args=normalized_args,
5032
5125
  details={"app_key": app_key},
5033
- 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}},
5034
5127
  )
5035
5128
  tag_ids_after = _coerce_int_list(base.get("tagIds"))
5036
5129
  package_attached = None if not expected_package_tag_id else expected_package_tag_id in tag_ids_after
@@ -5043,7 +5136,7 @@ class AiBuilderFacade:
5043
5136
  api_error,
5044
5137
  normalized_args=normalized_args,
5045
5138
  details={"app_key": app_key},
5046
- 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}},
5047
5140
  )
5048
5141
  views = views or []
5049
5142
  views_ok = isinstance(views, list) and not views_unavailable
@@ -5706,7 +5799,7 @@ class AiBuilderFacade:
5706
5799
  api_error,
5707
5800
  normalized_args=normalized_args,
5708
5801
  details={"app_key": app_key, "phase": "prepare_edit_context"},
5709
- 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}},
5710
5803
  )
5711
5804
  edit_version_no = _coerce_positive_int(version_result.get("editVersionNo") or version_result.get("versionNo")) or 1
5712
5805
  return edit_version_no, None
@@ -5987,7 +6080,7 @@ class AiBuilderFacade:
5987
6080
  "APP_CREATE_READBACK_FAILED",
5988
6081
  api_error,
5989
6082
  details={"app_key": new_app_key, "app_name": app_name, "package_tag_id": package_tag_id},
5990
- 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}},
5991
6084
  )
5992
6085
  return {
5993
6086
  "status": "success",
@@ -6060,7 +6153,7 @@ class AiBuilderFacade:
6060
6153
  api_error,
6061
6154
  normalized_args=normalized_args,
6062
6155
  details={"app_key": app_key},
6063
- 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}},
6064
6157
  )
6065
6158
  raw_base = base_result.get("result") if isinstance(base_result.get("result"), dict) else {}
6066
6159
  effective_title = str(raw_base.get("formTitle") or fallback_title or "未命名应用").strip() or "未命名应用"
@@ -6086,7 +6179,7 @@ class AiBuilderFacade:
6086
6179
  "app base info did not include editable auth payload required for icon update",
6087
6180
  normalized_args=normalized_args,
6088
6181
  details={"app_key": app_key},
6089
- 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}},
6090
6183
  )
6091
6184
  try:
6092
6185
  update_result = self.apps.app_update_base(profile=profile, app_key=app_key, payload=payload)
@@ -6097,7 +6190,7 @@ class AiBuilderFacade:
6097
6190
  api_error,
6098
6191
  normalized_args=normalized_args,
6099
6192
  details={"app_key": app_key, "app_icon": desired_icon},
6100
- 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}},
6101
6194
  )
6102
6195
  return {
6103
6196
  "status": "success",
@@ -8075,11 +8168,15 @@ def _parse_schema(schema: dict[str, Any]) -> dict[str, Any]:
8075
8168
  section_rows.append(labels)
8076
8169
  if section_rows:
8077
8170
  parsed_section_id = _coerce_positive_int(section_question.get("queId"))
8171
+ raw_section_id = None
8078
8172
  if parsed_section_id is None:
8079
- 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"))
8080
8176
  sections.append(
8081
8177
  {
8082
- "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")),
8083
8180
  "title": section_question.get("queTitle") or "未命名分组",
8084
8181
  "rows": section_rows,
8085
8182
  }