@josephyan/qingflow-cli 1.1.4 → 1.1.6

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.
Files changed (154) hide show
  1. package/README.md +7 -3
  2. package/docs/local-agent-install.md +57 -6
  3. package/entry_point.py +1 -1
  4. package/npm/bin/qingflow-skills.mjs +5 -0
  5. package/npm/bin/qingflow.mjs +1 -34
  6. package/npm/lib/runtime.mjs +21 -101
  7. package/npm/scripts/postinstall.mjs +1 -10
  8. package/package.json +3 -2
  9. package/pyproject.toml +1 -1
  10. package/skills/qingflow-cli/SKILL.md +58 -44
  11. package/skills/qingflow-cli/manifest.yaml +1 -1
  12. package/skills/qingflow-cli/reference/00-INDEX.md +35 -0
  13. package/skills/qingflow-cli/reference/builder/10-build-single-app.md +38 -0
  14. package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +39 -0
  15. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md → builder/30-schema-fields.md} +52 -10
  16. package/skills/qingflow-cli/reference/builder/40-layout.md +52 -0
  17. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md → builder/50-views.md} +39 -15
  18. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md → builder/60-charts.md} +36 -13
  19. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md → builder/70-portal.md} +36 -13
  20. package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +41 -0
  21. package/skills/qingflow-cli/reference/builder/90-workflow.md +34 -0
  22. package/skills/qingflow-cli/reference/builder/99-publish-verify.md +46 -0
  23. package/skills/qingflow-cli/reference/builder/README.md +41 -0
  24. package/skills/qingflow-cli/reference/builder/code-integrations/README.md +130 -0
  25. package/skills/qingflow-cli/reference/builder/code-integrations/code-block.md +66 -0
  26. package/skills/qingflow-cli/reference/builder/code-integrations/q-linker.md +77 -0
  27. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md → builder/reference/app-delivery-sop.md} +26 -16
  28. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/README.md +293 -0
  29. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-complete-system.md +809 -0
  30. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-single-app.md +830 -0
  31. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/complete-system-development-guide.md +123 -0
  32. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/create-app.md +182 -0
  33. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/environments.md +63 -0
  34. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/flow-actors-and-permissions.md +142 -0
  35. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/gotchas.md +108 -0
  36. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/match-rules.md +114 -0
  37. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/public-surface-sync.md +75 -0
  38. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/single-app-development-guide.md +58 -0
  39. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/solution-playbooks.md +52 -0
  40. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/tool-selection.md +107 -0
  41. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-flow.md +7 -0
  42. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-layout.md +7 -0
  43. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-schema.md +7 -0
  44. package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-views.md +7 -0
  45. package/skills/qingflow-cli/reference/builder/workflow/01-overview.md +45 -0
  46. package/skills/qingflow-cli/reference/builder/workflow/02-update-mode.md +53 -0
  47. package/skills/qingflow-cli/reference/builder/workflow/03-flow-patterns.md +57 -0
  48. package/skills/qingflow-cli/reference/builder/workflow/04-stage1-business-modeling.md +131 -0
  49. package/skills/qingflow-cli/reference/builder/workflow/05-stage2-members-roles.md +29 -0
  50. package/skills/qingflow-cli/reference/builder/workflow/06-stage3-build-spec.md +165 -0
  51. package/skills/qingflow-cli/reference/builder/workflow/07-stage4-validate-spec.md +33 -0
  52. package/skills/qingflow-cli/reference/builder/workflow/08-stage5-apply-verify.md +51 -0
  53. package/skills/qingflow-cli/reference/builder/workflow/09-stage6-summary.md +88 -0
  54. package/skills/qingflow-cli/reference/builder/workflow/10-node-config-reference.md +93 -0
  55. package/skills/qingflow-cli/reference/builder/workflow/11-troubleshooting.md +15 -0
  56. package/skills/qingflow-cli/reference/builder/workflow/README.md +88 -0
  57. package/skills/qingflow-cli/reference/builder/workflow/workflow-schema.json +1754 -0
  58. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ADMIN_CHEATSHEET.md → core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md} +3 -3
  59. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md → core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md} +6 -6
  60. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_EXPLORATION_REPORT.md → core/QINGFLOW_CLI_EXPLORATION_REPORT.md} +2 -2
  61. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_FIELD_DATA_TYPES.md → core/QINGFLOW_CLI_FIELD_DATA_TYPES.md} +11 -11
  62. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_MEMBER_CHEATSHEET.md → core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md} +4 -4
  63. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md → core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md} +4 -4
  64. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md} +3 -3
  65. package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_DELETE_WORKFLOW.md +31 -0
  66. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md} +4 -4
  67. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md} +7 -7
  68. package/skills/qingflow-cli/reference/record/analysis/README.md +130 -0
  69. package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +91 -0
  70. package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +112 -0
  71. package/skills/qingflow-cli/reference/record/analysis/business-context.md +74 -0
  72. package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +69 -0
  73. package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +106 -0
  74. package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +172 -0
  75. package/skills/qingflow-cli/reference/record/analysis/report-format.md +76 -0
  76. package/skills/qingflow-cli/reference/record/insert/README.md +75 -0
  77. package/skills/qingflow-cli/reference/{QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md → task/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md} +5 -5
  78. package/skills/qingflow-cli/reference/task/ops/README.md +131 -0
  79. package/skills/qingflow-cli/reference/task/ops/environments.md +43 -0
  80. package/skills/qingflow-cli/reference/task/ops/workflow-usage.md +26 -0
  81. package/skills/qingflow-cli/scripts/validate_system_build_summary.py +124 -0
  82. package/skills/qingflow-cli/scripts/workflow/diff_flow_spec.py +275 -0
  83. package/skills/qingflow-cli/scripts/workflow/validate_flow_spec.py +605 -0
  84. package/skills/qingflow-mcp-setup/SKILL.md +115 -0
  85. package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
  86. package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
  87. package/skills/qingflow-mcp-setup/references/environments.md +62 -0
  88. package/skills/qingflow-mcp-setup/references/generic-stdio.md +32 -0
  89. package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
  90. package/src/qingflow_mcp/__init__.py +1 -1
  91. package/src/qingflow_mcp/__main__.py +6 -2
  92. package/src/qingflow_mcp/builder_facade/models.py +282 -102
  93. package/src/qingflow_mcp/builder_facade/service.py +4192 -935
  94. package/src/qingflow_mcp/cli/commands/builder.py +316 -298
  95. package/src/qingflow_mcp/cli/commands/chart.py +1 -1
  96. package/src/qingflow_mcp/cli/commands/common.py +12 -3
  97. package/src/qingflow_mcp/cli/commands/exports.py +2 -2
  98. package/src/qingflow_mcp/cli/commands/imports.py +3 -3
  99. package/src/qingflow_mcp/cli/commands/portal.py +2 -2
  100. package/src/qingflow_mcp/cli/commands/record.py +101 -27
  101. package/src/qingflow_mcp/cli/commands/task.py +28 -47
  102. package/src/qingflow_mcp/cli/commands/view.py +1 -1
  103. package/src/qingflow_mcp/cli/context.py +0 -3
  104. package/src/qingflow_mcp/cli/formatters.py +784 -16
  105. package/src/qingflow_mcp/cli/main.py +117 -33
  106. package/src/qingflow_mcp/errors.py +43 -2
  107. package/src/qingflow_mcp/public_surface.py +26 -17
  108. package/src/qingflow_mcp/response_trim.py +81 -17
  109. package/src/qingflow_mcp/server.py +14 -12
  110. package/src/qingflow_mcp/server_app_builder.py +65 -21
  111. package/src/qingflow_mcp/server_app_user.py +22 -16
  112. package/src/qingflow_mcp/session_store.py +11 -7
  113. package/src/qingflow_mcp/solution/compiler/__init__.py +3 -1
  114. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
  115. package/src/qingflow_mcp/solution/executor.py +245 -18
  116. package/src/qingflow_mcp/tools/ai_builder_tools.py +1780 -406
  117. package/src/qingflow_mcp/tools/app_tools.py +184 -43
  118. package/src/qingflow_mcp/tools/approval_tools.py +197 -35
  119. package/src/qingflow_mcp/tools/auth_tools.py +92 -16
  120. package/src/qingflow_mcp/tools/code_block_tools.py +298 -40
  121. package/src/qingflow_mcp/tools/custom_button_tools.py +64 -10
  122. package/src/qingflow_mcp/tools/directory_tools.py +236 -72
  123. package/src/qingflow_mcp/tools/export_tools.py +244 -34
  124. package/src/qingflow_mcp/tools/feedback_tools.py +9 -0
  125. package/src/qingflow_mcp/tools/file_tools.py +9 -3
  126. package/src/qingflow_mcp/tools/import_tools.py +336 -49
  127. package/src/qingflow_mcp/tools/navigation_tools.py +91 -12
  128. package/src/qingflow_mcp/tools/package_tools.py +118 -6
  129. package/src/qingflow_mcp/tools/portal_tools.py +39 -3
  130. package/src/qingflow_mcp/tools/qingbi_report_tools.py +116 -7
  131. package/src/qingflow_mcp/tools/record_tools.py +1141 -356
  132. package/src/qingflow_mcp/tools/resource_read_tools.py +188 -39
  133. package/src/qingflow_mcp/tools/role_tools.py +80 -9
  134. package/src/qingflow_mcp/tools/solution_tools.py +59 -45
  135. package/src/qingflow_mcp/tools/task_context_tools.py +662 -158
  136. package/src/qingflow_mcp/tools/task_tools.py +113 -29
  137. package/src/qingflow_mcp/tools/view_tools.py +106 -3
  138. package/src/qingflow_mcp/tools/workflow_tools.py +48 -4
  139. package/src/qingflow_mcp/tools/workspace_tools.py +71 -3
  140. /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_MATCH_RULES.md → builder/reference/match-rules.md} +0 -0
  141. /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md → builder/reference/workspace-icons.md} +0 -0
  142. /package/skills/qingflow-cli/reference/{charts_remove.example.json → examples/charts/charts_remove.example.json} +0 -0
  143. /package/skills/qingflow-cli/reference/{charts_reorder.example.json → examples/charts/charts_reorder.example.json} +0 -0
  144. /package/skills/qingflow-cli/reference/{charts_upsert_bar.example.json → examples/charts/charts_upsert_bar.example.json} +0 -0
  145. /package/skills/qingflow-cli/reference/{charts_upsert_dashboard_starter.example.json → examples/charts/charts_upsert_dashboard_starter.example.json} +0 -0
  146. /package/skills/qingflow-cli/reference/{charts_upsert_minimal.example.json → examples/charts/charts_upsert_minimal.example.json} +0 -0
  147. /package/skills/qingflow-cli/reference/{portal_sections_all_types.example.json → examples/portal/portal_sections_all_types.example.json} +0 -0
  148. /package/skills/qingflow-cli/reference/{portal_sections_five_types.example.json → examples/portal/portal_sections_five_types.example.json} +0 -0
  149. /package/skills/qingflow-cli/reference/{portal_sections_standard_workbench.example.json → examples/portal/portal_sections_standard_workbench.example.json} +0 -0
  150. /package/skills/qingflow-cli/reference/{_batch_schema_complex.json → examples/schema/_batch_schema_complex.json} +0 -0
  151. /package/skills/qingflow-cli/reference/{_batch_schema_scalar.json → examples/schema/_batch_schema_scalar.json} +0 -0
  152. /package/skills/qingflow-cli/reference/{schema_add_fields_minimal.example.json → examples/schema/schema_add_fields_minimal.example.json} +0 -0
  153. /package/skills/qingflow-cli/reference/{schema_apply_add_fields_all_types.json → examples/schema/schema_apply_add_fields_all_types.json} +0 -0
  154. /package/skills/qingflow-cli/reference/{views_upsert_table_minimal.example.json → examples/views/views_upsert_table_minimal.example.json} +0 -0
@@ -50,6 +50,7 @@ All resource tools operate with the logged-in user's Qingflow permissions.
50
50
 
51
51
  If `app_key` is unknown, use `app_list` first. Pass `query` to filter visible apps by keyword.
52
52
  If the app is known but the data range is not, use `app_get` first and choose from `accessible_views`.
53
+ Treat an explicit `view_id` as the exact frontend view context. If `system:all` fails, do not silently switch to `system:initiated`, `system:todo`, or another system view unless the user, frontend URL, or `app_get.accessible_views` explicitly selects that view.
53
54
  If an accessible view has `analysis_supported=false`, do not use it for `record_access` or `record_list`. `boardView` and `ganttView` are special UI views, not data-access targets.
54
55
  `view_get(view_id=...)` also returns `export_capability`; it only means there is a supported export route, not that export permission has been verified.
55
56
 
@@ -57,7 +58,7 @@ If an accessible view has `analysis_supported=false`, do not use it for `record_
57
58
 
58
59
  Call `record_insert_schema_get` before `record_insert`.
59
60
  For simple field changes after the target record is clear, call `record_update` directly. Use `record_update_schema_get` for diagnostics, ambiguous fields, or complex writable-scope inspection.
60
- Call `record_code_block_schema_get` before `record_code_block_run`.
61
+ Prefer `record_code_block_schema_get` before `record_code_block_run` when field selection or binding diagnostics are unclear; if the exact code-block field id is already known from record/task detail, run directly.
61
62
  Call `app_get` first when the data range is unclear, then use `record_browse_schema_get(view_id=...)` before `record_access`, `record_list`, `record_get`, or `record_logs_get`.
62
63
  Call `record_import_schema_get` when the import field mapping is unclear before template download or verify.
63
64
 
@@ -67,7 +68,7 @@ Call `record_import_schema_get` when the import field mapping is unclear before
67
68
  ## Schema Scope
68
69
 
69
70
  `record_insert_schema_get` returns the current user's insert-ready applicant schema; read `required_fields`, `optional_fields`, `runtime_linked_required_fields`, and `payload_template`.
70
- `record_update_schema_get` returns the current record's overall update-ready writable field set and route diagnostics across matched accessible views; read `writable_fields`, `payload_template`, `available_update_routes`, and `recommended_update_route`.
71
+ `record_update_schema_get` returns the current record's update-ready writable field set and route diagnostics. When `view_id` is explicit, it is the exact frontend view context and must not be silently replaced by another view.
71
72
  `record_browse_schema_get(view_id=...)` returns the same readable fields shown in the selected Qingflow table view header.
72
73
  `record_access.fields` / CSV columns and `record_list.columns / where / order_by / query_fields` use that exact same view schema.
73
74
  `record_code_block_schema_get` returns code-block-ready schema for exact code block field selection.
@@ -106,8 +107,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
106
107
  `app_get -> record_browse_schema_get(view_id=...) -> record_list / record_get / record_logs_get`
107
108
  `record_insert_schema_get -> record_insert(items)`
108
109
  `record_update` for simple updates; `record_update_schema_get -> record_update` when the writable field scope is unclear.
109
- `record_list / record_get -> record_delete`
110
- `record_code_block_schema_get -> record_code_block_run`
110
+ `record_list / record_get -> record_delete(system view_id)`
111
+ `record_code_block_run` directly when the exact code-block field is known; otherwise `record_code_block_schema_get -> record_code_block_run`
111
112
 
112
113
  - Use `columns` as `[{{field_id}}]`
113
114
  - Use `record_list(query=..., query_fields=[{{field_id}}])` for fuzzy single-record lookup, then follow `lookup.next_action`; `query_fields` is search scope and `columns` is display shape.
@@ -116,14 +117,14 @@ Analysis answers must include concrete numbers. When applicable, include percent
116
117
  - Legacy forms such as bare integer `field_id`, `fieldId`, `operator`, `values`, or `order` may still parse, but they are compatibility-only and not the canonical DSL
117
118
 
118
119
  - `record_insert` defaults to an applicant-node `items` array; each item contains a field-title keyed `fields` map. A single insert is one item.
119
- - `record_update` uses a field-title keyed `fields` map. It first tries the data-manager direct update route, then falls back to the frontend custom-view detail edit route when the selected view can cover the payload; if a unique current-user todo task for the same record exposes editable fields, it can finally use the workflow save-only route. Read `update_route` and `tried_routes` after execution.
120
+ - `record_update` uses a field-title keyed `fields` map. It first tries the data-manager direct update route, then falls back to the frontend custom-view detail edit route when the selected view can cover the payload; if a unique current-user todo task for the same record exposes editable fields, it can finally use the workflow save-only route. On success, read `status`, `update_route`, and `verification_status`; on failure, read the failure reason and route diagnostics.
120
121
  - For insert, `runtime_linked_required_fields` means required-but-not-directly-writable fields that are usually supplied by runtime linkage or upstream context.
121
122
  - For insert, fields marked `may_become_required=true` stay in `optional_fields`; they are still directly writable, but linked visibility or option-driven rules can make them required at runtime.
122
123
  - Read field-level `linkage` whenever present on `record_insert_schema_get` or `record_update_schema_get`; it is the static hint for linked visibility, reference-driven auto fill, and formula/default auto-fill behavior.
123
124
  - `linkage.sources` lists upstream field titles that influence the current field; `linkage.affects_fields` lists downstream fields that may change when the current field changes.
124
125
  - `linkage.kind=logic_visibility` means linked visibility or option-driven rules are involved; `linkage.kind=reference_fill` means reference/default matching logic is involved; `linkage.kind=formula_fill` means formula/default auto-fill logic is involved.
125
- - `record_update_schema_get` exposes the overall writable field set and route candidates for the record, but not every field combination is guaranteed; `record_update` still needs data-manager permission, one single matched custom view that can cover the payload, or one unique editable current-user todo task.
126
- - `record_delete` deletes by `record_id` or `record_ids`.
126
+ - `record_update_schema_get` exposes the writable field set and route candidates for the record; with an explicit `view_id`, diagnostics stay scoped to that selected frontend view. Not every field combination is guaranteed; `record_update` still needs data-manager permission, one single matched custom view that can cover the payload, or one unique editable current-user todo task.
127
+ - `record_delete` deletes by `record_id` or `record_ids`, and requires an accessible system `view_id`; use custom views only to locate records, not as the delete route.
127
128
  - `record_get` is the single-record frontend detail context tool. It returns detail-page visible fields, one-level relation targets, first-page data/workflow logs, associated views/reports, local readable image assets, local downloadable file assets, unavailable context, and `semantic_context`.
128
129
  - Use `record_logs_get` only when the user needs the full visible data/workflow log history for a specific record. It writes JSONL files locally and returns file paths plus completeness metadata; do not expect full log arrays in the response.
129
130
  - Read record images from `record_get.media_assets.items[].local_path` when `readable_by_agent=true`; read attachments/documents/tables from `record_get.file_assets.items[].local_path` and `extraction.text_path` when present. `record_get` follows the frontend storage cookie redirect path for Qingflow attachments, and remote file URLs should not be treated as directly readable.
@@ -140,7 +141,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
140
141
 
141
142
  Use `record_code_block_run` when the user wants to execute a form code-block field against an existing record.
142
143
 
143
- - Always resolve the exact code-block field from `record_code_block_schema_get` first.
144
+ - Prefer resolving the exact code-block field from `record_code_block_schema_get`, but do not treat applicant-schema 40002 as final denial when record/task detail already exposes the code-block field id.
145
+ - `record_code_block_run` uses the record/task detail answers for the execution context; when applicant schema is unavailable it can still execute with a numeric code-block field id, but schema-bound relation writeback may be skipped.
144
146
  - Treat code-block execution as write-capable, not read-only.
145
147
  - If the code block is bound to relation outputs, Qingflow may calculate target answers and write them back automatically.
146
148
  - For safe debugging, pass `apply_writeback=false` and inspect the parsed alias results plus `relation.calculated_answers_preview` before allowing any writeback.
@@ -182,12 +184,12 @@ Use `record_code_block_run` when the user wants to execute a form code-block fie
182
184
  `task_list -> task_get -> task_action_execute`
183
185
 
184
186
  - `task_list` returns task-card summaries keyed by `task_id`.
185
- - Prefer `task_get(task_id=...)` for detail reads; MCP resolves the current todo locator internally.
186
- - `task_action_execute(task_id=..., action=...)` is also supported; MCP resolves the current todo locator internally before calling the real action route.
187
+ - For detail reads and actions, pass the exact `task_id` from `task_list.data.items[].task_id`.
188
+ - `task_id` is not a row number, list index, record id, or workflow node id.
189
+ - `task_action_execute` only uses `task_id`; do not reconstruct or pass `app_key + record_id + workflow_node_id` for actions.
187
190
  - `task_workflow_log_get(task_id=...)` and `task_associated_report_detail_get(task_id=...)` are also supported for the current todo context.
188
191
  - Use `task_associated_report_detail_get` for associated view or report details.
189
- - Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs, use `record_logs_get(app_key, record_id, view_id?)`.
190
- - Task actions operate on `app_key + record_id + workflow_node_id`, not `task_id`.
192
+ - Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs, first choose an accessible view with `app_get`, then call `record_logs_get(app_key, record_id, view_id)` with that same explicit `view_id`.
191
193
 
192
194
  ## Time Handling
193
195
 
@@ -37,21 +37,22 @@ def build_builder_server() -> FastMCP:
37
37
  "Use solution_install when the user explicitly wants to install a packaged solution/template by solution_key, optionally copying bundled demo data. "
38
38
  "Use package_list to find visible app packages by keyword and package_get to read package detail before editing; if creating or updating an app package may be appropriate, use package_apply with explicit user intent; otherwise use app_resolve to locate app resources, "
39
39
  "Use workspace_icon_catalog_get before creating app packages, apps, or portals when supported icon/color candidates are needed; new workspace resources require explicit non-template icon + color, and the CLI validates choices without inferring business defaults. "
40
- "app_get as the default app map read, then app_get_fields/app_repair_code_blocks/app_get_layout/app_get_views/app_get_flow/app_flow_get/app_flow_get_schema/app_get_charts/portal_list/portal_get/view_get/chart_get for focused configuration reads, "
40
+ "app_get as the default app map read, then app_get_fields/app_repair_code_blocks/app_get_layout/app_get_views/app_get_flow/app_get_charts/portal_list/portal_get/view_get/chart_get for focused configuration reads, "
41
41
  "member_search/role_search/role_create when workflow assignees must come from the directory or role catalog, preferring roles over explicit members unless the user explicitly names members, "
42
- "then app_schema_apply/app_layout_apply/app_flow_apply/app_views_apply/app_custom_buttons_apply/app_associated_resources_apply/app_charts_apply/portal_apply to execute normalized patches; these apply tools perform planning, normalization, and dependency checks internally where applicable. Schema/layout/views noop requests skip publish, app_custom_buttons_apply and app_associated_resources_apply publish after at least one write succeeds and expose no draft-only parameter, charts are immediate-live without publish and resolve targets by chart_id first then exact unique chart name, portal updates use replace semantics only when sections are supplied and edit-mode base-info-only updates may omit sections, portal pc layout is a 24-column grid and mobile is a 6-column grid so omit position or use layout_preset when unsure, publish=false only guarantees draft/base-info updates for tools that still expose that parameter, and flow should use publish=false whenever you only want draft/precheck behavior. "
42
+ "then app_schema_apply/app_layout_apply/app_flow_apply/app_views_apply/app_custom_buttons_apply/app_associated_resources_apply/app_charts_apply/portal_apply/portal_delete to execute normalized patches; these apply/delete tools perform planning, normalization, and dependency checks internally where applicable. Schema/layout/views noop requests skip publish, app_custom_buttons_apply and app_associated_resources_apply publish after at least one write succeeds and expose no draft-only parameter, charts are immediate-live without publish and resolve targets by chart_id first then exact unique chart name, portal updates use replace semantics only when sections are supplied and edit-mode base-info-only updates may omit sections, portal delete separates DELETE execution from readback verification, portal pc layout is a 24-column grid and mobile is a 6-column grid so omit position or use layout_preset when unsure, publish=false only guarantees draft/base-info updates for tools that still expose that parameter, and flow should use publish=false whenever you only want draft/precheck behavior. "
43
43
  "Builder apply/write outputs include schema_version, operation, summary, and resources[]; use resources[].id/key/name/ids/parent as the stable UI and agent display entry, and keep legacy fields such as field_diff/views_diff/chart_results only for compatibility or troubleshooting. "
44
44
  "For existing object parameter replacement, prefer patch_views, patch_buttons, patch_resources, and patch_charts with set/unset; the tool reads current config and full-saves internally, while upsert_* is for creation or full target configuration and should not be used as an incomplete partial update. "
45
45
  "For builder delete/remove apply results, separate delete execution from readback verification: after DELETE is sent, resources expose delete_executed, readback_status, and safe_to_retry_delete=false. If readback_status is unavailable or still_exists, do not blindly repeat the delete; confirm later with app_get/view_get/chart_get or the relevant apply readback. Views/buttons use single-item readback; associated resources use one app-level resource-pool readback because there is no confirmed single-item GET. "
46
+ "For builder write/apply results, separate write execution from final readback verification: status=partial_success with write_executed=true and safe_to_retry=false means the write was sent or succeeded, while final readback, publish verification, or metadata confirmation is pending or permission-restricted. Report it as written but unverified; do not repeat the same write blindly. "
46
47
  "For app_schema_apply, configure data title and data cover directly in field JSON with as_data_title=true and as_data_cover=true; data title is required and exactly one field may be marked, while data cover is optional and must be a top-level attachment field. For multi-app creation, pass apps[]/--apps-file on app_schema_apply; each item may have client_key, and relation fields may use target_app_ref to point at another same-call client_key. "
47
- "For app_views_apply, keep fixed saved filters in filters and configure the frontend query panel separately with query_conditions; query_conditions.rows is a matrix of field names compiled to backend queryCondition queIds. New views default associated report/view display to visible with limit_type=all; existing views preserve their current associated display unless associated_resources is explicitly patched. "
48
- "For custom button body create/update/delete and view placement, use app_custom_buttons_apply. For addData buttons, prefer trigger_add_data_config.target_app_key + field_mappings/default_values; do not ask agents to write raw que_relation unless maintaining a legacy config. field_mappings.source_field accepts source schema fields and supported system fields: 数据ID/row_record_id/apply_id/_id means current record id (-17), 编号/record_number means visible record number (0). To fill a target relation with the current record, map {'source_field': '数据ID', 'target_field': '目标引用字段'}; default_values is only for static constants. View button bindings merge by default and merge-mode view_configs must include buttons; use view_configs[].mode=replace or explicit buttons=[] only when clearing/replacing existing bindings is intended. Builder view_key arguments are raw keys from app_get.views[].view_key and must not be prefixed with custom:. "
48
+ "For app_views_apply, keep fixed saved filters in filters and configure the frontend query panel separately with query_conditions; query_conditions.rows is a matrix of field names compiled to backend queryCondition queIds. New views default associated report/view display to visible with limit_type=all; existing views preserve their current associated display unless associated_resources is explicitly patched. View writes use ViewManagementAuth (beingViewManageStatus), falling back to DataManageAuth when advanced app permissions are not enabled. "
49
+ "For custom button body create/update/delete and view placement, use app_custom_buttons_apply. Button body writes require EditAppAuth; view_configs placement writes require ViewManagementAuth, matching the backend viewConfig route. For addData buttons, prefer trigger_add_data_config.target_app_key + field_mappings/default_values; do not ask agents to write raw que_relation unless maintaining a legacy config. field_mappings.source_field accepts source schema fields and supported system fields: 数据ID/row_record_id/apply_id/_id means current record id (-17), 编号/record_number means visible record number (0). To fill a target relation with the current record, map {'source_field': '数据ID', 'target_field': '目标引用字段'}; default_values is only for static constants. View button bindings merge by default and merge-mode view_configs must include buttons; use view_configs[].mode=replace or explicit buttons=[] only when clearing/replacing existing bindings is intended. Builder view_key arguments are raw keys from app_get.views[].view_key and must not be prefixed with custom:. "
49
50
  "For BI reports, keep report-body development separate from Qingflow in-app display: use app_charts_apply to create, update, remove, or reorder app-source QingBI chart bodies/configs with dataSourceType=qingflow; chart dimension/metric/filter/query fields must come from app_get_fields.chart_fields, not record schema or form-only fields; dataset BI reports are not created or edited by app_charts_apply yet and should be created in QingBI first, then attached with app_associated_resources_apply using report_source=dataset. "
50
51
  "For associated views/reports, use app_associated_resources_apply. Use match_mappings for filtering associated resources: dynamic current-record conditions use source_field, static conditions use value. match_mappings also supports 数据ID(-17) and 编号(0). Do not ask agents to write raw match_rules unless preserving a legacy backend config. "
51
52
  "For associated reports/views, use app_associated_resources_apply for both the app-level associated_resources pool and per-view display config; associated_item_id is the app-level form_asos_chart.id, and view_configs/remove/reorder may also pass an existing resource's chart_id/chart_key/view_key because the tool resolves those to the internal id. Before creating an associated resource, read app_get.associated_resources and reuse an existing matching target_app_key + view_key/chart_key through patch_resources; client_key only works inside one apply call and is not persisted. Do not ask agents to pass backend raw sourceType: views infer the internal Qingflow view source, reports default to BI app reports, and dataset reports use report_source=dataset. "
52
53
  "For code_block fields with output bindings, always use qf_output assignment rather than const/let qf_output, and use app_repair_code_blocks when an existing form hangs because output-bound fields stay loading. "
53
54
  "Use package_apply to manage package metadata, visibility, grouping, and ordering, and app_publish_verify for explicit final publish verification. "
54
- "For workflow edits, use app_flow_get_schema and app_flow_get (GET-first), author a complete WorkflowSpecDTO in spec, then app_flow_apply; CLI equivalent is qingflow builder flow schema|get|apply --spec-file. Do not use general-server legacy workflow config reads (auditNodes list/detail, global settings, qsource config reads); they are removed. "
55
+ "For workflow edits, keep the public builder surface on stable linear flows only: start/approve/fill/copy/webhook/end. Branch and condition nodes are intentionally disabled because the backend workflow route is not front-end stable for those node types. Declare node assignees and editable fields explicitly. "
55
56
  "If builder writes are blocked by the current user's own edit lock, use app_release_edit_lock_if_mine with the lock owner details from the failed result. "
56
57
  "Do not handcraft internal solution payloads or rely on build_id/stage/repair. "
57
58
  "If the current MCP capability is unsupported, the workflow is awkward, or the user's need still cannot be satisfied after reasonable use, first summarize the gap, ask whether to submit feedback, and call feedback_submit only after explicit user confirmation."
@@ -320,6 +321,7 @@ def build_builder_server() -> FastMCP:
320
321
  patch_buttons: list[dict] | None = None,
321
322
  remove_buttons: list[dict] | None = None,
322
323
  view_configs: list[dict] | None = None,
324
+ apps: list[dict] | None = None,
323
325
  ) -> dict:
324
326
  return ai_builder.app_custom_buttons_apply(
325
327
  profile=profile,
@@ -328,6 +330,7 @@ def build_builder_server() -> FastMCP:
328
330
  patch_buttons=patch_buttons or [],
329
331
  remove_buttons=remove_buttons or [],
330
332
  view_configs=view_configs or [],
333
+ apps=apps,
331
334
  )
332
335
 
333
336
  @server.tool()
@@ -339,6 +342,7 @@ def build_builder_server() -> FastMCP:
339
342
  remove_associated_item_ids: list[int] | None = None,
340
343
  reorder_associated_item_ids: list[int] | None = None,
341
344
  view_configs: list[dict] | None = None,
345
+ apps: list[dict] | None = None,
342
346
  ) -> dict:
343
347
  return ai_builder.app_associated_resources_apply(
344
348
  profile=profile,
@@ -348,15 +352,16 @@ def build_builder_server() -> FastMCP:
348
352
  remove_associated_item_ids=remove_associated_item_ids or [],
349
353
  reorder_associated_item_ids=reorder_associated_item_ids or [],
350
354
  view_configs=view_configs or [],
355
+ apps=apps,
351
356
  )
352
357
 
353
358
  @server.tool()
354
- def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
355
- return ai_builder.app_get(profile=profile, app_key=app_key)
359
+ def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
360
+ return ai_builder.app_get(profile=profile, app_key=app_key, app_keys=app_keys)
356
361
 
357
362
  @server.tool()
358
- def app_get_fields(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
359
- return ai_builder.app_get_fields(profile=profile, app_key=app_key)
363
+ def app_get_fields(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
364
+ return ai_builder.app_get_fields(profile=profile, app_key=app_key, app_keys=app_keys)
360
365
 
361
366
  @server.tool()
362
367
  def app_repair_code_blocks(
@@ -368,20 +373,28 @@ def build_builder_server() -> FastMCP:
368
373
  return ai_builder.app_repair_code_blocks(profile=profile, app_key=app_key, field=field, apply=apply)
369
374
 
370
375
  @server.tool()
371
- def app_get_layout(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
372
- return ai_builder.app_get_layout(profile=profile, app_key=app_key)
376
+ def app_get_layout(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
377
+ return ai_builder.app_get_layout(profile=profile, app_key=app_key, app_keys=app_keys)
378
+
379
+ @server.tool()
380
+ def app_get_views(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
381
+ return ai_builder.app_get_views(profile=profile, app_key=app_key, app_keys=app_keys)
382
+
383
+ @server.tool()
384
+ def app_get_flow(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
385
+ return ai_builder.app_get_flow(profile=profile, app_key=app_key, app_keys=app_keys)
373
386
 
374
387
  @server.tool()
375
- def app_get_views(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
376
- return ai_builder.app_get_views(profile=profile, app_key=app_key)
388
+ def app_get_charts(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
389
+ return ai_builder.app_get_charts(profile=profile, app_key=app_key, app_keys=app_keys)
377
390
 
378
391
  @server.tool()
379
- def app_get_flow(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
380
- return ai_builder.app_get_flow(profile=profile, app_key=app_key)
392
+ def app_get_buttons(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
393
+ return ai_builder.app_get_buttons(profile=profile, app_key=app_key, app_keys=app_keys)
381
394
 
382
395
  @server.tool()
383
- def app_get_charts(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
384
- return ai_builder.app_get_charts(profile=profile, app_key=app_key)
396
+ def app_get_associated_resources(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
397
+ return ai_builder.app_get_associated_resources(profile=profile, app_key=app_key, app_keys=app_keys)
385
398
 
386
399
  @server.tool()
387
400
  def portal_list(profile: str = DEFAULT_PROFILE) -> dict:
@@ -481,12 +494,17 @@ def build_builder_server() -> FastMCP:
481
494
  mode: str = "merge",
482
495
  publish: bool = True,
483
496
  sections: list[dict] | None = None,
497
+ apps: list[dict] | None = None,
484
498
  ) -> dict:
485
- return ai_builder.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or [])
499
+ return ai_builder.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or [], apps=apps)
486
500
 
487
501
  @server.tool()
488
- def app_flow_get(profile: str = DEFAULT_PROFILE, app_key: str = "", version_id: str = "") -> dict:
489
- return ai_builder.app_get_flow(profile=profile, app_key=app_key, version_id=version_id or None)
502
+ def app_flow_get(
503
+ profile: str = DEFAULT_PROFILE,
504
+ app_key: str = "",
505
+ version_id: str = "",
506
+ ) -> dict:
507
+ return ai_builder.app_flow_get(profile=profile, app_key=app_key, version_id=version_id or None)
490
508
 
491
509
  @server.tool()
492
510
  def app_flow_get_schema(profile: str = DEFAULT_PROFILE, schema_version: str = "") -> dict:
@@ -496,18 +514,26 @@ def build_builder_server() -> FastMCP:
496
514
  def app_flow_apply(
497
515
  profile: str = DEFAULT_PROFILE,
498
516
  app_key: str = "",
517
+ mode: str = "replace",
499
518
  publish: bool = True,
519
+ nodes: list[dict] | None = None,
520
+ transitions: list[dict] | None = None,
500
521
  spec: dict | None = None,
501
522
  idempotency_key: str = "",
502
523
  schema_version: str = "",
524
+ patch_nodes: list[dict] | None = None,
503
525
  ) -> dict:
504
526
  return ai_builder.app_flow_apply(
505
527
  profile=profile,
506
528
  app_key=app_key,
529
+ mode=mode,
507
530
  publish=publish,
508
- spec=spec or {},
531
+ nodes=nodes or [],
532
+ transitions=transitions or [],
533
+ spec=spec,
509
534
  idempotency_key=idempotency_key or None,
510
535
  schema_version=schema_version or None,
536
+ patch_nodes=patch_nodes,
511
537
  )
512
538
 
513
539
  @server.tool()
@@ -518,6 +544,7 @@ def build_builder_server() -> FastMCP:
518
544
  upsert_views: list[dict] | None = None,
519
545
  patch_views: list[dict] | None = None,
520
546
  remove_views: list[str] | None = None,
547
+ apps: list[dict] | None = None,
521
548
  ) -> dict:
522
549
  return ai_builder.app_views_apply(
523
550
  profile=profile,
@@ -526,6 +553,7 @@ def build_builder_server() -> FastMCP:
526
553
  upsert_views=upsert_views or [],
527
554
  patch_views=patch_views or [],
528
555
  remove_views=remove_views or [],
556
+ apps=apps,
529
557
  )
530
558
 
531
559
  @server.tool()
@@ -536,6 +564,7 @@ def build_builder_server() -> FastMCP:
536
564
  patch_charts: list[dict] | None = None,
537
565
  remove_chart_ids: list[str] | None = None,
538
566
  reorder_chart_ids: list[str] | None = None,
567
+ apps: list[dict] | None = None,
539
568
  ) -> dict:
540
569
  return ai_builder.app_charts_apply(
541
570
  profile=profile,
@@ -544,6 +573,7 @@ def build_builder_server() -> FastMCP:
544
573
  patch_charts=patch_charts or [],
545
574
  remove_chart_ids=remove_chart_ids or [],
546
575
  reorder_chart_ids=reorder_chart_ids or [],
576
+ apps=apps,
547
577
  )
548
578
 
549
579
  @server.tool()
@@ -565,6 +595,7 @@ def build_builder_server() -> FastMCP:
565
595
  dash_global_config: dict | None = None,
566
596
  config: dict | None = None,
567
597
  payload: dict | None = None,
598
+ patch_sections: list[dict] | None = None,
568
599
  ) -> dict:
569
600
  payload = payload if isinstance(payload, dict) else {}
570
601
  has_dash_key = bool((dash_key or "").strip())
@@ -600,17 +631,30 @@ def build_builder_server() -> FastMCP:
600
631
  dash_global_config=dash_global_config,
601
632
  config=config or {},
602
633
  payload=payload,
634
+ patch_sections=patch_sections,
635
+ )
636
+
637
+ @server.tool()
638
+ def portal_delete(
639
+ profile: str = DEFAULT_PROFILE,
640
+ dash_key: str = "",
641
+ ) -> dict:
642
+ return ai_builder.portal_delete(
643
+ profile=profile,
644
+ dash_key=dash_key,
603
645
  )
604
646
 
605
647
  @server.tool()
606
648
  def app_publish_verify(
607
649
  profile: str = DEFAULT_PROFILE,
608
650
  app_key: str = "",
651
+ app_keys: list[str] | None = None,
609
652
  expected_package_id: int | None = None,
610
653
  ) -> dict:
611
654
  return ai_builder.app_publish_verify(
612
655
  profile=profile,
613
656
  app_key=app_key,
657
+ app_keys=app_keys,
614
658
  expected_package_id=expected_package_id,
615
659
  )
616
660
 
@@ -33,6 +33,7 @@ def build_user_server() -> FastMCP:
33
33
 
34
34
  If `app_key` is unknown, use `app_list` first. Pass `query` to filter visible apps by keyword.
35
35
  If the app is known but the data range is not, use `app_get` first and choose from `accessible_views`.
36
+ Treat an explicit `view_id` as the exact frontend view context. If `system:all` fails, do not silently switch to `system:initiated`, `system:todo`, or another system view unless the user, frontend URL, or `app_get.accessible_views` explicitly selects that view.
36
37
  If an accessible view has `analysis_supported=false`, do not use it for `record_access` or `record_list`. `boardView` and `ganttView` are special UI views, not data-access targets.
37
38
  `view_get(view_id=...)` also returns `export_capability`; it only means there is a supported export route, not that export permission has been verified.
38
39
 
@@ -48,7 +49,7 @@ If an accessible view has `analysis_supported=false`, do not use it for `record_
48
49
 
49
50
  Call `record_insert_schema_get` before `record_insert`.
50
51
  For simple field changes after the target record is clear, call `record_update` directly. Use `record_update_schema_get` for diagnostics, ambiguous fields, or complex writable-scope inspection.
51
- Call `record_code_block_schema_get` before `record_code_block_run`.
52
+ Prefer `record_code_block_schema_get` before `record_code_block_run` when field selection or binding diagnostics are unclear; if the exact code-block field id is already known from record/task detail, run directly.
52
53
  Call `app_get` first when the data range is unclear, then use `record_browse_schema_get(view_id=...)` before `record_access`, `record_list`, `record_get`, or `record_logs_get`.
53
54
  Call `record_import_schema_get` when the import field mapping is unclear before template download or verify.
54
55
 
@@ -59,11 +60,11 @@ Call `record_import_schema_get` when the import field mapping is unclear before
59
60
 
60
61
  `record_insert_schema_get` returns the current user's insert-ready applicant schema; read `required_fields`, `optional_fields`, `runtime_linked_required_fields`, and `payload_template`.
61
62
  Inside `optional_fields`, any field with `may_become_required=true` is still writable, but may become required when linked visibility or option-driven runtime rules activate.
62
- `record_update_schema_get` returns the current record's overall update-ready writable field set and route diagnostics across matched accessible views; read `writable_fields`, `payload_template`, `available_update_routes`, and `recommended_update_route`.
63
+ `record_update_schema_get` returns the current record's update-ready writable field set and route diagnostics. When `view_id` is explicit, it is the exact frontend view context and must not be silently replaced by another view.
63
64
  `record_browse_schema_get(view_id=...)` returns the same readable fields shown in the selected Qingflow table view header.
64
65
  `record_access.fields` / CSV columns and `record_list.columns / where / order_by / query_fields` use that exact same view schema; a missing field means it is not readable in that view.
65
66
  `searchQueIds` is a backend full-text search scope, not an output-column/projection mechanism.
66
- `record_code_block_schema_get` returns code-block-ready schema for exact code block field selection.
67
+ `record_code_block_schema_get` returns code-block-ready schema for exact code block field selection when the applicant schema is readable.
67
68
  `record_import_schema_get` returns import-ready column metadata.
68
69
 
69
70
  - Hidden fields are omitted.
@@ -105,8 +106,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
105
106
  `app_get -> record_browse_schema_get(view_id=...) -> record_list / record_get / record_logs_get`
106
107
  `record_insert_schema_get -> record_insert(items)`
107
108
  `record_update` for simple updates; `record_update_schema_get -> record_update` when the writable field scope is unclear.
108
- `record_list / record_get -> record_delete`
109
- `record_code_block_schema_get -> record_code_block_run`
109
+ `record_list / record_get -> record_delete(system view_id)`
110
+ `record_code_block_run` directly when the exact code-block field is known; otherwise `record_code_block_schema_get -> record_code_block_run`
110
111
  `portal_list -> portal_get -> chart_get / view_get`
111
112
  `portal_get -> view_get -> record_list`
112
113
 
@@ -117,14 +118,14 @@ Analysis answers must include concrete numbers. When applicable, include percent
117
118
  - Legacy forms such as bare integer `field_id`, `fieldId`, `operator`, `values`, or `order` may still parse, but they are compatibility-only and not the canonical DSL
118
119
 
119
120
  - `record_insert` defaults to an applicant-node `items` array; each item contains a field-title keyed `fields` map. A single insert is one item.
120
- - `record_update` uses a field-title keyed `fields` map. It first tries the data-manager direct update route, then falls back to the frontend custom-view detail edit route when the selected view can cover the payload; if a unique current-user todo task for the same record exposes editable fields, it can finally use the workflow save-only route. Read `update_route` and `tried_routes` after execution.
121
+ - `record_update` uses a field-title keyed `fields` map. It first tries the data-manager direct update route, then falls back to the frontend custom-view detail edit route when the selected view can cover the payload; if a unique current-user todo task for the same record exposes editable fields, it can finally use the workflow save-only route. On success, read `status`, `update_route`, and `verification_status`; on failure, read the failure reason and route diagnostics.
121
122
  - For insert, `runtime_linked_required_fields` means required-but-not-directly-writable fields that are usually supplied by runtime linkage or upstream context.
122
123
  - For insert, fields marked `may_become_required=true` stay in `optional_fields`; they are still directly writable, but linked visibility or option-driven rules can make them required at runtime.
123
124
  - Read field-level `linkage` whenever present on `record_insert_schema_get` or `record_update_schema_get`; it is the static hint for linked visibility, reference-driven auto fill, and formula/default auto-fill behavior.
124
125
  - `linkage.sources` lists upstream field titles that influence the current field; `linkage.affects_fields` lists downstream fields that may change when the current field changes.
125
126
  - `linkage.kind=logic_visibility` means linked visibility or option-driven rules are involved; `linkage.kind=reference_fill` means reference/default matching logic is involved; `linkage.kind=formula_fill` means formula/default auto-fill logic is involved.
126
- - `record_update_schema_get` exposes the overall writable field set and update route candidates for the record, but not every field combination is guaranteed; `record_update` still needs data-manager permission, one single matched custom view that can cover the payload, or one unique editable current-user todo task.
127
- - `record_delete` deletes by `record_id` or `record_ids`.
127
+ - `record_update_schema_get` exposes the writable field set and update route candidates for the record; with an explicit `view_id`, diagnostics stay scoped to that selected frontend view. Not every field combination is guaranteed; `record_update` still needs data-manager permission, one single matched custom view that can cover the payload, or one unique editable current-user todo task.
128
+ - `record_delete` deletes by `record_id` or `record_ids`, and requires an accessible system `view_id`; use custom views only to locate records, not as the delete route.
128
129
  - `record_get` is the single-record frontend detail context tool. It returns detail-page visible fields, one-level relation targets, first-page data/workflow logs, associated views/reports, local readable image assets, local downloadable file assets, unavailable context, and `semantic_context`.
129
130
  - Use `record_logs_get` only when the user needs the full visible data/workflow log history for a specific record. It writes JSONL files locally and returns file paths plus completeness metadata; do not expect full log arrays in the response.
130
131
  - Read record images from `record_get.media_assets.items[].local_path` when `readable_by_agent=true`; read attachments/documents/tables from `record_get.file_assets.items[].local_path` and `extraction.text_path` when present. `record_get` follows the frontend storage cookie redirect path for Qingflow attachments, and remote file URLs should not be treated as directly readable.
@@ -142,7 +143,8 @@ Analysis answers must include concrete numbers. When applicable, include percent
142
143
 
143
144
  Use `record_code_block_run` when the user wants to execute a form code-block field against an existing record.
144
145
 
145
- - Always resolve the exact code-block field from `record_code_block_schema_get` first.
146
+ - Prefer resolving the exact code-block field from `record_code_block_schema_get`, but do not treat applicant-schema 40002 as final denial when record/task detail already exposes the code-block field id.
147
+ - `record_code_block_run` uses the record/task detail answers for the execution context; when applicant schema is unavailable it can still execute with a numeric code-block field id, but schema-bound relation writeback may be skipped.
146
148
  - Treat code-block execution as write-capable, not read-only.
147
149
  - If the code block is bound to relation outputs, Qingflow may calculate target answers and write them back automatically.
148
150
  - For safe debugging, pass `apply_writeback=false` and inspect the parsed alias results plus `relation.calculated_answers_preview` before allowing any writeback.
@@ -187,12 +189,12 @@ Use export only when the user explicitly asks to export/download/generate an Exc
187
189
  `task_list -> task_get -> task_action_execute`
188
190
 
189
191
  - `task_list` returns task-card summaries keyed by `task_id`.
190
- - Prefer `task_get(task_id=...)` for detail reads; MCP resolves the current todo locator internally.
191
- - `task_action_execute(task_id=..., action=...)` is also supported; MCP resolves the current todo locator internally before calling the real action route.
192
+ - For detail reads and actions, pass the exact `task_id` from `task_list.data.items[].task_id`.
193
+ - `task_id` is not a row number, list index, record id, or workflow node id.
194
+ - `task_action_execute` only uses `task_id`; do not reconstruct or pass `app_key + record_id + workflow_node_id` for actions.
192
195
  - `task_workflow_log_get(task_id=...)` and `task_associated_report_detail_get(task_id=...)` are also supported for the current todo context.
193
196
  - Use `task_associated_report_detail_get` for associated view or report details.
194
- - Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs, use `record_logs_get(app_key, record_id, view_id?)`.
195
- - Task actions operate on `app_key + record_id + workflow_node_id`, not `task_id`.
197
+ - Use `task_workflow_log_get` for the current task context workflow log page. For full record-level data/workflow logs, first choose an accessible view with `app_get`, then call `record_logs_get(app_key, record_id, view_id)` with that same explicit `view_id`.
196
198
  - Treat `task_action_execute` as the tool-level action enum surface; the current task's real actions are only the ones listed in `task_get.capabilities.available_actions`.
197
199
  - Use `task_action_execute(action="save_only", fields=...)` when the user wants to save editable field changes on the current node without advancing the workflow.
198
200
  - `save_only` is exposed only when the backend current-node `editableQueIds` signal returns a non-empty result; MCP no longer infers `save_only` from local schema reconstruction.
@@ -263,10 +265,11 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
263
265
  def record_export_start(
264
266
  profile: str = DEFAULT_PROFILE,
265
267
  app_key: str = "",
266
- view_id: str = "system:all",
268
+ view_id: str = "",
267
269
  columns: list[dict | int] | None = None,
268
270
  where: list[dict] | None = None,
269
271
  order_by: list[dict] | None = None,
272
+ record_id: str | int | None = None,
270
273
  record_ids: list[str | int] | None = None,
271
274
  include_workflow_log: bool = False,
272
275
  ) -> dict:
@@ -277,6 +280,7 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
277
280
  columns=columns or [],
278
281
  where=where or [],
279
282
  order_by=order_by or [],
283
+ record_id=record_id,
280
284
  record_ids=record_ids or [],
281
285
  include_workflow_log=include_workflow_log,
282
286
  )
@@ -304,10 +308,11 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
304
308
  def record_export_direct(
305
309
  profile: str = DEFAULT_PROFILE,
306
310
  app_key: str = "",
307
- view_id: str = "system:all",
311
+ view_id: str = "",
308
312
  columns: list[dict | int] | None = None,
309
313
  where: list[dict] | None = None,
310
314
  order_by: list[dict] | None = None,
315
+ record_id: str | int | None = None,
311
316
  record_ids: list[str | int] | None = None,
312
317
  include_workflow_log: bool = False,
313
318
  download_to_path: str | None = None,
@@ -320,6 +325,7 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
320
325
  columns=columns or [],
321
326
  where=where or [],
322
327
  order_by=order_by or [],
328
+ record_id=record_id,
323
329
  record_ids=record_ids or [],
324
330
  include_workflow_log=include_workflow_log,
325
331
  download_to_path=download_to_path,
@@ -474,7 +480,7 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
474
480
 
475
481
  code_block_tools.register(server)
476
482
  task_context_tools.register(server)
477
- directory_tools.register(server)
483
+ directory_tools.register_frontend_search(server)
478
484
 
479
485
  return server
480
486
 
@@ -176,17 +176,21 @@ class SessionStore:
176
176
  return memory_session
177
177
  if not session_profile:
178
178
  return None
179
- token = self._get_secret(self._token_key(profile)) if session_profile.persisted else None
180
- if not token:
181
- token = session_profile.token
179
+ token = session_profile.token
180
+ if not token and session_profile.persisted:
181
+ token = self._get_secret(self._token_key(profile))
182
182
  if not token:
183
183
  return None
184
- login_token = self._get_secret(self._login_token_key(profile)) if session_profile.persisted else None
185
- credential = self._get_secret(self._credential_key(profile)) if session_profile.persisted else None
184
+ login_token = session_profile.login_token
185
+ if not login_token and session_profile.persisted:
186
+ login_token = self._get_secret(self._login_token_key(profile))
187
+ credential = session_profile.credential
188
+ if not credential and session_profile.persisted:
189
+ credential = self._get_secret(self._credential_key(profile))
186
190
  backend_session = BackendSession(
187
191
  token=token,
188
- login_token=login_token or session_profile.login_token,
189
- credential=credential or session_profile.credential,
192
+ login_token=login_token,
193
+ credential=credential,
190
194
  profile=profile,
191
195
  base_url=session_profile.base_url,
192
196
  qf_version=session_profile.qf_version,
@@ -10,6 +10,8 @@ from .navigation_compiler import compile_navigation
10
10
  from .package_compiler import compile_package
11
11
  from .portal_compiler import compile_portal
12
12
  from .view_compiler import compile_views
13
+ from .workflow_compiler import compile_workflow
14
+
13
15
 
14
16
  @dataclass(slots=True)
15
17
  class ExecutionStep:
@@ -99,7 +101,7 @@ def compile_solution(spec: SolutionSpec) -> CompiledSolution:
99
101
 
100
102
  def compile_entity(entity: EntitySpec, *, include_package: bool) -> CompiledEntity:
101
103
  app_create_payload, form_base_payload, form_relation_payload, field_specs, field_labels = compile_entity_form(entity, include_package=include_package)
102
- workflow_plan = None
104
+ workflow_plan = compile_workflow(entity)
103
105
  view_plans = compile_views(entity)
104
106
  chart_plans = compile_charts(entity)
105
107
  return CompiledEntity(