@qingflow-tech/qingflow-app-user-mcp 1.0.43 → 1.1.0

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.
@@ -27,10 +27,6 @@ from ..builder_facade.models import (
27
27
  FieldPatch,
28
28
  FieldRemovePatch,
29
29
  FieldUpdatePatch,
30
- FlowPreset,
31
- FlowNodePatch,
32
- FlowPlanRequest,
33
- FlowTransitionPatch,
34
30
  LayoutApplyMode,
35
31
  LayoutPlanRequest,
36
32
  LayoutPreset,
@@ -70,7 +66,6 @@ from .qingbi_report_tools import QingbiReportTools
70
66
  from .role_tools import RoleTools
71
67
  from .solution_tools import SolutionTools
72
68
  from .view_tools import ViewTools
73
- from .workflow_tools import WorkflowTools
74
69
 
75
70
 
76
71
  def _normalize_builder_view_key(value: str) -> str:
@@ -80,7 +75,6 @@ def _normalize_builder_view_key(value: str) -> str:
80
75
  return raw
81
76
 
82
77
 
83
- PUBLIC_STABLE_FLOW_NODE_TYPES = ["start", "approve", "fill", "copy", "webhook", "end"]
84
78
  BUILDER_APPLY_SCHEMA_VERSION = "builder.apply.v1"
85
79
  BUILDER_APPLY_TOOL_NAMES = {
86
80
  "package_apply",
@@ -115,7 +109,6 @@ class AiBuilderTools(ToolBase):
115
109
  buttons=CustomButtonTools(sessions, backend),
116
110
  packages=PackageTools(sessions, backend),
117
111
  views=ViewTools(sessions, backend),
118
- workflows=WorkflowTools(sessions, backend),
119
112
  portals=PortalTools(sessions, backend),
120
113
  charts=QingbiReportTools(sessions, backend),
121
114
  roles=RoleTools(sessions, backend),
@@ -455,22 +448,34 @@ class AiBuilderTools(ToolBase):
455
448
  ) -> JSONObject:
456
449
  return self.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or [])
457
450
 
451
+ @mcp.tool()
452
+ def app_flow_get(
453
+ profile: str = DEFAULT_PROFILE,
454
+ app_key: str = "",
455
+ version_id: str = "",
456
+ ) -> JSONObject:
457
+ return self.app_get_flow(profile=profile, app_key=app_key, version_id=version_id or None)
458
+
459
+ @mcp.tool()
460
+ def app_flow_get_schema(profile: str = DEFAULT_PROFILE, schema_version: str = "") -> JSONObject:
461
+ return self.app_flow_get_schema(profile=profile, schema_version=schema_version or None)
462
+
458
463
  @mcp.tool()
459
464
  def app_flow_apply(
460
465
  profile: str = DEFAULT_PROFILE,
461
466
  app_key: str = "",
462
- mode: str = "replace",
463
467
  publish: bool = True,
464
- nodes: list[JSONObject] | None = None,
465
- transitions: list[JSONObject] | None = None,
468
+ spec: JSONObject | None = None,
469
+ idempotency_key: str = "",
470
+ schema_version: str = "",
466
471
  ) -> JSONObject:
467
472
  return self.app_flow_apply(
468
473
  profile=profile,
469
474
  app_key=app_key,
470
- mode=mode,
471
475
  publish=publish,
472
- nodes=nodes or [],
473
- transitions=transitions or [],
476
+ spec=spec or {},
477
+ idempotency_key=idempotency_key or None,
478
+ schema_version=schema_version or None,
474
479
  )
475
480
 
476
481
  @mcp.tool()
@@ -631,15 +636,16 @@ class AiBuilderTools(ToolBase):
631
636
  "allowed_values": {"tool_name": public_tool_names},
632
637
  "details": {"reason_path": "tool_name"},
633
638
  "suggested_next_call": None,
634
- "request_id": None,
635
- "backend_code": None,
636
- "http_status": None,
637
- "noop": False,
638
- "warnings": [],
639
- "verification": {},
640
- "verified": False,
641
- }
639
+ "request_id": None,
640
+ "backend_code": None,
641
+ "http_status": None,
642
+ "noop": False,
643
+ "warnings": [],
644
+ "verification": {},
645
+ "verified": False,
646
+ }
642
647
  contract = _builder_contract_with_apply_output(lookup_name, contract)
648
+ contract_summary = _builder_tool_contract_summary(lookup_name, contract)
643
649
  return {
644
650
  "status": "success",
645
651
  "error_code": None,
@@ -658,6 +664,8 @@ class AiBuilderTools(ToolBase):
658
664
  "verification": {},
659
665
  "verified": True,
660
666
  "tool_name": requested,
667
+ "summary": contract_summary,
668
+ "json_paths": contract_summary["json_paths"],
661
669
  "contract": contract,
662
670
  }
663
671
 
@@ -680,6 +688,12 @@ class AiBuilderTools(ToolBase):
680
688
  "color_count": len(catalog["icon_colors"]),
681
689
  "warnings": [],
682
690
  "verification": {"source": "backend AiBuildConstant ICON_NAMES/ICON_COLORS"},
691
+ "json_paths": {
692
+ "icon_names": "$.icon_names",
693
+ "icon_colors": "$.icon_colors",
694
+ "generic_icon_names": "$.generic_icon_names",
695
+ "common_examples": "$.common_examples",
696
+ },
683
697
  }
684
698
 
685
699
  @tool_cn_name("分组创建")
@@ -1361,26 +1375,25 @@ class AiBuilderTools(ToolBase):
1361
1375
  suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
1362
1376
  )
1363
1377
 
1364
- @tool_cn_name("应用流程摘要读取")
1365
- def app_read_flow_summary(self, *, profile: str, app_key: str) -> JSONObject:
1366
- """执行应用相关逻辑。"""
1367
- normalized_args = {"app_key": app_key}
1378
+ @tool_cn_name("Workflow Spec Schema")
1379
+ def app_flow_get_schema(self, *, profile: str, schema_version: str | None = None) -> JSONObject:
1380
+ normalized_args = {"schema_version": schema_version}
1368
1381
  return _safe_tool_call(
1369
- lambda: self._facade.app_read_flow_summary(profile=profile, app_key=app_key),
1370
- error_code="FLOW_READ_FAILED",
1382
+ lambda: self._facade.flow_get_schema(profile=profile, schema_version=schema_version),
1383
+ error_code="FLOW_SPEC_SCHEMA_FAILED",
1371
1384
  normalized_args=normalized_args,
1372
- suggested_next_call={"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}},
1385
+ suggested_next_call={"tool_name": "app_flow_get_schema", "arguments": {"profile": profile, **normalized_args}},
1373
1386
  )
1374
1387
 
1375
- @tool_cn_name("应用流程详情查询")
1376
- def app_get_flow(self, *, profile: str, app_key: str) -> JSONObject:
1388
+ @tool_cn_name("Workflow Spec 读取")
1389
+ def app_get_flow(self, *, profile: str, app_key: str, version_id: str | None = None) -> JSONObject:
1377
1390
  """执行应用相关逻辑。"""
1378
- normalized_args = {"app_key": app_key}
1391
+ normalized_args = {"app_key": app_key, "version_id": version_id}
1379
1392
  return _safe_tool_call(
1380
- lambda: self._facade.app_get_flow(profile=profile, app_key=app_key),
1393
+ lambda: self._facade.flow_get(profile=profile, app_key=app_key, version_id=version_id),
1381
1394
  error_code="APP_GET_FLOW_FAILED",
1382
1395
  normalized_args=normalized_args,
1383
- suggested_next_call={"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}},
1396
+ suggested_next_call={"tool_name": "app_flow_get", "arguments": {"profile": profile, "app_key": app_key}},
1384
1397
  )
1385
1398
 
1386
1399
  @tool_cn_name("应用图表摘要读取")
@@ -1591,52 +1604,6 @@ class AiBuilderTools(ToolBase):
1591
1604
  suggested_next_call={"tool_name": "app_layout_plan", "arguments": {"profile": profile, **normalized_request}},
1592
1605
  )
1593
1606
 
1594
- @tool_cn_name("应用流程规划")
1595
- def app_flow_plan(
1596
- self,
1597
- *,
1598
- profile: str,
1599
- app_key: str,
1600
- mode: str = "replace",
1601
- nodes: list[JSONObject] | None = None,
1602
- transitions: list[JSONObject] | None = None,
1603
- preset: str | None = None,
1604
- ) -> JSONObject:
1605
- """执行应用相关逻辑。"""
1606
- try:
1607
- request = FlowPlanRequest.model_validate(
1608
- {
1609
- "app_key": app_key,
1610
- "mode": mode,
1611
- "nodes": nodes or [],
1612
- "transitions": transitions or [],
1613
- "preset": preset,
1614
- }
1615
- )
1616
- except ValidationError as exc:
1617
- return _validation_failure(
1618
- str(exc),
1619
- tool_name="app_flow_plan",
1620
- exc=exc,
1621
- suggested_next_call={
1622
- "tool_name": "app_flow_plan",
1623
- "arguments": {
1624
- "profile": profile,
1625
- "app_key": app_key,
1626
- "mode": "replace",
1627
- "preset": "basic_approval",
1628
- "nodes": [],
1629
- "transitions": [],
1630
- },
1631
- },
1632
- )
1633
- return _safe_tool_call(
1634
- lambda: self._facade.app_flow_plan(profile=profile, request=request),
1635
- error_code="FLOW_PLAN_FAILED",
1636
- normalized_args=request.model_dump(mode="json"),
1637
- suggested_next_call={"tool_name": "app_flow_plan", "arguments": {"profile": profile, **request.model_dump(mode="json")}},
1638
- )
1639
-
1640
1607
  @tool_cn_name("应用视图规划")
1641
1608
  def app_views_plan(
1642
1609
  self,
@@ -2367,111 +2334,51 @@ class AiBuilderTools(ToolBase):
2367
2334
  *,
2368
2335
  profile: str,
2369
2336
  app_key: str,
2370
- mode: str = "replace",
2337
+ spec: JSONObject,
2371
2338
  publish: bool = True,
2372
- nodes: list[JSONObject],
2373
- transitions: list[JSONObject],
2339
+ idempotency_key: str | None = None,
2340
+ schema_version: str | None = None,
2374
2341
  ) -> JSONObject:
2375
2342
  """执行应用相关逻辑。"""
2376
- result = self._app_flow_apply_once(
2377
- profile=profile,
2378
- app_key=app_key,
2379
- mode=mode,
2380
- publish=publish,
2381
- nodes=nodes,
2382
- transitions=transitions,
2383
- )
2384
- result = self._retry_after_self_lock_release(
2385
- profile=profile,
2386
- result=result,
2387
- retry_call=lambda: self._app_flow_apply_once(
2388
- profile=profile,
2389
- app_key=app_key,
2390
- mode=mode,
2391
- publish=publish,
2392
- nodes=nodes,
2393
- transitions=transitions,
2394
- ),
2395
- )
2396
- return _attach_builder_apply_envelope("app_flow_apply", result)
2397
-
2398
- def _app_flow_apply_once(
2399
- self,
2400
- *,
2401
- profile: str,
2402
- app_key: str,
2403
- mode: str = "replace",
2404
- publish: bool = True,
2405
- nodes: list[JSONObject],
2406
- transitions: list[JSONObject],
2407
- ) -> JSONObject:
2408
- """执行内部辅助逻辑。"""
2409
- plan_result = self._rewrite_plan_result_for_apply(
2410
- result=self.app_flow_plan(
2411
- profile=profile,
2412
- app_key=app_key,
2413
- mode=mode,
2414
- nodes=nodes,
2415
- transitions=transitions,
2416
- preset=None,
2417
- ),
2418
- profile=profile,
2419
- publish=publish,
2420
- plan_tool_name="app_flow_plan",
2421
- apply_tool_name="app_flow_apply",
2422
- )
2423
- if not isinstance(plan_result, dict) or plan_result.get("status") != "success":
2424
- return plan_result
2425
- plan_args = plan_result.get("normalized_args")
2426
- if not isinstance(plan_args, dict):
2427
- plan_args = {}
2428
- try:
2429
- request = FlowPlanRequest.model_validate(
2430
- {
2431
- "app_key": plan_args.get("app_key") or app_key,
2432
- "mode": plan_args.get("mode") or mode,
2433
- "nodes": plan_args.get("nodes") or [],
2434
- "transitions": plan_args.get("transitions") or [],
2435
- "preset": None,
2436
- }
2437
- )
2438
- except ValidationError as exc:
2439
- return _validation_failure(
2440
- str(exc),
2343
+ if not isinstance(spec, dict) or not spec:
2344
+ return _config_failure(
2441
2345
  tool_name="app_flow_apply",
2442
- exc=exc,
2443
- suggested_next_call={
2444
- "tool_name": "app_flow_apply",
2445
- "arguments": {
2446
- "profile": profile,
2447
- "app_key": str(plan_args.get("app_key") or app_key),
2448
- "mode": str(plan_args.get("mode") or "replace"),
2449
- "publish": publish,
2450
- "nodes": plan_args.get("nodes") or [{"id": "start", "type": "start", "name": "发起"}],
2451
- "transitions": plan_args.get("transitions") or [],
2452
- },
2453
- },
2346
+ message="app_flow_apply requires a non-empty WorkflowSpecDTO `spec` object.",
2347
+ fix_hint="Call app_flow_get_schema, then app_flow_get for GET-first baseline, patch spec, and apply.",
2454
2348
  )
2455
2349
  normalized_args = {
2456
- "app_key": request.app_key,
2457
- "mode": request.mode,
2350
+ "app_key": app_key,
2458
2351
  "publish": publish,
2459
- "nodes": [node.model_dump(mode="json") for node in request.nodes],
2460
- "transitions": [transition.model_dump(mode="json", by_alias=True) for transition in request.transitions],
2352
+ "spec": spec,
2353
+ "idempotency_key": idempotency_key,
2354
+ "schema_version": schema_version,
2461
2355
  }
2462
- return _safe_tool_call(
2463
- lambda: self._facade.app_flow_apply(
2356
+ result = _safe_tool_call(
2357
+ lambda: self._facade.flow_apply(
2464
2358
  profile=profile,
2465
- app_key=request.app_key,
2466
- mode=request.mode,
2359
+ app_key=app_key,
2360
+ spec=spec,
2467
2361
  publish=publish,
2468
- nodes=[node.model_dump(mode="json") for node in request.nodes],
2469
- transitions=[transition.model_dump(mode="json", by_alias=True) for transition in request.transitions],
2362
+ idempotency_key=idempotency_key,
2363
+ schema_version=schema_version,
2470
2364
  ),
2471
2365
  error_code="FLOW_APPLY_FAILED",
2472
2366
  normalized_args=normalized_args,
2473
2367
  suggested_next_call={"tool_name": "app_flow_apply", "arguments": {"profile": profile, **normalized_args}},
2474
2368
  )
2369
+ result = self._retry_after_self_lock_release(
2370
+ profile=profile,
2371
+ result=result,
2372
+ retry_call=lambda: self._facade.flow_apply(
2373
+ profile=profile,
2374
+ app_key=app_key,
2375
+ spec=spec,
2376
+ publish=publish,
2377
+ idempotency_key=idempotency_key,
2378
+ schema_version=schema_version,
2379
+ ),
2380
+ )
2381
+ return _attach_builder_apply_envelope("app_flow_apply", result)
2475
2382
 
2476
2383
  @tool_cn_name("应用视图应用")
2477
2384
  def app_views_apply(
@@ -3927,11 +3834,58 @@ def _builder_contract_with_apply_output(tool_name: str, contract: JSONObject) ->
3927
3834
  "schema_version": BUILDER_APPLY_SCHEMA_VERSION,
3928
3835
  "preferred_ui_fields": ["operation", "summary", "resources"],
3929
3836
  "resource_fields": ["resource_type", "operation", "status", "id", "key", "name", "ids", "parent", "icon_config", "error_code", "message"],
3837
+ "json_paths": _builder_apply_json_paths(),
3930
3838
  "legacy_fields_preserved": True,
3931
3839
  }
3932
3840
  return public
3933
3841
 
3934
3842
 
3843
+ def _builder_tool_contract_summary(tool_name: str, contract: JSONObject) -> JSONObject:
3844
+ allowed_values = contract.get("allowed_values") if isinstance(contract, dict) else {}
3845
+ allowed_values_keys = sorted(str(key) for key in allowed_values) if isinstance(allowed_values, dict) else []
3846
+ return {
3847
+ "tool_name": tool_name,
3848
+ "contract_path": "$.contract",
3849
+ "allowed_keys_path": "$.contract.allowed_keys",
3850
+ "allowed_values_path": "$.contract.allowed_values",
3851
+ "allowed_values_key_style": "flat_dotted_keys",
3852
+ "minimal_example_path": "$.contract.minimal_example",
3853
+ "execution_notes_path": "$.contract.execution_notes",
3854
+ "top_level_allowed_values_usage": "empty on successful contract lookup; use $.contract.allowed_values instead",
3855
+ "allowed_values_keys_sample": allowed_values_keys[:12],
3856
+ "json_paths": {
3857
+ "contract": "$.contract",
3858
+ "allowed_keys": "$.contract.allowed_keys",
3859
+ "allowed_values": "$.contract.allowed_values",
3860
+ "minimal_example": "$.contract.minimal_example",
3861
+ "execution_notes": "$.contract.execution_notes",
3862
+ "aliases": "$.contract.aliases",
3863
+ },
3864
+ }
3865
+
3866
+
3867
+ def _builder_apply_json_paths() -> JSONObject:
3868
+ return {
3869
+ "status": "$.status",
3870
+ "schema_version": "$.schema_version",
3871
+ "operation": "$.operation",
3872
+ "summary": "$.summary",
3873
+ "resources": "$.resources",
3874
+ "warnings": "$.warnings",
3875
+ "verification": "$.verification",
3876
+ "details": "$.details",
3877
+ "write_executed": "$.write_executed",
3878
+ "write_may_have_succeeded": "$.write_may_have_succeeded",
3879
+ "safe_to_retry": "$.safe_to_retry",
3880
+ "next_action": "$.next_action",
3881
+ "summary_write_executed": "$.summary.write_executed",
3882
+ "summary_safe_to_retry": "$.summary.safe_to_retry",
3883
+ "resource_keys": "$.resources[].key",
3884
+ "resource_ids": "$.resources[].id",
3885
+ "resource_statuses": "$.resources[].status",
3886
+ }
3887
+
3888
+
3935
3889
  def _attach_builder_apply_envelope(tool_name: str, payload: JSONObject) -> JSONObject:
3936
3890
  if not isinstance(payload, dict):
3937
3891
  return payload
@@ -3940,6 +3894,7 @@ def _attach_builder_apply_envelope(tool_name: str, payload: JSONObject) -> JSONO
3940
3894
  payload["operation"] = tool_name
3941
3895
  payload["resources"] = resources
3942
3896
  payload["summary"] = _builder_apply_summary(payload, resources)
3897
+ payload["json_paths"] = _builder_apply_json_paths()
3943
3898
  return payload
3944
3899
 
3945
3900
 
@@ -5748,92 +5703,56 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
5748
5703
  "sections": [{"type": "paragraph", "paragraph_id": "basic", "title": "基础信息", "rows": [["项目名称", "项目负责人", "项目阶段", "优先级"]]}],
5749
5704
  },
5750
5705
  },
5751
- "app_flow_plan": {
5752
- "allowed_keys": ["app_key", "mode", "nodes", "transitions", "preset"],
5753
- "aliases": {
5754
- "overwrite": "replace",
5755
- "base_preset": "preset",
5756
- "default_approval": "basic_approval",
5757
- "node.role_names": "node.assignees.role_names",
5758
- "node.role_ids": "node.assignees.role_ids",
5759
- "node.member_names": "node.assignees.member_names",
5760
- "node.member_emails": "node.assignees.member_emails",
5761
- "node.member_uids": "node.assignees.member_uids",
5762
- "node.editable_fields": "node.permissions.editable_fields",
5763
- "default_approval": "basic_approval",
5764
- },
5765
- "allowed_values": {
5766
- "mode": ["replace"],
5767
- "preset": [member.value for member in FlowPreset],
5768
- "node.type": PUBLIC_STABLE_FLOW_NODE_TYPES,
5769
- },
5770
- "dependency_hints": [
5771
- "approval-style workflows require an explicit status field",
5772
- "approve/fill/copy nodes require at least one assignee",
5706
+ "app_flow_get_schema": {
5707
+ "allowed_keys": ["schema_version"],
5708
+ "aliases": {"schemaVersion": "schema_version"},
5709
+ "allowed_values": {},
5710
+ "execution_notes": [
5711
+ "returns WorkflowSpec JSON Schema from /workflow/spec/schema",
5712
+ "call this before authoring a new spec or validating spec shape",
5713
+ "worksheet-level approval deduplication toggles (legacy global settings) are not yet part of WorkflowSpec",
5773
5714
  ],
5715
+ "minimal_example": {
5716
+ "profile": "default",
5717
+ "schema_version": "vnext-2026-06",
5718
+ },
5719
+ },
5720
+ "app_flow_get": {
5721
+ "allowed_keys": ["app_key", "version_id"],
5722
+ "aliases": {"versionId": "version_id"},
5723
+ "allowed_values": {},
5774
5724
  "execution_notes": [
5775
- "public flow building is intentionally limited to linear workflows",
5776
- "branch and condition nodes are disabled because the backend workflow route is not front-end stable for these node types",
5725
+ "returns current WorkflowSpecDTO for one app via GET /workflow/spec",
5726
+ "use GET-first before patching and app_flow_apply",
5777
5727
  ],
5778
5728
  "minimal_example": {
5779
5729
  "profile": "default",
5780
5730
  "app_key": "APP_KEY",
5781
- "mode": "replace",
5782
- "preset": "basic_approval",
5783
- "nodes": [
5784
- {
5785
- "id": "approve_1",
5786
- "type": "approve",
5787
- "name": "部门审批",
5788
- "assignees": {"role_names": ["项目经理"]},
5789
- "permissions": {"editable_fields": ["状态", "审批意见"]},
5790
- }
5791
- ],
5792
- "transitions": [],
5793
5731
  },
5794
5732
  },
5795
5733
  "app_flow_apply": {
5796
- "allowed_keys": ["app_key", "mode", "publish", "nodes", "transitions"],
5734
+ "allowed_keys": ["app_key", "publish", "spec", "idempotency_key", "schema_version"],
5797
5735
  "aliases": {
5798
- "overwrite": "replace",
5799
- "node.role_names": "node.assignees.role_names",
5800
- "node.role_ids": "node.assignees.role_ids",
5801
- "node.member_names": "node.assignees.member_names",
5802
- "node.member_emails": "node.assignees.member_emails",
5803
- "node.member_uids": "node.assignees.member_uids",
5804
- "node.editable_fields": "node.permissions.editable_fields",
5805
- },
5806
- "allowed_values": {
5807
- "mode": ["replace"],
5808
- "node.type": PUBLIC_STABLE_FLOW_NODE_TYPES,
5736
+ "schemaVersion": "schema_version",
5737
+ "idempotencyKey": "idempotency_key",
5809
5738
  },
5739
+ "allowed_values": {},
5810
5740
  "dependency_hints": [
5811
- "approval-style workflows require an explicit status field",
5812
- "approve/fill/copy nodes require at least one assignee",
5741
+ "spec must be a complete WorkflowSpecDTO object (replace-only apply)",
5813
5742
  ],
5814
5743
  "execution_notes": [
5815
- "public flow building is intentionally limited to linear workflows",
5816
- "app_flow_apply is replace-only; do not treat node snippets as partial patches",
5817
- "branch and condition nodes are disabled because the backend workflow route is not front-end stable for these node types",
5818
- "workflow verification only covers linear node structure in the public tool surface",
5744
+ "posts to /workflow/spec:apply with appKey, idempotencyKey, schemaVersion, and spec",
5745
+ "verification uses appliedSpec, diffSummary, and semanticLint from the apply response",
5746
+ "publish=false keeps changes in draft when supported by the backend",
5819
5747
  ],
5820
5748
  "minimal_example": {
5821
5749
  "profile": "default",
5822
5750
  "app_key": "APP_KEY",
5823
- "mode": "replace",
5824
5751
  "publish": True,
5825
- "nodes": [
5826
- {"id": "start", "type": "start", "name": "发起"},
5827
- {
5828
- "id": "approve_1",
5829
- "type": "approve",
5830
- "name": "部门审批",
5831
- "assignees": {"role_names": ["项目经理"]},
5832
- "permissions": {"editable_fields": ["状态", "审批意见"]},
5833
- },
5834
- {"id": "end", "type": "end", "name": "结束"},
5835
- ],
5836
- "transitions": [{"from": "start", "to": "approve_1"}, {"from": "approve_1", "to": "end"}],
5752
+ "spec": {
5753
+ "nodes": [{"id": "n1", "type": "APPLICANT", "name": "发起"}],
5754
+ "transitions": [],
5755
+ },
5837
5756
  },
5838
5757
  },
5839
5758
  "app_views_plan": {
@@ -5894,7 +5813,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
5894
5813
  "creating a new view follows backend createViewgraphConfig and requires both ViewManagementAuth and DataManageAuth; updating/deleting existing views only requires ViewManagementAuth",
5895
5814
  "upsert_views[].visibility may set per-view visibility; omit it to preserve an existing view's auth or default a new view to workspace/not",
5896
5815
  "filters are saved fixed filters that apply when the view opens; query_conditions configure the frontend query panel and only apply after a user enters query values",
5897
- "upsert_views[].query_conditions.rows is a layout matrix of field names; it is compiled to backend queryCondition queIds",
5816
+ "upsert_views[].query_conditions.rows is a layout matrix of query-panel supported field names; it is compiled to backend queryCondition queIds",
5817
+ "do not put relation/attachment/subtable/code_block/q_linker/address fields in query_conditions; use filters for fixed filters or app_associated_resources_apply.match_mappings for current-record relation/report matching",
5898
5818
  "use patch_views for partial parameter replacement on existing views; the tool reads current config, merges patch_views[].set/unset, then submits the backend full-save payload internally",
5899
5819
  "remove_views accepts a raw view_key or an exact unique view name; after DELETE the tool verifies deletion by single view_key readback, not by a full app view list",
5900
5820
  "deleted views return verification.by_view[].delete_executed, readback_status, and safe_to_retry_delete=false; if readback is pending, do not blindly repeat the delete",
@@ -5945,6 +5865,14 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
5945
5865
  ],
5946
5866
  "remove_views": [],
5947
5867
  },
5868
+ "query_conditions_field_rules": {
5869
+ "supported_field_types": ["text", "long_text", "number", "amount", "date", "datetime", "single_select", "multi_select", "phone", "email", "boolean", "member", "department"],
5870
+ "unsupported_field_types": ["relation", "attachment", "subtable", "address", "code_block", "q_linker"],
5871
+ "use_instead": {
5872
+ "fixed_filter": "filters",
5873
+ "current_record_related_report_or_view": "app_associated_resources_apply.match_mappings",
5874
+ },
5875
+ },
5948
5876
  "gantt_example": {
5949
5877
  "profile": "default",
5950
5878
  "app_key": "APP_KEY",
@@ -6024,7 +5952,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
6024
5952
  "buttons omitted preserves existing button config; buttons=[] clears all buttons; buttons=[...] replaces the full button config",
6025
5953
  "upsert_views[].visibility may set per-view visibility; omit it to preserve an existing view's auth or default a new view to workspace/not",
6026
5954
  "filters are saved fixed filters that apply when the view opens; query_conditions configure the frontend query panel and only apply after a user enters query values",
6027
- "upsert_views[].query_conditions.rows is a layout matrix of field names; it is compiled to backend queryCondition queIds",
5955
+ "upsert_views[].query_conditions.rows is a layout matrix of query-panel supported field names; it is compiled to backend queryCondition queIds",
5956
+ "do not put relation/attachment/subtable/code_block/q_linker/address fields in query_conditions; use filters for fixed filters or app_associated_resources_apply.match_mappings for current-record relation/report matching",
6028
5957
  "use patch_views for partial parameter replacement on existing views; the public update mode is patch even though the backend save is still a full view payload",
6029
5958
  "remove_views accepts a raw view_key or an exact unique view name; after DELETE the tool verifies deletion by single view_key readback, not by a full app view list",
6030
5959
  "deleted views return verification.by_view[].delete_executed, readback_status, and safe_to_retry_delete=false; if readback is pending, do not blindly repeat the delete",
@@ -6078,6 +6007,14 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
6078
6007
  ],
6079
6008
  "remove_views": [],
6080
6009
  },
6010
+ "query_conditions_field_rules": {
6011
+ "supported_field_types": ["text", "long_text", "number", "amount", "date", "datetime", "single_select", "multi_select", "phone", "email", "boolean", "member", "department"],
6012
+ "unsupported_field_types": ["relation", "attachment", "subtable", "address", "code_block", "q_linker"],
6013
+ "use_instead": {
6014
+ "fixed_filter": "filters",
6015
+ "current_record_related_report_or_view": "app_associated_resources_apply.match_mappings",
6016
+ },
6017
+ },
6081
6018
  "gantt_example": {
6082
6019
  "profile": "default",
6083
6020
  "app_key": "APP_KEY",
@@ -6165,8 +6102,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
6165
6102
  "aliases": {},
6166
6103
  "allowed_values": {},
6167
6104
  "execution_notes": [
6168
- "returns workflow configuration summary for one app",
6169
- "use this before app_flow_apply when you need the current node structure",
6105
+ "returns WorkflowSpecDTO for one app (alias of app_flow_get)",
6106
+ "use app_flow_get_schema then app_flow_get before app_flow_apply",
6170
6107
  ],
6171
6108
  "minimal_example": {
6172
6109
  "profile": "default",
@@ -6472,7 +6409,6 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
6472
6409
  _PRIVATE_BUILDER_TOOL_CONTRACTS = {
6473
6410
  "app_schema_plan",
6474
6411
  "app_layout_plan",
6475
- "app_flow_plan",
6476
6412
  "app_views_plan",
6477
6413
  }
6478
6414