@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.
- package/README.md +2 -2
- package/npm/bin/qingflow-app-user-mcp.mjs +31 -2
- package/npm/lib/runtime.mjs +43 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-user/SKILL.md +1 -1
- package/skills/qingflow-mcp-setup/SKILL.md +1 -1
- package/skills/qingflow-record-analysis/SKILL.md +1 -1
- package/skills/qingflow-record-delete/SKILL.md +1 -1
- package/skills/qingflow-record-import/SKILL.md +1 -1
- package/skills/qingflow-record-insert/SKILL.md +5 -2
- package/skills/qingflow-record-update/SKILL.md +1 -1
- package/skills/qingflow-task-ops/SKILL.md +1 -1
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/builder_facade/models.py +0 -39
- package/src/qingflow_mcp/builder_facade/service.py +532 -859
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
- package/src/qingflow_mcp/cli/commands/builder.py +44 -12
- package/src/qingflow_mcp/cli/main.py +4 -6
- package/src/qingflow_mcp/public_surface.py +2 -0
- package/src/qingflow_mcp/server_app_builder.py +16 -8
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/executor.py +3 -133
- package/src/qingflow_mcp/tools/ai_builder_tools.py +177 -241
- package/src/qingflow_mcp/tools/solution_tools.py +30 -2
- package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
- package/src/qingflow_mcp/version.py +71 -1
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -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
|
-
|
|
465
|
-
|
|
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
|
-
|
|
473
|
-
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
|
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.
|
|
1370
|
-
error_code="
|
|
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": "
|
|
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.
|
|
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": "
|
|
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
|
-
|
|
2337
|
+
spec: JSONObject,
|
|
2371
2338
|
publish: bool = True,
|
|
2372
|
-
|
|
2373
|
-
|
|
2339
|
+
idempotency_key: str | None = None,
|
|
2340
|
+
schema_version: str | None = None,
|
|
2374
2341
|
) -> JSONObject:
|
|
2375
2342
|
"""执行应用相关逻辑。"""
|
|
2376
|
-
|
|
2377
|
-
|
|
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
|
-
|
|
2443
|
-
|
|
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":
|
|
2457
|
-
"mode": request.mode,
|
|
2350
|
+
"app_key": app_key,
|
|
2458
2351
|
"publish": publish,
|
|
2459
|
-
"
|
|
2460
|
-
"
|
|
2352
|
+
"spec": spec,
|
|
2353
|
+
"idempotency_key": idempotency_key,
|
|
2354
|
+
"schema_version": schema_version,
|
|
2461
2355
|
}
|
|
2462
|
-
|
|
2463
|
-
lambda: self._facade.
|
|
2356
|
+
result = _safe_tool_call(
|
|
2357
|
+
lambda: self._facade.flow_apply(
|
|
2464
2358
|
profile=profile,
|
|
2465
|
-
app_key=
|
|
2466
|
-
|
|
2359
|
+
app_key=app_key,
|
|
2360
|
+
spec=spec,
|
|
2467
2361
|
publish=publish,
|
|
2468
|
-
|
|
2469
|
-
|
|
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
|
-
"
|
|
5752
|
-
"allowed_keys": ["
|
|
5753
|
-
"aliases": {
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
"
|
|
5757
|
-
"
|
|
5758
|
-
"
|
|
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
|
-
"
|
|
5776
|
-
"
|
|
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", "
|
|
5734
|
+
"allowed_keys": ["app_key", "publish", "spec", "idempotency_key", "schema_version"],
|
|
5797
5735
|
"aliases": {
|
|
5798
|
-
"
|
|
5799
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
5816
|
-
"
|
|
5817
|
-
"
|
|
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
|
-
"
|
|
5826
|
-
{"id": "
|
|
5827
|
-
|
|
5828
|
-
|
|
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
|
|
6169
|
-
"use
|
|
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
|
|