diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e3df6c7..7edd3f3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -694,7 +694,7 @@ jobs: go-version-file: go.mod - name: Test macOS auth and Keychain paths with Race Detection - run: go test -v -race -count=1 -timeout=10m ./internal/keychain ./internal/auth ./internal/app + run: go test -v -race -count=1 -timeout=12m ./internal/keychain ./internal/auth ./internal/app test-windows: name: Test (Windows) diff --git a/docs/command-index.md b/docs/command-index.md index efaaf54c..a1cd90d8 100644 --- a/docs/command-index.md +++ b/docs/command-index.md @@ -40,7 +40,7 @@ Every command inherits these flags (documented here once, not repeated per comma - [`dws doc` — DingTalk Doc](#dws-doc) · 21 commands - [`dws drive` — DingTalk Drive](#dws-drive) · 6 commands - [`dws minutes` — AI Minutes](#dws-minutes) · 19 commands -- [`dws oa` — OA Approval](#dws-oa) · 9 commands +- [`dws oa` — OA Approval](#dws-oa) · 12 commands - [`dws report` — Reports](#dws-report) · 7 commands - [`dws todo` — Todo Tasks](#dws-todo) · 6 commands @@ -277,14 +277,17 @@ _AI meeting notes: listing, summary, todos, transcription, recording control, mi ## `dws oa` — OA Approval -_OA approval workflows: list, approve, reject, revoke, records._ +_OA approval workflows: inspect forms, forecast routes, create instances, approve, reject, revoke, and audit records._ -**9 commands** +**12 commands** | Command | Description | When to use | |---|---|---| | `dws oa approval approve` | Approve a pending approval process instance (task) as the current user. | When the agent acts on a pending approval the user has delegated it to handle. | +| `dws oa approval create-instance` | Create a real approval process instance from validated form values or a complete request payload. | After the agent has inspected the form Schema, forecast the route, resolved any selectable approvers, and obtained explicit user confirmation. | | `dws oa approval detail` | Retrieve full details of an approval process instance, including form fields, attachments, and state. | When the agent needs to read the content of an approval ticket before deciding on it or summarizing it. | +| `dws oa approval form-schema` | Retrieve the form Schema for an approval template by processCode. | Before collecting or validating values for a new approval instance. | +| `dws oa approval forecast-process` | Forecast the approval route for a template and its proposed form values. | Before creating an instance, especially when the route contains user-selectable approver or notifier nodes. | | `dws oa approval list-forms` | List approval process templates (forms) the current user is allowed to initiate. | When the agent needs to pick the right approval form before submitting a new request. | | `dws oa approval list-initiated` | List approval process instances the current user has initiated. | When the agent reviews the status of approvals the user submitted. | | `dws oa approval list-pending` | List approval process instances currently awaiting action from the current user. | When the agent surfaces "needs your approval" items in the user's inbox. | diff --git a/internal/app/schema_oa_approval_contract_test.go b/internal/app/schema_oa_approval_contract_test.go new file mode 100644 index 00000000..14407c51 --- /dev/null +++ b/internal/app/schema_oa_approval_contract_test.go @@ -0,0 +1,76 @@ +// Copyright 2026 Alibaba Group +// Licensed under the Apache License, Version 2.0 (the "License"); + +package app + +import ( + "strings" + "testing" +) + +func TestOAApprovalDualModeConstraintsReachEmbeddedSchema(t *testing.T) { + tools := embeddedSchemaAllToolsForHelpFlagTest(t, NewRootCommand()) + + tests := []struct { + canonical string + optional []string + requireTogether []string + mutuallyExclusive []string + }{ + { + canonical: "oa.forecast_process", + optional: []string{"request", "process-code", "dept-id", "form-values"}, + requireTogether: []string{"process-code", "dept-id", "form-values"}, + mutuallyExclusive: []string{"process-code", "dept-id", "form-values"}, + }, + { + canonical: "oa.start_process_instance", + optional: []string{"request", "process-code", "dept-id", "form-values", "originator-user-id", "approvers", "approvers-action-type", "cc-list", "cc-position"}, + requireTogether: []string{"process-code", "form-values"}, + mutuallyExclusive: []string{"process-code", "dept-id", "form-values", "originator-user-id", "approvers", "approvers-action-type", "cc-list", "cc-position"}, + }, + } + + for _, test := range tests { + t.Run(test.canonical, func(t *testing.T) { + tool := tools[test.canonical] + parameters := schemaContractMap(tool["parameters"]) + for _, name := range test.optional { + if got := parameters[name]["required"]; got != false { + t.Errorf("--%s required = %#v, want false for dual-mode command", name, got) + } + } + assertSchemaContractConstraintGroup(t, tool, "require_one_of", []string{"request", "process-code"}) + assertSchemaContractConstraintGroup(t, tool, "require_together", test.requireTogether) + for _, name := range test.mutuallyExclusive { + assertSchemaContractConstraintGroup(t, tool, "mutually_exclusive", []string{"request", name}) + } + constraints, _ := tool["constraints"].(map[string]any) + groups, _ := constraints["mutually_exclusive"].([]any) + if len(groups) != len(test.mutuallyExclusive) { + t.Errorf("mutually_exclusive group count = %d, want %d: %#v", len(groups), len(test.mutuallyExclusive), groups) + } + for _, rawGroup := range groups { + group, _ := rawGroup.([]any) + if len(group) != 2 { + t.Errorf("mutually_exclusive contains an over-broad group: %#v", group) + } + } + + hasRequestOnlyExample := false + for _, example := range schemaContractStringSlice(tool["examples"]) { + if strings.Contains(example, " --request ") && !strings.Contains(example, " --process-code ") && !strings.Contains(example, " --form-values ") { + hasRequestOnlyExample = true + } + } + if !hasRequestOnlyExample { + t.Errorf("examples do not contain a request-only invocation: %#v", tool["examples"]) + } + }) + } + + create := tools["oa.start_process_instance"] + if got := schemaContractString(create["confirmation"]); got != "user_required" { + t.Errorf("create-instance confirmation = %q, want user_required", got) + } +} diff --git a/internal/cli/schema_agent_metadata/index.json b/internal/cli/schema_agent_metadata/index.json index 8362187c..850465a8 100644 --- a/internal/cli/schema_agent_metadata/index.json +++ b/internal/cli/schema_agent_metadata/index.json @@ -1,17 +1,17 @@ { "version": 1, - "source_hash": "sha256:1f76aa71b00f7b807b863ced6e98aca76dc4bda65107ea2a6c67eb4ec629db43", - "surface_hash": "sha256:89bc72d0a5ff03df634d733dc8b71c3010572524542538552c33c657ae012562", + "source_hash": "sha256:9a6bc544f78424a85d7465cbb8ce20ee80c954de7ec3f7657eb42b05679d663d", + "surface_hash": "sha256:beeddac7cd934e409e47e5b4552dbc188840ad46205d8d48340569a97c51b59b", "coverage": { "surface_products": 26, "products_with_metadata": 26, - "surface_tools": 847, - "tools_with_metadata": 847, - "tools_with_agent_summary": 847, - "tools_with_use_when": 847, - "tools_with_avoid_when": 847, - "tools_with_examples": 847, - "tools_with_interface_mode": 847, + "surface_tools": 850, + "tools_with_metadata": 850, + "tools_with_agent_summary": 850, + "tools_with_use_when": 850, + "tools_with_avoid_when": 850, + "tools_with_examples": 850, + "tools_with_interface_mode": 850, "unmatched_skill_tools": 122, "unreviewed_skill_tools": 11 }, diff --git a/internal/cli/schema_agent_metadata/oa.json b/internal/cli/schema_agent_metadata/oa.json index e2d172d8..9eef7eab 100644 --- a/internal/cli/schema_agent_metadata/oa.json +++ b/internal/cli/schema_agent_metadata/oa.json @@ -2077,6 +2077,290 @@ "已知 processInstanceId 与待办 taskId,用户明确要求同意该审批任务时" ] }, + "oa approval create-instance": { + "agent_summary": "发起新的审批实例", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起" + ], + "confirmation": "user_required", + "effect": "write", + "effect_source": "agent-hint", + "examples": [ + "dws oa approval create-instance --process-code \u003cprocessCode\u003e --form-values '{\"事由\":\"测试\"}'", + "dws oa approval create-instance --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[{\"name\":\"事由\",\"value\":\"测试\"}],\"targetSelectActioners\":[{\"actionerKey\":\"manual-node\",\"actionerStaffIds\":[\"user-id\"]}]}'" + ], + "field_provenance": { + "agent_summary": { + "value": "发起新的审批实例", + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "candidates": [ + { + "value": "发起新的审批实例", + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。" + } + ] + }, + "availability": { + "value": "available", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "candidates": [ + { + "value": "available", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。" + }, + { + "value": "available", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance.interface_ref", + "precedence": "mcp_fallback", + "selected": false + } + ] + }, + "avoid_when": { + "value": [ + "只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "candidates": [ + { + "value": [ + "只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。" + } + ] + }, + "confirmation": { + "value": "user_required", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "candidates": [ + { + "value": "user_required", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。" + }, + { + "value": "not_required", + "source": "risk-default", + "precedence": "inference_or_default", + "selected": false + } + ] + }, + "effect": { + "value": "write", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "candidates": [ + { + "value": "write", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。" + }, + { + "value": "write", + "source": "command-verb", + "precedence": "inference_or_default", + "selected": false + } + ] + }, + "examples": { + "value": [ + "dws oa approval create-instance --process-code \u003cprocessCode\u003e --form-values '{\"事由\":\"测试\"}'", + "dws oa approval create-instance --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[{\"name\":\"事由\",\"value\":\"测试\"}],\"targetSelectActioners\":[{\"actionerKey\":\"manual-node\",\"actionerStaffIds\":[\"user-id\"]}]}'" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "candidates": [ + { + "value": [ + "dws oa approval create-instance --process-code \u003cprocessCode\u003e --form-values '{\"事由\":\"测试\"}'", + "dws oa approval create-instance --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[{\"name\":\"事由\",\"value\":\"测试\"}],\"targetSelectActioners\":[{\"actionerKey\":\"manual-node\",\"actionerStaffIds\":[\"user-id\"]}]}'" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。" + } + ] + }, + "idempotency": { + "value": "non_idempotent", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "candidates": [ + { + "value": "non_idempotent", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。" + }, + { + "value": "unknown", + "source": "effect-default", + "precedence": "inference_or_default", + "selected": false + } + ] + }, + "interface_mode": { + "value": "mcp", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "candidates": [ + { + "value": "mcp", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。" + }, + { + "value": "mcp", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance.interface_ref", + "precedence": "mcp_fallback", + "selected": false + } + ] + }, + "interface_ref": { + "value": "oa.start_process_instance", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance.interface_ref", + "precedence": "mcp_fallback", + "resolution": "highest_precedence", + "candidates": [ + { + "value": "oa.start_process_instance", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance.interface_ref", + "precedence": "mcp_fallback", + "selected": true + } + ] + }, + "reviewed": { + "value": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "candidates": [ + { + "value": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。" + }, + { + "value": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": false, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。" + } + ] + }, + "risk": { + "value": "high", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "candidates": [ + { + "value": "high", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。" + }, + { + "value": "medium", + "source": "effect-default", + "precedence": "inference_or_default", + "selected": false + } + ] + }, + "use_when": { + "value": [ + "用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "candidates": [ + { + "value": [ + "用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。" + } + ] + } + }, + "idempotency": "non_idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "start_process_instance" + }, + "reviewed": true, + "risk": "high", + "source_refs": [ + "CommandRegistry:canonical_path=oa.start_process_instance", + "cobra-help:dws oa approval create-instance", + "internal/cli/schema_hints/metadata/oa.json", + "internal/cli/schema_hints/selection/oa.json", + "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance", + "live-mcp-tools-list:oa.start_process_instance", + "skills/mono/references/products/oa.md" + ], + "use_when": [ + "用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用" + ] + }, "oa approval detail": { "agent_summary": "获取指定审批实例的详情信息", "agent_summary_source": "dws-agent-selection/oa", @@ -2343,6 +2627,523 @@ "已知 processInstanceId,需要查看表单内容与当前状态详情时" ] }, + "oa approval forecast-process": { + "agent_summary": "预测审批流程与自选审批节点", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema" + ], + "confirmation": "not_required", + "effect": "read", + "effect_source": "agent-hint", + "examples": [ + "dws oa approval forecast-process --process-code \u003cprocessCode\u003e --dept-id -1 --form-values '{\"金额\":\"100\"}'", + "dws oa approval forecast-process --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[[{\"name\":\"金额\",\"value\":\"100\"}]]}'" + ], + "field_provenance": { + "agent_summary": { + "value": "预测审批流程与自选审批节点", + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "candidates": [ + { + "value": "预测审批流程与自选审批节点", + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。" + } + ] + }, + "availability": { + "value": "available", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "candidates": [ + { + "value": "available", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。" + }, + { + "value": "available", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process.interface_ref", + "precedence": "mcp_fallback", + "selected": false + } + ] + }, + "avoid_when": { + "value": [ + "需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "candidates": [ + { + "value": [ + "需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。" + } + ] + }, + "confirmation": { + "value": "not_required", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "candidates": [ + { + "value": "not_required", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。" + } + ] + }, + "effect": { + "value": "read", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "candidates": [ + { + "value": "read", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。" + } + ] + }, + "examples": { + "value": [ + "dws oa approval forecast-process --process-code \u003cprocessCode\u003e --dept-id -1 --form-values '{\"金额\":\"100\"}'", + "dws oa approval forecast-process --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[[{\"name\":\"金额\",\"value\":\"100\"}]]}'" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "candidates": [ + { + "value": [ + "dws oa approval forecast-process --process-code \u003cprocessCode\u003e --dept-id -1 --form-values '{\"金额\":\"100\"}'", + "dws oa approval forecast-process --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[[{\"name\":\"金额\",\"value\":\"100\"}]]}'" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。" + } + ] + }, + "idempotency": { + "value": "idempotent", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "candidates": [ + { + "value": "idempotent", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。" + } + ] + }, + "interface_mode": { + "value": "mcp", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "candidates": [ + { + "value": "mcp", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。" + }, + { + "value": "mcp", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process.interface_ref", + "precedence": "mcp_fallback", + "selected": false + } + ] + }, + "interface_ref": { + "value": "oa.forecast_process", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process.interface_ref", + "precedence": "mcp_fallback", + "resolution": "highest_precedence", + "candidates": [ + { + "value": "oa.forecast_process", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process.interface_ref", + "precedence": "mcp_fallback", + "selected": true + } + ] + }, + "reviewed": { + "value": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "candidates": [ + { + "value": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。" + }, + { + "value": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": false, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。" + } + ] + }, + "risk": { + "value": "low", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "candidates": [ + { + "value": "low", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。" + } + ] + }, + "use_when": { + "value": [ + "已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "candidates": [ + { + "value": [ + "已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。" + } + ] + } + }, + "idempotency": "idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "forecast_process" + }, + "reviewed": true, + "risk": "low", + "source_refs": [ + "CommandRegistry:canonical_path=oa.forecast_process", + "cobra-help:dws oa approval forecast-process", + "internal/cli/schema_hints/metadata/oa.json", + "internal/cli/schema_hints/selection/oa.json", + "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process", + "live-mcp-tools-list:oa.forecast_process", + "skills/mono/references/products/oa.md" + ], + "use_when": [ + "已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时" + ] + }, + "oa approval form-schema": { + "agent_summary": "查询审批模板的表单 Schema", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求" + ], + "confirmation": "not_required", + "effect": "read", + "effect_source": "agent-hint", + "examples": [ + "dws oa approval form-schema --process-code \u003cprocessCode\u003e" + ], + "field_provenance": { + "agent_summary": { + "value": "查询审批模板的表单 Schema", + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "candidates": [ + { + "value": "查询审批模板的表单 Schema", + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。" + } + ] + }, + "availability": { + "value": "available", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "candidates": [ + { + "value": "available", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。" + }, + { + "value": "available", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema.interface_ref", + "precedence": "mcp_fallback", + "selected": false + } + ] + }, + "avoid_when": { + "value": [ + "只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "candidates": [ + { + "value": [ + "只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。" + } + ] + }, + "confirmation": { + "value": "not_required", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "candidates": [ + { + "value": "not_required", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。" + } + ] + }, + "effect": { + "value": "read", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "candidates": [ + { + "value": "read", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。" + } + ] + }, + "examples": { + "value": [ + "dws oa approval form-schema --process-code \u003cprocessCode\u003e" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "candidates": [ + { + "value": [ + "dws oa approval form-schema --process-code \u003cprocessCode\u003e" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。" + } + ] + }, + "idempotency": { + "value": "idempotent", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "candidates": [ + { + "value": "idempotent", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。" + } + ] + }, + "interface_mode": { + "value": "mcp", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "candidates": [ + { + "value": "mcp", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。" + }, + { + "value": "mcp", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema.interface_ref", + "precedence": "mcp_fallback", + "selected": false + } + ] + }, + "interface_ref": { + "value": "oa.get_process_schema", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema.interface_ref", + "precedence": "mcp_fallback", + "resolution": "highest_precedence", + "candidates": [ + { + "value": "oa.get_process_schema", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema.interface_ref", + "precedence": "mcp_fallback", + "selected": true + } + ] + }, + "reviewed": { + "value": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "candidates": [ + { + "value": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。" + }, + { + "value": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": false, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。" + } + ] + }, + "risk": { + "value": "low", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "candidates": [ + { + "value": "low", + "source": "internal/cli/schema_hints/metadata/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。" + } + ] + }, + "use_when": { + "value": [ + "已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "candidates": [ + { + "value": [ + "已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时" + ], + "source": "internal/cli/schema_hints/selection/oa.json", + "precedence": "reviewed_explicit", + "selected": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。" + } + ] + } + }, + "idempotency": "idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "get_process_schema" + }, + "reviewed": true, + "risk": "low", + "source_refs": [ + "CommandRegistry:canonical_path=oa.get_process_schema", + "cobra-help:dws oa approval form-schema", + "internal/cli/schema_hints/metadata/oa.json", + "internal/cli/schema_hints/selection/oa.json", + "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema", + "live-mcp-tools-list:oa.get_process_schema", + "skills/mono/references/products/oa.md" + ], + "use_when": [ + "已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时" + ] + }, "oa approval list-cc": { "agent_summary": "获取抄送用户的列表", "agent_summary_source": "dws-agent-selection/oa", diff --git a/internal/cli/schema_agent_metadata_audit.json b/internal/cli/schema_agent_metadata_audit.json index 300c3a6b..18c22245 100644 --- a/internal/cli/schema_agent_metadata_audit.json +++ b/internal/cli/schema_agent_metadata_audit.json @@ -1,20 +1,20 @@ { "version": 1, - "source_hash": "sha256:1f76aa71b00f7b807b863ced6e98aca76dc4bda65107ea2a6c67eb4ec629db43", - "surface_hash": "sha256:89bc72d0a5ff03df634d733dc8b71c3010572524542538552c33c657ae012562", - "source_files": 160, + "source_hash": "sha256:9a6bc544f78424a85d7465cbb8ce20ee80c954de7ec3f7657eb42b05679d663d", + "surface_hash": "sha256:beeddac7cd934e409e47e5b4552dbc188840ad46205d8d48340569a97c51b59b", + "source_files": 162, "hint_files": 54, "hint_products": 43, - "hint_tools": 1846, + "hint_tools": 1852, "interface_metadata": { "source": "mcp-tools-list+cli-registry", "revision": "4574f7022c32cf4c033e9b7b4156e2fec815fed8", "source_hash": "sha256:17251f74a4f76142457cc0e87251ecb86c1e3bda0cdf2edc02f351589716ecbc", - "source_tools": 420, - "surface_tools": 415, - "eligible_summaries": 414, + "source_tools": 423, + "surface_tools": 418, + "eligible_summaries": 417, "applied_summaries": 0, - "preserved_summaries": 414, + "preserved_summaries": 417, "rejected_tools": [ "chat.unread_message_conversation_list" ], @@ -29,13 +29,13 @@ "coverage": { "surface_products": 26, "products_with_metadata": 26, - "surface_tools": 847, - "tools_with_metadata": 847, - "tools_with_agent_summary": 847, - "tools_with_use_when": 847, - "tools_with_avoid_when": 847, - "tools_with_examples": 847, - "tools_with_interface_mode": 847, + "surface_tools": 850, + "tools_with_metadata": 850, + "tools_with_agent_summary": 850, + "tools_with_use_when": 850, + "tools_with_avoid_when": 850, + "tools_with_examples": 850, + "tools_with_interface_mode": 850, "unmatched_skill_tools": 122, "unreviewed_skill_tools": 11 }, @@ -3671,8 +3671,8 @@ "line": 114, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3685,8 +3685,8 @@ "line": 115, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3696,11 +3696,11 @@ { "tool_path": "oa approval ding-info", "source": "skills/mono/references/products/oa.md", - "line": 128, + "line": 485, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3710,11 +3710,11 @@ { "tool_path": "oa approval ding-info", "source": "skills/mono/references/products/oa.md", - "line": 149, + "line": 506, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3724,11 +3724,11 @@ { "tool_path": "oa approval revert-activities", "source": "skills/mono/references/products/oa.md", - "line": 164, + "line": 521, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3738,11 +3738,11 @@ { "tool_path": "oa approval append-task", "source": "skills/mono/references/products/oa.md", - "line": 276, + "line": 633, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3752,11 +3752,11 @@ { "tool_path": "oa approval append-task", "source": "skills/mono/references/products/oa.md", - "line": 277, + "line": 634, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3766,11 +3766,11 @@ { "tool_path": "oa approval revert-task", "source": "skills/mono/references/products/oa.md", - "line": 297, + "line": 654, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3780,11 +3780,67 @@ { "tool_path": "oa approval revert-task", "source": "skills/mono/references/products/oa.md", - "line": 299, + "line": 656, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" + ], + "review": { + "status": "stale", + "reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射" + } + }, + { + "tool_path": "oa approval search-forms", + "source": "skills/mono/references/products/oa.md", + "line": 676, + "candidates": [ + "oa approval approve", + "oa approval create-instance", + "oa approval detail" + ], + "review": { + "status": "stale", + "reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射" + } + }, + { + "tool_path": "oa approval search-forms", + "source": "skills/mono/references/products/oa.md", + "line": 682, + "candidates": [ + "oa approval approve", + "oa approval create-instance", + "oa approval detail" + ], + "review": { + "status": "stale", + "reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射" + } + }, + { + "tool_path": "oa approval search-forms", + "source": "skills/mono/references/products/oa.md", + "line": 724, + "candidates": [ + "oa approval approve", + "oa approval create-instance", + "oa approval detail" + ], + "review": { + "status": "stale", + "reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射" + } + }, + { + "tool_path": "oa approval search-forms", + "source": "skills/mono/references/products/oa.md", + "line": 754, + "candidates": [ + "oa approval approve", + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "stale", @@ -3839,8 +3895,8 @@ "line": 102, "candidates": [ "oa approval approve", - "oa approval detail", - "oa approval list-cc" + "oa approval create-instance", + "oa approval detail" ], "review": { "status": "group", diff --git a/internal/cli/schema_catalog/catalog.json b/internal/cli/schema_catalog/catalog.json index 011f2544..59fd40fc 100644 --- a/internal/cli/schema_catalog/catalog.json +++ b/internal/cli/schema_catalog/catalog.json @@ -1,17 +1,17 @@ { "version": 1, - "surface_hash": "sha256:89bc72d0a5ff03df634d733dc8b71c3010572524542538552c33c657ae012562", - "source_hash": "sha256:ccdbef2cd88ade9fce277b4dfc9ef433724cf54f9ce6676b77169e99c9944f45", + "surface_hash": "sha256:beeddac7cd934e409e47e5b4552dbc188840ad46205d8d48340569a97c51b59b", + "source_hash": "sha256:6fe48c19b11a2d917fad3d59c96b40bf1b7620ed4334d96cef6544dbe3742e30", "catalog": { "agent_metadata": { "products_with_metadata": 26, "source": "embedded-skill-metadata", - "source_hash": "sha256:1f76aa71b00f7b807b863ced6e98aca76dc4bda65107ea2a6c67eb4ec629db43", - "surface_hash": "sha256:89bc72d0a5ff03df634d733dc8b71c3010572524542538552c33c657ae012562", + "source_hash": "sha256:9a6bc544f78424a85d7465cbb8ce20ee80c954de7ec3f7657eb42b05679d663d", + "surface_hash": "sha256:beeddac7cd934e409e47e5b4552dbc188840ad46205d8d48340569a97c51b59b", "surface_products": 26, - "surface_tools": 847, - "tools_with_agent_summary": 847, - "tools_with_metadata": 847, + "surface_tools": 850, + "tools_with_agent_summary": 850, + "tools_with_metadata": 850, "unmatched_skill_tools": 122, "version": 1 }, @@ -33,7 +33,7 @@ "source": "mcp-tools-list+cli-registry", "source_hash": "sha256:17251f74a4f76142457cc0e87251ecb86c1e3bda0cdf2edc02f351589716ecbc", "source_revision": "4574f7022c32cf4c033e9b7b4156e2fec815fed8", - "tool_count": 420, + "tool_count": 423, "version": 1 }, "kind": "schema", @@ -21117,7 +21117,7 @@ "id": "oa", "name": "OA 审批 / 同意 / 拒绝 / 撤销", "runtime": true, - "tool_count": 22, + "tool_count": 25, "tools": [ { "agent_metadata_source": "embedded-skill-metadata", @@ -21183,6 +21183,37 @@ "已知 processInstanceId,需要为审批实例添加评论文本时" ] }, + { + "agent_metadata_source": "embedded-skill-metadata", + "agent_summary": "预测审批流程与自选审批节点", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema" + ], + "canonical_path": "oa.forecast_process", + "cli_name": "forecast-process", + "cli_path": "oa approval forecast-process", + "confirmation": "not_required", + "description": "根据表单值预测审批流程与自选节点", + "effect": "read", + "group": "approval", + "idempotency": "idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "forecast_process" + }, + "metadata_source": "embedded-mcp-metadata", + "name": "forecast_process", + "primary_cli_path": "oa approval forecast-process", + "reviewed": true, + "risk": "low", + "title": "根据表单值预测审批流程与自选节点", + "use_when": [ + "已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时" + ] + }, { "agent_metadata_source": "embedded-skill-metadata", "agent_summary": "获取员工已处理任务列表", @@ -21311,6 +21342,37 @@ "已知 processInstanceId,需要查看谁做了什么审批操作及结果时" ] }, + { + "agent_metadata_source": "embedded-skill-metadata", + "agent_summary": "查询审批模板的表单 Schema", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求" + ], + "canonical_path": "oa.get_process_schema", + "cli_name": "form-schema", + "cli_path": "oa approval form-schema", + "confirmation": "not_required", + "description": "查询审批模板的表单 Schema", + "effect": "read", + "group": "approval", + "idempotency": "idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "get_process_schema" + }, + "metadata_source": "embedded-mcp-metadata", + "name": "get_process_schema", + "primary_cli_path": "oa approval form-schema", + "reviewed": true, + "risk": "low", + "title": "查询审批模板的表单 Schema", + "use_when": [ + "已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时" + ] + }, { "agent_metadata_source": "embedded-skill-metadata", "agent_summary": "获取已提交实例列表", @@ -21779,6 +21841,37 @@ "use_when": [ "当你已知想找的审批大致名称(如「报销」「请假」)、想快速定位对应表单及其 processCode 时使用,比 +list-forms 全量列举更高效;传入关键字,返回名称或 processCode 匹配的表单,供后续 +list-initiated 按模板查询。" ] + }, + { + "agent_metadata_source": "embedded-skill-metadata", + "agent_summary": "发起新的审批实例", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起" + ], + "canonical_path": "oa.start_process_instance", + "cli_name": "create-instance", + "cli_path": "oa approval create-instance", + "confirmation": "user_required", + "description": "发起审批实例(需要 --yes 确认)", + "effect": "write", + "group": "approval", + "idempotency": "non_idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "start_process_instance" + }, + "metadata_source": "embedded-mcp-metadata", + "name": "start_process_instance", + "primary_cli_path": "oa approval create-instance", + "reviewed": true, + "risk": "high", + "title": "发起审批实例(需要 --yes 确认)", + "use_when": [ + "用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用" + ] } ], "use_when": [ @@ -26704,6 +26797,6 @@ } ], "source": "embedded-command-catalog", - "tool_count": 847 + "tool_count": 850 } } diff --git a/internal/cli/schema_catalog/tools/oa.json b/internal/cli/schema_catalog/tools/oa.json index 1378ed07..887f8f99 100644 --- a/internal/cli/schema_catalog/tools/oa.json +++ b/internal/cli/schema_catalog/tools/oa.json @@ -1257,6 +1257,799 @@ "已知 processInstanceId,需要为审批实例添加评论文本时" ] }, + "oa.forecast_process": { + "agent_metadata_source": "embedded-skill-metadata", + "agent_source_refs": [ + "CommandRegistry:canonical_path=oa.forecast_process", + "cobra-help:dws oa approval forecast-process", + "internal/cli/schema_hints/metadata/oa.json", + "internal/cli/schema_hints/selection/oa.json", + "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process", + "live-mcp-tools-list:oa.forecast_process", + "skills/mono/references/products/oa.md" + ], + "agent_summary": "预测审批流程与自选审批节点", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema" + ], + "canonical_path": "oa.forecast_process", + "cli_name": "forecast-process", + "cli_path": "oa approval forecast-process", + "confirmation": "not_required", + "constraints": { + "mutually_exclusive": [ + [ + "request", + "process-code" + ], + [ + "request", + "dept-id" + ], + [ + "request", + "form-values" + ] + ], + "require_one_of": [ + [ + "request", + "process-code" + ] + ], + "require_together": [ + [ + "process-code", + "dept-id", + "form-values" + ] + ] + }, + "description": "根据表单值预测审批流程与自选节点", + "display": "OA 审批 / 同意 / 拒绝 / 撤销", + "effect": "read", + "effect_source": "agent-hint", + "examples": [ + "dws oa approval forecast-process --process-code \u003cprocessCode\u003e --dept-id -1 --form-values '{\"金额\":\"100\"}'", + "dws oa approval forecast-process --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[[{\"name\":\"金额\",\"value\":\"100\"}]]}'" + ], + "field_provenance": { + "agent_summary": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": "预测审批流程与自选审批节点" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": "预测审批流程与自选审批节点" + }, + "availability": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "available" + }, + { + "precedence": "mcp_fallback", + "selected": false, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process.interface_ref", + "value": "available" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "available" + }, + "avoid_when": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema" + ] + }, + "canonical_path": { + "candidates": [ + { + "precedence": "command_registry", + "selected": true, + "source": "reviewed_command_registry", + "source_ref": "oa approval forecast-process", + "value": "oa.forecast_process" + } + ], + "precedence": "command_registry", + "resolution": "registry_identity", + "source": "reviewed_command_registry", + "source_ref": "oa approval forecast-process", + "value": "oa.forecast_process" + }, + "confirmation": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "not_required" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "not_required" + }, + "description": { + "candidates": [ + { + "precedence": "cobra_help", + "selected": true, + "source": "cobra_help", + "value": "根据表单值预测审批流程与自选节点" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "审批流程预测" + } + ], + "precedence": "cobra_help", + "resolution": "highest_precedence", + "source": "cobra_help", + "value": "根据表单值预测审批流程与自选节点" + }, + "effect": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "read" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "read" + }, + "examples": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "dws oa approval forecast-process --process-code \u003cprocessCode\u003e --dept-id -1 --form-values '{\"金额\":\"100\"}'", + "dws oa approval forecast-process --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[[{\"name\":\"金额\",\"value\":\"100\"}]]}'" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "dws oa approval forecast-process --process-code \u003cprocessCode\u003e --dept-id -1 --form-values '{\"金额\":\"100\"}'", + "dws oa approval forecast-process --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[[{\"name\":\"金额\",\"value\":\"100\"}]]}'" + ] + }, + "idempotency": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "idempotent" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "idempotent" + }, + "interface_mode": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "mcp" + }, + { + "precedence": "mcp_fallback", + "selected": false, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process.interface_ref", + "value": "mcp" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "mcp" + }, + "interface_ref": { + "candidates": [ + { + "precedence": "mcp_fallback", + "selected": true, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process.interface_ref", + "value": { + "product_id": "oa", + "rpc_name": "forecast_process" + } + } + ], + "precedence": "mcp_fallback", + "resolution": "highest_precedence", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.forecast_process.interface_ref", + "value": { + "product_id": "oa", + "rpc_name": "forecast_process" + } + }, + "metadata_source": { + "candidates": [ + { + "precedence": "derived_resolution", + "selected": true, + "source": "metadata_source_resolution", + "value": "embedded-mcp-metadata" + } + ], + "precedence": "derived_resolution", + "resolution": "highest_precedence", + "source": "metadata_source_resolution", + "value": "embedded-mcp-metadata" + }, + "reviewed": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": true + }, + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "selected": false, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": true + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": true + }, + "risk": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "low" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "low" + }, + "title": { + "candidates": [ + { + "precedence": "cobra_help", + "selected": true, + "source": "cobra_help", + "value": "根据表单值预测审批流程与自选节点" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "forecast_process" + } + ], + "precedence": "cobra_help", + "resolution": "highest_precedence", + "source": "cobra_help", + "value": "根据表单值预测审批流程与自选节点" + }, + "use_when": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时" + ] + } + }, + "group": "approval", + "has_parameters": true, + "idempotency": "idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "forecast_process" + }, + "is_alias": false, + "metadata_source": "embedded-mcp-metadata", + "name": "forecast_process", + "parameter_count": 4, + "parameters": { + "dept-id": { + "description": "发起人部门 ID(简单模式使用;与 --request 互斥)", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "发起人部门 ID(简单模式使用;与 --request 互斥)" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "发起人部门 ID(简单模式使用;与 --request 互斥)" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --dept-id is encoded inside ProcessForecastPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "deptId" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --dept-id is encoded inside ProcessForecastPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": false + } + ], + "precedence": "default", + "resolution": "fallback", + "source": "default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "form-values": { + "description": "表单值 JSON(简单模式使用;与 --request 互斥)", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "表单值 JSON(简单模式使用;与 --request 互斥)" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "表单值 JSON(简单模式使用;与 --request 互斥)" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --form-values is transformed into formComponentValues inside ProcessForecastPopRequest, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "formValues" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --form-values is transformed into formComponentValues inside ProcessForecastPopRequest, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": false + } + ], + "precedence": "default", + "resolution": "fallback", + "source": "default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "process-code": { + "description": "审批模板 processCode(简单模式使用;与 --request 互斥)", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "审批模板 processCode(简单模式使用;与 --request 互斥)" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "审批模板 processCode(简单模式使用;与 --request 互斥)" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --process-code is encoded inside ProcessForecastPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "processCode" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --process-code is encoded inside ProcessForecastPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "command_constraint", + "selected": true, + "source": "require_one_of_constraint", + "value": false + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": false + } + ], + "precedence": "command_constraint", + "resolution": "highest_precedence", + "source": "require_one_of_constraint", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "request": { + "description": "完整请求 JSON(高级模式;与简单模式参数互斥)", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "完整请求 JSON(高级模式;与简单模式参数互斥)" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "ProcessForecastPopRequest" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "完整请求 JSON(高级模式;与简单模式参数互斥)" + }, + "interface_type": { + "candidates": [ + { + "precedence": "mcp_metadata", + "selected": true, + "source": "mcp_metadata", + "value": "object" + }, + { + "precedence": "fallback", + "selected": false, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "mcp_metadata", + "resolution": "highest_precedence", + "source": "mcp_metadata", + "value": "object" + }, + "property": { + "candidates": [ + { + "precedence": "versioned_binding", + "selected": true, + "source": "versioned_parameter_binding", + "value": "ProcessForecastPopRequest" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "request" + } + ], + "precedence": "versioned_binding", + "resolution": "highest_precedence", + "source": "versioned_parameter_binding", + "value": "ProcessForecastPopRequest" + }, + "required": { + "candidates": [ + { + "precedence": "command_constraint", + "selected": true, + "source": "require_one_of_constraint", + "value": false + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": false + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": false + } + ], + "precedence": "command_constraint", + "resolution": "highest_precedence", + "source": "require_one_of_constraint", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "interface_description": "ProcessForecastPopRequest", + "interface_type": "object", + "property": "ProcessForecastPopRequest", + "required": false, + "type": "string" + } + }, + "path": "oa.forecast_process", + "primary_cli_path": "oa approval forecast-process", + "product_id": "oa", + "reviewed": true, + "risk": "low", + "source": "reviewed_command_registry", + "title": "根据表单值预测审批流程与自选节点", + "use_when": [ + "已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时" + ] + }, "oa.get_done_tasks": { "agent_metadata_source": "embedded-skill-metadata", "agent_source_refs": [ @@ -3607,6 +4400,461 @@ "已知 processInstanceId,需要查看谁做了什么审批操作及结果时" ] }, + "oa.get_process_schema": { + "agent_metadata_source": "embedded-skill-metadata", + "agent_source_refs": [ + "CommandRegistry:canonical_path=oa.get_process_schema", + "cobra-help:dws oa approval form-schema", + "internal/cli/schema_hints/metadata/oa.json", + "internal/cli/schema_hints/selection/oa.json", + "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema", + "live-mcp-tools-list:oa.get_process_schema", + "skills/mono/references/products/oa.md" + ], + "agent_summary": "查询审批模板的表单 Schema", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求" + ], + "canonical_path": "oa.get_process_schema", + "cli_name": "form-schema", + "cli_path": "oa approval form-schema", + "confirmation": "not_required", + "description": "查询审批模板的表单 Schema", + "display": "OA 审批 / 同意 / 拒绝 / 撤销", + "effect": "read", + "effect_source": "agent-hint", + "examples": [ + "dws oa approval form-schema --process-code \u003cprocessCode\u003e" + ], + "field_provenance": { + "agent_summary": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": "查询审批模板的表单 Schema" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": "查询审批模板的表单 Schema" + }, + "availability": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "available" + }, + { + "precedence": "mcp_fallback", + "selected": false, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema.interface_ref", + "value": "available" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "available" + }, + "avoid_when": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求" + ] + }, + "canonical_path": { + "candidates": [ + { + "precedence": "command_registry", + "selected": true, + "source": "reviewed_command_registry", + "source_ref": "oa approval form-schema", + "value": "oa.get_process_schema" + } + ], + "precedence": "command_registry", + "resolution": "registry_identity", + "source": "reviewed_command_registry", + "source_ref": "oa approval form-schema", + "value": "oa.get_process_schema" + }, + "confirmation": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "not_required" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "not_required" + }, + "description": { + "candidates": [ + { + "precedence": "cobra_help", + "selected": true, + "source": "cobra_help", + "value": "查询审批模板的表单 Schema" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "根据表单的processCode,获取该表单的 schema 信息" + } + ], + "precedence": "cobra_help", + "resolution": "highest_precedence", + "source": "cobra_help", + "value": "查询审批模板的表单 Schema" + }, + "effect": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "read" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "read" + }, + "examples": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "dws oa approval form-schema --process-code \u003cprocessCode\u003e" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "dws oa approval form-schema --process-code \u003cprocessCode\u003e" + ] + }, + "idempotency": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "idempotent" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "idempotent" + }, + "interface_mode": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "mcp" + }, + { + "precedence": "mcp_fallback", + "selected": false, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema.interface_ref", + "value": "mcp" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "mcp" + }, + "interface_ref": { + "candidates": [ + { + "precedence": "mcp_fallback", + "selected": true, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema.interface_ref", + "value": { + "product_id": "oa", + "rpc_name": "get_process_schema" + } + } + ], + "precedence": "mcp_fallback", + "resolution": "highest_precedence", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.get_process_schema.interface_ref", + "value": { + "product_id": "oa", + "rpc_name": "get_process_schema" + } + }, + "metadata_source": { + "candidates": [ + { + "precedence": "derived_resolution", + "selected": true, + "source": "metadata_source_resolution", + "value": "embedded-mcp-metadata" + } + ], + "precedence": "derived_resolution", + "resolution": "highest_precedence", + "source": "metadata_source_resolution", + "value": "embedded-mcp-metadata" + }, + "reviewed": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": true + }, + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "selected": false, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": true + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": true + }, + "risk": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "low" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "low" + }, + "title": { + "candidates": [ + { + "precedence": "cobra_help", + "selected": true, + "source": "cobra_help", + "value": "查询审批模板的表单 Schema" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "get_process_schema" + } + ], + "precedence": "cobra_help", + "resolution": "highest_precedence", + "source": "cobra_help", + "value": "查询审批模板的表单 Schema" + }, + "use_when": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时" + ] + } + }, + "group": "approval", + "has_parameters": true, + "idempotency": "idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "get_process_schema" + }, + "is_alias": false, + "metadata_source": "embedded-mcp-metadata", + "name": "get_process_schema", + "parameter_count": 1, + "parameters": { + "process-code": { + "description": "审批模板 processCode (必填)", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "审批模板 processCode (必填)" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "需要查询schema信息的表单processCode" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "审批模板 processCode (必填)" + }, + "property": { + "candidates": [ + { + "precedence": "inference", + "selected": true, + "source": "flag_name_inference", + "value": "processCode" + } + ], + "precedence": "inference", + "resolution": "highest_precedence", + "source": "flag_name_inference", + "value": "processCode" + }, + "required": { + "candidates": [ + { + "precedence": "mcp_metadata", + "selected": true, + "source": "mcp_metadata", + "value": true + }, + { + "precedence": "inference", + "selected": false, + "source": "usage_required_inference", + "value": true + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": false + } + ], + "precedence": "mcp_metadata", + "resolution": "highest_precedence", + "source": "mcp_metadata", + "value": true + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "interface_description": "需要查询schema信息的表单processCode", + "property": "processCode", + "required": true, + "type": "string" + } + }, + "path": "oa.get_process_schema", + "primary_cli_path": "oa approval form-schema", + "product_id": "oa", + "reviewed": true, + "risk": "low", + "source": "reviewed_command_registry", + "title": "查询审批模板的表单 Schema", + "use_when": [ + "已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时" + ] + }, "oa.get_submitted_instances": { "agent_metadata_source": "embedded-skill-metadata", "agent_source_refs": [ @@ -14074,6 +15322,1318 @@ "use_when": [ "当你已知想找的审批大致名称(如「报销」「请假」)、想快速定位对应表单及其 processCode 时使用,比 +list-forms 全量列举更高效;传入关键字,返回名称或 processCode 匹配的表单,供后续 +list-initiated 按模板查询。" ] + }, + "oa.start_process_instance": { + "agent_metadata_source": "embedded-skill-metadata", + "agent_source_refs": [ + "CommandRegistry:canonical_path=oa.start_process_instance", + "cobra-help:dws oa approval create-instance", + "internal/cli/schema_hints/metadata/oa.json", + "internal/cli/schema_hints/selection/oa.json", + "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance", + "live-mcp-tools-list:oa.start_process_instance", + "skills/mono/references/products/oa.md" + ], + "agent_summary": "发起新的审批实例", + "agent_summary_source": "dws-agent-selection/oa", + "availability": "available", + "avoid_when": [ + "只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起" + ], + "canonical_path": "oa.start_process_instance", + "cli_name": "create-instance", + "cli_path": "oa approval create-instance", + "confirmation": "user_required", + "constraints": { + "mutually_exclusive": [ + [ + "request", + "process-code" + ], + [ + "request", + "dept-id" + ], + [ + "request", + "form-values" + ], + [ + "request", + "originator-user-id" + ], + [ + "request", + "approvers" + ], + [ + "request", + "approvers-action-type" + ], + [ + "request", + "cc-list" + ], + [ + "request", + "cc-position" + ] + ], + "require_one_of": [ + [ + "request", + "process-code" + ] + ], + "require_together": [ + [ + "process-code", + "form-values" + ] + ] + }, + "description": "发起审批实例(需要 --yes 确认)", + "display": "OA 审批 / 同意 / 拒绝 / 撤销", + "effect": "write", + "effect_source": "agent-hint", + "examples": [ + "dws oa approval create-instance --process-code \u003cprocessCode\u003e --form-values '{\"事由\":\"测试\"}'", + "dws oa approval create-instance --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[{\"name\":\"事由\",\"value\":\"测试\"}],\"targetSelectActioners\":[{\"actionerKey\":\"manual-node\",\"actionerStaffIds\":[\"user-id\"]}]}'" + ], + "field_provenance": { + "agent_summary": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": "发起新的审批实例" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": "发起新的审批实例" + }, + "availability": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "available" + }, + { + "precedence": "mcp_fallback", + "selected": false, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance.interface_ref", + "value": "available" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "available" + }, + "avoid_when": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起" + ] + }, + "canonical_path": { + "candidates": [ + { + "precedence": "command_registry", + "selected": true, + "source": "reviewed_command_registry", + "source_ref": "oa approval create-instance", + "value": "oa.start_process_instance" + } + ], + "precedence": "command_registry", + "resolution": "registry_identity", + "source": "reviewed_command_registry", + "source_ref": "oa approval create-instance", + "value": "oa.start_process_instance" + }, + "confirmation": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "user_required" + }, + { + "precedence": "inference_or_default", + "selected": false, + "source": "risk-default", + "value": "not_required" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "user_required" + }, + "description": { + "candidates": [ + { + "precedence": "cobra_help", + "selected": true, + "source": "cobra_help", + "value": "发起审批实例(需要 --yes 确认)" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "根据用户输入审批表单内容、流程、抄送人等信息发起审批" + } + ], + "precedence": "cobra_help", + "resolution": "highest_precedence", + "source": "cobra_help", + "value": "发起审批实例(需要 --yes 确认)" + }, + "effect": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "write" + }, + { + "precedence": "inference_or_default", + "selected": false, + "source": "command-verb", + "value": "write" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "write" + }, + "examples": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "dws oa approval create-instance --process-code \u003cprocessCode\u003e --form-values '{\"事由\":\"测试\"}'", + "dws oa approval create-instance --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[{\"name\":\"事由\",\"value\":\"测试\"}],\"targetSelectActioners\":[{\"actionerKey\":\"manual-node\",\"actionerStaffIds\":[\"user-id\"]}]}'" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "dws oa approval create-instance --process-code \u003cprocessCode\u003e --form-values '{\"事由\":\"测试\"}'", + "dws oa approval create-instance --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[{\"name\":\"事由\",\"value\":\"测试\"}],\"targetSelectActioners\":[{\"actionerKey\":\"manual-node\",\"actionerStaffIds\":[\"user-id\"]}]}'" + ] + }, + "idempotency": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "non_idempotent" + }, + { + "precedence": "inference_or_default", + "selected": false, + "source": "effect-default", + "value": "unknown" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "non_idempotent" + }, + "interface_mode": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "mcp" + }, + { + "precedence": "mcp_fallback", + "selected": false, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance.interface_ref", + "value": "mcp" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "mcp" + }, + "interface_ref": { + "candidates": [ + { + "precedence": "mcp_fallback", + "selected": true, + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance.interface_ref", + "value": { + "product_id": "oa", + "rpc_name": "start_process_instance" + } + } + ], + "precedence": "mcp_fallback", + "resolution": "highest_precedence", + "source": "internal/cli/schema_mcp_metadata.json#tools.oa.start_process_instance.interface_ref", + "value": { + "product_id": "oa", + "rpc_name": "start_process_instance" + } + }, + "metadata_source": { + "candidates": [ + { + "precedence": "derived_resolution", + "selected": true, + "source": "metadata_source_resolution", + "value": "embedded-mcp-metadata" + } + ], + "precedence": "derived_resolution", + "resolution": "highest_precedence", + "source": "metadata_source_resolution", + "value": "embedded-mcp-metadata" + }, + "reviewed": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": true + }, + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "selected": false, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": true + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": true + }, + "risk": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "selected": true, + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "high" + }, + { + "precedence": "inference_or_default", + "selected": false, + "source": "effect-default", + "value": "medium" + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。", + "source": "internal/cli/schema_hints/metadata/oa.json", + "value": "high" + }, + "title": { + "candidates": [ + { + "precedence": "cobra_help", + "selected": true, + "source": "cobra_help", + "value": "发起审批实例(需要 --yes 确认)" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "审批发起表单实例" + } + ], + "precedence": "cobra_help", + "resolution": "highest_precedence", + "source": "cobra_help", + "value": "发起审批实例(需要 --yes 确认)" + }, + "use_when": { + "candidates": [ + { + "precedence": "reviewed_explicit", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "selected": true, + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用" + ] + } + ], + "precedence": "reviewed_explicit", + "resolution": "highest_precedence", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "source": "internal/cli/schema_hints/selection/oa.json", + "value": [ + "用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用" + ] + } + }, + "group": "approval", + "has_parameters": true, + "idempotency": "non_idempotent", + "interface_mode": "mcp", + "interface_ref": { + "product_id": "oa", + "rpc_name": "start_process_instance" + }, + "is_alias": false, + "metadata_source": "embedded-mcp-metadata", + "name": "start_process_instance", + "parameter_count": 9, + "parameters": { + "approvers": { + "description": "审批人 userId 列表,多个用逗号分隔", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "审批人 userId 列表,多个用逗号分隔" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "审批人 userId 列表,多个用逗号分隔" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --approvers is transformed into an approvers array inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "approvers" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --approvers is transformed into an approvers array inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": false + } + ], + "precedence": "default", + "resolution": "fallback", + "source": "default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "approvers-action-type": { + "default": "OR", + "description": "审批类型:AND、OR 或 NONE", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "审批类型:AND、OR 或 NONE" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "审批类型:AND、OR 或 NONE" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --approvers-action-type only configures the generated approvers array inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "approversActionType" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --approvers-action-type only configures the generated approvers array inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_nonzero_default", + "value": false + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": false + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_nonzero_default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "cc-list": { + "description": "抄送人 userId 列表,多个用逗号分隔", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "抄送人 userId 列表,多个用逗号分隔" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "抄送人 userId 列表,多个用逗号分隔" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --cc-list is encoded inside ProcessInstanceCreationPopRequest only when supplied, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "ccList" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --cc-list is encoded inside ProcessInstanceCreationPopRequest only when supplied, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": false + } + ], + "precedence": "default", + "resolution": "fallback", + "source": "default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "cc-position": { + "default": "START", + "description": "抄送时点:START、FINISH 或 START_FINISH", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "抄送时点:START、FINISH 或 START_FINISH" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "抄送时点:START、FINISH 或 START_FINISH" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --cc-position only configures the generated ccList inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "ccPosition" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --cc-position only configures the generated ccList inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_nonzero_default", + "value": false + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": false + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_nonzero_default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "dept-id": { + "default": "-1", + "description": "发起人部门 ID", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "发起人部门 ID" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "发起人部门 ID" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --dept-id is encoded inside ProcessInstanceCreationPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "deptId" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --dept-id is encoded inside ProcessInstanceCreationPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_nonzero_default", + "value": false + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": false + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_nonzero_default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "form-values": { + "description": "表单值 JSON(简单模式使用;与 --request 互斥)", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "表单值 JSON(简单模式使用;与 --request 互斥)" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "表单值 JSON(简单模式使用;与 --request 互斥)" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --form-values is transformed into formComponentValues inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "formValues" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --form-values is transformed into formComponentValues inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": false + } + ], + "precedence": "default", + "resolution": "fallback", + "source": "default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "originator-user-id": { + "description": "审批发起人 userId", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "审批发起人 userId" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "审批发起人 userId" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --originator-user-id is encoded inside ProcessInstanceCreationPopRequest only when supplied, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "originatorUserId" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --originator-user-id is encoded inside ProcessInstanceCreationPopRequest only when supplied, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": false + } + ], + "precedence": "default", + "resolution": "fallback", + "source": "default", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "process-code": { + "description": "审批模板 processCode(简单模式使用;与 --request 互斥)", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "审批模板 processCode(简单模式使用;与 --request 互斥)" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "审批模板 processCode(简单模式使用;与 --request 互斥)" + }, + "property": { + "candidates": [ + { + "precedence": "reviewed_mapping_exclusion", + "review_reason": "Conditional request wrapper: --process-code is encoded inside ProcessInstanceCreationPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "selected": true, + "source": "reviewed_mapping_exclusion", + "value": "" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "processCode" + } + ], + "precedence": "reviewed_mapping_exclusion", + "resolution": "highest_precedence", + "review_reason": "Conditional request wrapper: --process-code is encoded inside ProcessInstanceCreationPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "source": "reviewed_mapping_exclusion", + "value": "" + }, + "required": { + "candidates": [ + { + "precedence": "command_constraint", + "selected": true, + "source": "require_one_of_constraint", + "value": false + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": false + } + ], + "precedence": "command_constraint", + "resolution": "highest_precedence", + "source": "require_one_of_constraint", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "required": false, + "type": "string" + }, + "request": { + "description": "完整请求 JSON(高级模式;与简单模式参数互斥)", + "field_provenance": { + "description": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_usage", + "value": "完整请求 JSON(高级模式;与简单模式参数互斥)" + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": "ProcessInstanceCreationPopRequest" + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": "" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_usage", + "value": "完整请求 JSON(高级模式;与简单模式参数互斥)" + }, + "interface_type": { + "candidates": [ + { + "precedence": "mcp_metadata", + "selected": true, + "source": "mcp_metadata", + "value": "object" + }, + { + "precedence": "fallback", + "selected": false, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "mcp_metadata", + "resolution": "highest_precedence", + "source": "mcp_metadata", + "value": "object" + }, + "property": { + "candidates": [ + { + "precedence": "versioned_binding", + "selected": true, + "source": "versioned_parameter_binding", + "value": "ProcessInstanceCreationPopRequest" + }, + { + "precedence": "inference", + "selected": false, + "source": "flag_name_inference", + "value": "request" + } + ], + "precedence": "versioned_binding", + "resolution": "highest_precedence", + "source": "versioned_parameter_binding", + "value": "ProcessInstanceCreationPopRequest" + }, + "required": { + "candidates": [ + { + "precedence": "command_constraint", + "selected": true, + "source": "require_one_of_constraint", + "value": false + }, + { + "precedence": "mcp_metadata", + "selected": false, + "source": "mcp_metadata", + "value": false + }, + { + "precedence": "default", + "selected": false, + "source": "default", + "value": false + } + ], + "precedence": "command_constraint", + "resolution": "highest_precedence", + "source": "require_one_of_constraint", + "value": false + }, + "required_when": { + "candidates": [ + { + "precedence": "default", + "selected": true, + "source": "default", + "value": "" + } + ], + "precedence": "default", + "resolution": "highest_precedence", + "source": "default", + "value": "" + }, + "type": { + "candidates": [ + { + "precedence": "cobra_contract", + "selected": true, + "source": "cobra_flag_type", + "value": "string" + } + ], + "precedence": "cobra_contract", + "resolution": "highest_precedence", + "source": "cobra_flag_type", + "value": "string" + } + }, + "interface_description": "ProcessInstanceCreationPopRequest", + "interface_type": "object", + "property": "ProcessInstanceCreationPopRequest", + "required": false, + "type": "string" + } + }, + "path": "oa.start_process_instance", + "primary_cli_path": "oa approval create-instance", + "product_id": "oa", + "reviewed": true, + "risk": "high", + "source": "reviewed_command_registry", + "title": "发起审批实例(需要 --yes 确认)", + "use_when": [ + "用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用" + ] } } } diff --git a/internal/cli/schema_command_registry/products/oa.json b/internal/cli/schema_command_registry/products/oa.json index fc8738c4..de76fd9f 100644 --- a/internal/cli/schema_command_registry/products/oa.json +++ b/internal/cli/schema_command_registry/products/oa.json @@ -61,6 +61,18 @@ "canonical_path": "oa.list_pending_tasks", "cli_path": "oa approval tasks" }, + { + "canonical_path": "oa.get_process_schema", + "cli_path": "oa approval form-schema" + }, + { + "canonical_path": "oa.forecast_process", + "cli_path": "oa approval forecast-process" + }, + { + "canonical_path": "oa.start_process_instance", + "cli_path": "oa approval create-instance" + }, { "canonical_path": "oa.shortcut_list_pending", "cli_path": "oa +list-pending" diff --git a/internal/cli/schema_hints/metadata/oa.json b/internal/cli/schema_hints/metadata/oa.json index 01c74b56..869305b8 100644 --- a/internal/cli/schema_hints/metadata/oa.json +++ b/internal/cli/schema_hints/metadata/oa.json @@ -91,6 +91,21 @@ "reviewed": true, "runtime_gate": "confirm_delete" }, + "oa.get_process_schema": { + "effect": "read", "risk": "low", "confirmation": "not_required", "idempotency": "idempotent", + "interface_mode": "mcp", "availability": "available", "reviewed": true, "runtime_gate": "none", + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,可按 processCode 只读查询表单结构。" + }, + "oa.forecast_process": { + "effect": "read", "risk": "low", "confirmation": "not_required", "idempotency": "idempotent", + "interface_mode": "mcp", "availability": "available", "reviewed": true, "runtime_gate": "none", + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process,只预测流程而不创建审批实例。" + }, + "oa.start_process_instance": { + "effect": "write", "risk": "high", "confirmation": "user_required", "idempotency": "non_idempotent", + "interface_mode": "mcp", "availability": "available", "reviewed": true, "runtime_gate": "typed_yes", + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;命令在调用前强制显式 --yes,未提供时直接拒绝,不进入交互确认。" + }, "oa.shortcut_list_pending": { "effect": "read", "risk": "low", diff --git a/internal/cli/schema_hints/runtime-surface-completeness.json b/internal/cli/schema_hints/runtime-surface-completeness.json index d9e7511e..3e278d66 100644 --- a/internal/cli/schema_hints/runtime-surface-completeness.json +++ b/internal/cli/schema_hints/runtime-surface-completeness.json @@ -7,7 +7,7 @@ "channel": "open-source" }, "coverage": { - "source_tools": 847, + "source_tools": 850, "matched_tools": 71 }, "tools": { diff --git a/internal/cli/schema_hints/selection/oa.json b/internal/cli/schema_hints/selection/oa.json index 77b03835..cd4386f0 100644 --- a/internal/cli/schema_hints/selection/oa.json +++ b/internal/cli/schema_hints/selection/oa.json @@ -32,6 +32,39 @@ "live-dws-schema:oa.approve_processInstance" ] }, + "oa.get_process_schema": { + "agent_summary": "查询审批模板的表单 Schema", + "use_when": ["已从 list-forms 或 search-forms 获得 processCode,需要读取字段、选项和必填规则后再填写审批时"], + "avoid_when": ["只需列出可用模板时使用 list-forms;不要把返回的 Schema 当作可直接提交的实例请求"], + "examples": ["dws oa approval form-schema --process-code "], + "reviewed": true, + "review_reason": "公开 OA MCP tools/list 已验证 get_process_schema,按 GitHub OA 命令树手写选型。", + "source_refs": ["CommandRegistry:canonical_path=oa.get_process_schema", "cobra-help:dws oa approval form-schema", "live-mcp-tools-list:oa.get_process_schema"] + }, + "oa.forecast_process": { + "agent_summary": "预测审批流程与自选审批节点", + "use_when": ["已知道 processCode 且已根据表单 Schema 组装字段,需要在发起前确认审批路径或自选节点时"], + "avoid_when": ["需要真正创建审批单时改用 create-instance;未获得字段定义时先用 form-schema"], + "examples": [ + "dws oa approval forecast-process --process-code --dept-id -1 --form-values '{\"金额\":\"100\"}'", + "dws oa approval forecast-process --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[[{\"name\":\"金额\",\"value\":\"100\"}]]}'" + ], + "reviewed": true, + "review_reason": "公开 OA MCP tools/list 已验证 forecast_process;示例覆盖 Cobra 简单模式与完整请求高级模式。", + "source_refs": ["CommandRegistry:canonical_path=oa.forecast_process", "cobra-help:dws oa approval forecast-process", "live-mcp-tools-list:oa.forecast_process"] + }, + "oa.start_process_instance": { + "agent_summary": "发起新的审批实例", + "use_when": ["用户确认要发起审批,且已查询表单 Schema、核对字段及审批路径后使用"], + "avoid_when": ["只需预测流程时使用 forecast-process;用户尚未确认或字段未按 Schema 核对时不要发起"], + "examples": [ + "dws oa approval create-instance --process-code --form-values '{\"事由\":\"测试\"}'", + "dws oa approval create-instance --request '{\"processCode\":\"PROC-xxx\",\"deptId\":-1,\"formComponentValues\":[{\"name\":\"事由\",\"value\":\"测试\"}],\"targetSelectActioners\":[{\"actionerKey\":\"manual-node\",\"actionerStaffIds\":[\"user-id\"]}]}'" + ], + "reviewed": true, + "review_reason": "公开 OA MCP tools/list 已验证 start_process_instance;示例覆盖简单与高级请求契约,真实执行仍须在用户确认后显式添加 --yes。", + "source_refs": ["CommandRegistry:canonical_path=oa.start_process_instance", "cobra-help:dws oa approval create-instance", "live-mcp-tools-list:oa.start_process_instance"] + }, "oa.dingflow_comments": { "agent_summary": "用户添加审批评论", "use_when": [ diff --git a/internal/cli/schema_mcp_metadata.json b/internal/cli/schema_mcp_metadata.json index b2df663b..bd72017a 100644 --- a/internal/cli/schema_mcp_metadata.json +++ b/internal/cli/schema_mcp_metadata.json @@ -17,6 +17,30 @@ "unmatched_tools": 85 }, "tools": { + "oa.forecast_process": { + "title": "forecast_process", + "description": "审批流程预测", + "parameters": { + "ProcessForecastPopRequest": {"type": "object", "description": "ProcessForecastPopRequest", "required": false} + }, + "interface_ref": {"product_id": "oa", "rpc_name": "forecast_process"} + }, + "oa.get_process_schema": { + "title": "get_process_schema", + "description": "根据表单的processCode,获取该表单的 schema 信息", + "parameters": { + "processCode": {"type": "string", "description": "需要查询schema信息的表单processCode", "required": true} + }, + "interface_ref": {"product_id": "oa", "rpc_name": "get_process_schema"} + }, + "oa.start_process_instance": { + "title": "审批发起表单实例", + "description": "根据用户输入审批表单内容、流程、抄送人等信息发起审批", + "parameters": { + "ProcessInstanceCreationPopRequest": {"type": "object", "description": "ProcessInstanceCreationPopRequest", "required": false} + }, + "interface_ref": {"product_id": "oa", "rpc_name": "start_process_instance"} + }, "aisearch.enterprise_person_search": { "title": "enterprise_person_search", "description": "企业内找人搜索工具,支持按姓名、部门、职位、技能、工作职责等多维度搜索企业内人员。\\n\\n**适用场景示例**:\\n- 找一下负责智能化的人\\n- 帮我找产品经理\\n- 技术部有哪些人\\n- 张三的领导是谁\\n- 李四手下有哪些人\\n- 帮我找下王芳\\n- 谁在做大模型相关的工作\\n- 公司有哪些架构师\\n- 市场部的同事有谁\\n- 有没有会Java的同事\\n- 研发部的负责人是谁\\n- 帮我找一下跟智能客服有关的人\\n- 做设计的同事有谁\\n- 谁是技术总监\\n- 张三在哪个部门\\n\\n", diff --git a/internal/cli/schema_parameter_bindings.json b/internal/cli/schema_parameter_bindings.json index c0995309..d1839e16 100644 --- a/internal/cli/schema_parameter_bindings.json +++ b/internal/cli/schema_parameter_bindings.json @@ -2,8 +2,8 @@ "version": 3, "baseline": { "manifest": "schema-parameter-bindings-v3", - "sha256": "sha256:43147fbb7881204bc9cf9662543c7d49d7aefdfa82a2f9e684923547e0b8a0bd", - "reason": "Reviewed v3 baseline after binding wiki.list_workspace_feeds --workspace/--limit/--cursor/--exclude-file to the workspaceId/maxResults/nextToken/excludeFile properties while porting the wukong knowledge base feed query command.", + "sha256": "sha256:157aaf77922525f0cc010ce25432282c27c87f9b8ce2ef877a43724f2bc6d15b", + "reason": "Reviewed v3 baseline after combining the OA full-request wrapper bindings and exclusions with wiki.list_workspace_feeds workspaceId/maxResults/nextToken/excludeFile bindings.", "reviewed": true }, "removals": { @@ -955,6 +955,9 @@ "limit": "pageSize", "page": "pageNumber" }, + "oa.forecast_process": { + "request": "ProcessForecastPopRequest" + }, "oa.list_initiated_instances": { "cursor": "nextToken", "end": "endTime", @@ -983,6 +986,9 @@ "oa.revoke_processInstance": { "instance-id": "processInstanceId" }, + "oa.start_process_instance": { + "request": "ProcessInstanceCreationPopRequest" + }, "report.get_received_report_list": { "end": "endTime", "start": "startTime" @@ -1977,6 +1983,17 @@ "sheet.write_image --file": "local upload input used to obtain resourceId/resourceUrl", "sheet.write_image --mime-type": "local upload metadata", "sheet.write_image --name": "local upload metadata", + "oa.forecast_process --dept-id": "Conditional request wrapper: --dept-id is encoded inside ProcessForecastPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "oa.forecast_process --form-values": "Conditional request wrapper: --form-values is transformed into formComponentValues inside ProcessForecastPopRequest, so it has no independent top-level MCP property.", + "oa.forecast_process --process-code": "Conditional request wrapper: --process-code is encoded inside ProcessForecastPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "oa.start_process_instance --approvers": "Conditional request wrapper: --approvers is transformed into an approvers array inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "oa.start_process_instance --approvers-action-type": "Conditional request wrapper: --approvers-action-type only configures the generated approvers array inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "oa.start_process_instance --cc-list": "Conditional request wrapper: --cc-list is encoded inside ProcessInstanceCreationPopRequest only when supplied, so it has no independent top-level MCP property.", + "oa.start_process_instance --cc-position": "Conditional request wrapper: --cc-position only configures the generated ccList inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "oa.start_process_instance --dept-id": "Conditional request wrapper: --dept-id is encoded inside ProcessInstanceCreationPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", + "oa.start_process_instance --form-values": "Conditional request wrapper: --form-values is transformed into formComponentValues inside ProcessInstanceCreationPopRequest, so it has no independent top-level MCP property.", + "oa.start_process_instance --originator-user-id": "Conditional request wrapper: --originator-user-id is encoded inside ProcessInstanceCreationPopRequest only when supplied, so it has no independent top-level MCP property.", + "oa.start_process_instance --process-code": "Conditional request wrapper: --process-code is encoded inside ProcessInstanceCreationPopRequest together with the other simple-mode flags, so it has no independent top-level MCP property.", "todo.add_todo_attachment --file-path": "local upload input used to construct attachmentList", "todo.get_user_todos_in_current_org --query-all": "Local route selector: the default path calls get_user_todos_in_current_org, while --query-all switches to get_user_todos; it is not a property of the pinned default RPC.", "todo.list_todo_attachment --task-id": "Reviewed unpinned adapter: --task-id is nested under todoAttachmentListRequest at runtime, while the immutable pinned MCP snapshot has no interface_ref for todo.list_todo_attachment.", diff --git a/internal/helpers/oa.go b/internal/helpers/oa.go index 690fa4a9..eebcf6e5 100644 --- a/internal/helpers/oa.go +++ b/internal/helpers/oa.go @@ -1,20 +1,54 @@ package helpers import ( + "bytes" + "encoding/json" + "errors" "fmt" + "io" "strconv" "strings" + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli" "github.com/spf13/cobra" ) +func decodeOARequest(raw string) (map[string]any, error) { + dec := json.NewDecoder(bytes.NewBufferString(raw)) + dec.UseNumber() + var request map[string]any + if err := dec.Decode(&request); err != nil || request == nil { + if err != nil { + return nil, err + } + return nil, fmt.Errorf("JSON 请求不能为 null") + } + if err := dec.Decode(new(any)); !errors.Is(err, io.EOF) { + return nil, fmt.Errorf("JSON 请求包含多余内容") + } + return request, nil +} + +func oaFormValues(raw string) ([]map[string]string, error) { + var values map[string]string + if err := json.Unmarshal([]byte(raw), &values); err != nil { + return nil, err + } + result := make([]map[string]string, 0, len(values)) + for name, value := range values { + result = append(result, map[string]string{"name": name, "value": value}) + } + return result, nil +} + // ────────────────────────────────────────────────────────── // dws oa — OA 审批 // MCP tools(tools/list): list_pending_approvals, get_processInstance_detail, // approve_processInstance, reject_processInstance, revoke_processInstance, // get_processInstance_records, list_initiated_instances, list_pending_tasks, // list_user_visible_process, append_task, search_form, oa_ding_user, revert_task, -// get_inst_revert_activities +// get_inst_revert_activities, get_process_schema, forecast_process, +// start_process_instance // ────────────────────────────────────────────────────────── func newOaCommand() *cobra.Command { @@ -463,6 +497,97 @@ func newOaCommand() *cobra.Command { }, } + approvalFormSchemaCmd := &cobra.Command{ + Use: "form-schema", Short: "查询审批模板的表单 Schema", + Example: "dws oa approval form-schema --process-code ", + RunE: func(cmd *cobra.Command, args []string) error { + if err := validateRequiredFlags(cmd, "process-code"); err != nil { + return err + } + return callMCPTool("get_process_schema", map[string]any{"processCode": mustGetFlag(cmd, "process-code")}) + }, + } + approvalForecastCmd := &cobra.Command{ + Use: "forecast-process", Short: "根据表单值预测审批流程与自选节点", + Example: "dws oa approval forecast-process --process-code --dept-id -1 --form-values '{\"金额\":\"100\"}'", + RunE: func(cmd *cobra.Command, args []string) error { + if raw, _ := cmd.Flags().GetString("request"); raw != "" { + request, err := decodeOARequest(raw) + if err != nil { + return fmt.Errorf("--request JSON 解析失败: %w", err) + } + return callMCPTool("forecast_process", map[string]any{"ProcessForecastPopRequest": request}) + } + if err := validateRequiredFlags(cmd, "process-code", "dept-id", "form-values"); err != nil { + return err + } + deptID, err := strconv.ParseInt(mustGetFlag(cmd, "dept-id"), 10, 64) + if err != nil { + return fmt.Errorf("--dept-id 必须为整数: %w", err) + } + values, err := oaFormValues(mustGetFlag(cmd, "form-values")) + if err != nil { + return fmt.Errorf("--form-values JSON 解析失败: %w", err) + } + return callMCPTool("forecast_process", map[string]any{"ProcessForecastPopRequest": map[string]any{"processCode": mustGetFlag(cmd, "process-code"), "deptId": deptID, "formComponentValues": [][]map[string]string{values}}}) + }, + } + approvalCreateCmd := &cobra.Command{ + Use: "create-instance", Short: "发起审批实例(需要 --yes 确认)", + Example: "dws oa approval create-instance --process-code --form-values '{\"事由\":\"测试\"}' --yes", + RunE: func(cmd *cobra.Command, args []string) error { + if !commandDryRun(cmd) { + yes, _ := cmd.Flags().GetBool("yes") + if !yes { + return fmt.Errorf("发起审批实例会创建真实业务数据;请先核对参数,然后添加 --yes 确认执行") + } + } + var request map[string]any + if raw, _ := cmd.Flags().GetString("request"); raw != "" { + var err error + request, err = decodeOARequest(raw) + if err != nil { + return fmt.Errorf("--request JSON 解析失败: %w", err) + } + } else { + if err := validateRequiredFlags(cmd, "process-code", "form-values"); err != nil { + return err + } + values, err := oaFormValues(mustGetFlag(cmd, "form-values")) + if err != nil { + return fmt.Errorf("--form-values JSON 解析失败: %w", err) + } + request = map[string]any{"processCode": mustGetFlag(cmd, "process-code"), "formComponentValues": values} + if dept, _ := cmd.Flags().GetString("dept-id"); dept != "" { + value, err := strconv.ParseInt(dept, 10, 64) + if err != nil { + return fmt.Errorf("--dept-id 必须为整数: %w", err) + } + request["deptId"] = value + } + if userID, _ := cmd.Flags().GetString("originator-user-id"); userID != "" { + request["originatorUserId"] = userID + } + if rawApprovers, _ := cmd.Flags().GetString("approvers"); rawApprovers != "" { + action, _ := cmd.Flags().GetString("approvers-action-type") + if action != "AND" && action != "OR" && action != "NONE" { + return fmt.Errorf("--approvers-action-type 必须为 AND、OR 或 NONE") + } + request["approvers"] = []map[string]any{{"actionType": action, "userIds": strings.Split(rawApprovers, ",")}} + } + if rawCC, _ := cmd.Flags().GetString("cc-list"); rawCC != "" { + position, _ := cmd.Flags().GetString("cc-position") + if position != "START" && position != "FINISH" && position != "START_FINISH" { + return fmt.Errorf("--cc-position 必须为 START、FINISH 或 START_FINISH") + } + request["ccList"] = strings.Split(rawCC, ",") + request["ccPosition"] = position + } + } + return callMCPTool("start_process_instance", map[string]any{"ProcessInstanceCreationPopRequest": request}) + }, + } + approvalListPendingCmd.Flags().String("start", "", "开始时间 ISO-8601 (如 2026-03-10T00:00:00+08:00) (必填)") approvalListPendingCmd.Flags().String("end", "", "结束时间 ISO-8601 (如 2026-03-10T23:59:59+08:00) (必填)") approvalListPendingCmd.Flags().String("page", "", "分页页码 (可选)") @@ -533,6 +658,46 @@ func newOaCommand() *cobra.Command { approvalRevertTaskCmd.Flags().String("target-activity-id", "", "退回到的节点 ID(退回发起人固定传 sid-startevent)(必填)") approvalRevertTaskCmd.Flags().String("action", "", "退回方式:REVERT_FOR_APPROVAL(退回到审批人)/ REVERT_FOR_RESUBMIT(退回到发起人)(必填)") approvalRevertTaskCmd.Flags().String("remark", "", "退回说明 (可选)") + approvalFormSchemaCmd.Flags().String("process-code", "", "审批模板 processCode (必填)") + approvalForecastCmd.Flags().String("process-code", "", "审批模板 processCode(简单模式使用;与 --request 互斥)") + approvalForecastCmd.Flags().String("dept-id", "", "发起人部门 ID(简单模式使用;与 --request 互斥)") + approvalForecastCmd.Flags().String("form-values", "", "表单值 JSON(简单模式使用;与 --request 互斥)") + approvalForecastCmd.Flags().String("request", "", "完整请求 JSON(高级模式;与简单模式参数互斥)") + approvalForecastCmd.MarkFlagsOneRequired("request", "process-code") + approvalForecastCmd.MarkFlagsRequiredTogether("process-code", "dept-id", "form-values") + forecastMutuallyExclusive := make([][]string, 0, 3) + for _, name := range []string{"process-code", "dept-id", "form-values"} { + approvalForecastCmd.MarkFlagsMutuallyExclusive("request", name) + forecastMutuallyExclusive = append(forecastMutuallyExclusive, []string{"request", name}) + } + cli.AnnotateRuntimeConstraints(approvalForecastCmd, cli.RuntimeSchemaConstraints{ + MutuallyExclusive: forecastMutuallyExclusive, + RequireOneOf: [][]string{{"request", "process-code"}}, + RequireTogether: [][]string{{"process-code", "dept-id", "form-values"}}, + }) + + approvalCreateCmd.Flags().String("process-code", "", "审批模板 processCode(简单模式使用;与 --request 互斥)") + approvalCreateCmd.Flags().String("dept-id", "-1", "发起人部门 ID") + approvalCreateCmd.Flags().String("form-values", "", "表单值 JSON(简单模式使用;与 --request 互斥)") + approvalCreateCmd.Flags().String("request", "", "完整请求 JSON(高级模式;与简单模式参数互斥)") + approvalCreateCmd.Flags().String("originator-user-id", "", "审批发起人 userId") + approvalCreateCmd.Flags().String("approvers", "", "审批人 userId 列表,多个用逗号分隔") + approvalCreateCmd.Flags().String("approvers-action-type", "OR", "审批类型:AND、OR 或 NONE") + approvalCreateCmd.Flags().String("cc-list", "", "抄送人 userId 列表,多个用逗号分隔") + approvalCreateCmd.Flags().String("cc-position", "START", "抄送时点:START、FINISH 或 START_FINISH") + approvalCreateCmd.MarkFlagsOneRequired("request", "process-code") + approvalCreateCmd.MarkFlagsRequiredTogether("process-code", "form-values") + createSimpleFlags := []string{"process-code", "dept-id", "form-values", "originator-user-id", "approvers", "approvers-action-type", "cc-list", "cc-position"} + createMutuallyExclusive := make([][]string, 0, len(createSimpleFlags)) + for _, name := range createSimpleFlags { + approvalCreateCmd.MarkFlagsMutuallyExclusive("request", name) + createMutuallyExclusive = append(createMutuallyExclusive, []string{"request", name}) + } + cli.AnnotateRuntimeConstraints(approvalCreateCmd, cli.RuntimeSchemaConstraints{ + MutuallyExclusive: createMutuallyExclusive, + RequireOneOf: [][]string{{"request", "process-code"}}, + RequireTogether: [][]string{{"process-code", "form-values"}}, + }) approvalCmd.AddCommand( approvalListPendingCmd, @@ -555,6 +720,9 @@ func newOaCommand() *cobra.Command { approvalAppendTaskCmd, approvalRevertActivitiesCmd, approvalRevertTaskCmd, + approvalFormSchemaCmd, + approvalForecastCmd, + approvalCreateCmd, ) root.AddCommand(approvalCmd) diff --git a/internal/helpers/oa_coverage_more_test.go b/internal/helpers/oa_coverage_more_test.go index 8316cb7b..70cb18cc 100644 --- a/internal/helpers/oa_coverage_more_test.go +++ b/internal/helpers/oa_coverage_more_test.go @@ -1,6 +1,34 @@ package helpers -import "testing" +import ( + "io" + "os" + "strings" + "testing" +) + +func executeOACommand(t *testing.T, caller *scriptedToolCaller, args ...string) error { + t.Helper() + previous := deps + previousArgs := os.Args + os.Args = []string{"dws", "oa"} + InitDeps(caller) + deps.Out.w = io.Discard + deps.Out.errW = io.Discard + t.Cleanup(func() { + deps = previous + os.Args = previousArgs + }) + + cmd := newOaCommand() + cmd.PersistentFlags().Bool("yes", false, "跳过确认") + cmd.SilenceErrors = true + cmd.SilenceUsage = true + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + cmd.SetArgs(args) + return cmd.Execute() +} func TestCrossPlatformCoverageOARemainingTimeAndRevertBranches(t *testing.T) { installScriptedCaller(t, &scriptedToolCaller{dry: true}) @@ -31,3 +59,143 @@ func TestCrossPlatformCoverageOARemainingTimeAndRevertBranches(t *testing.T) { t.Fatalf("revert task: %v", err) } } + +func TestCrossPlatformCoverageOAApprovalCreateInstanceMapsInternalSimpleOptions(t *testing.T) { + caller := &scriptedToolCaller{} + err := executeOACommand(t, caller, + "approval", "create-instance", + "--process-code", "PROC", + "--form-values", `{"事由":"测试"}`, + "--originator-user-id", "originator", + "--approvers", "approver-1,approver-2", + "--approvers-action-type", "AND", + "--cc-list", "cc-1,cc-2", + "--cc-position", "FINISH", + "--yes", + ) + if err != nil { + t.Fatalf("create instance: %v", err) + } + if caller.server != "oa" || caller.tool != "start_process_instance" { + t.Fatalf("called %s/%s, want oa/start_process_instance", caller.server, caller.tool) + } + request, ok := caller.args["ProcessInstanceCreationPopRequest"].(map[string]any) + if !ok { + t.Fatalf("request payload = %#v", caller.args) + } + if got := request["originatorUserId"]; got != "originator" { + t.Fatalf("originatorUserId = %#v", got) + } + approvers, ok := request["approvers"].([]map[string]any) + if !ok || len(approvers) != 1 || approvers[0]["actionType"] != "AND" { + t.Fatalf("approvers = %#v", request["approvers"]) + } + if got := approvers[0]["userIds"]; len(got.([]string)) != 2 || got.([]string)[0] != "approver-1" || got.([]string)[1] != "approver-2" { + t.Fatalf("approver userIds = %#v", got) + } + if got := request["ccList"]; len(got.([]string)) != 2 || got.([]string)[0] != "cc-1" || got.([]string)[1] != "cc-2" { + t.Fatalf("ccList = %#v", got) + } + if got := request["ccPosition"]; got != "FINISH" { + t.Fatalf("ccPosition = %#v", got) + } +} + +func TestCrossPlatformCoverageOAApprovalCreateInstanceRejectsMixedRequestModes(t *testing.T) { + caller := &scriptedToolCaller{} + err := executeOACommand(t, caller, + "approval", "create-instance", + "--request", `{"processCode":"PROC"}`, + "--process-code", "PROC", + "--yes", + ) + if err == nil { + t.Fatal("mixed request modes returned nil") + } + if caller.calls != 0 { + t.Fatalf("unexpected MCP call count: %d", caller.calls) + } +} + +func TestCrossPlatformCoverageOAApprovalCreateInstanceRequiresExplicitYes(t *testing.T) { + caller := &scriptedToolCaller{} + err := executeOACommand(t, caller, + "approval", "create-instance", + "--request", `{"processCode":"PROC"}`, + ) + if err == nil || !strings.Contains(err.Error(), "--yes") { + t.Fatalf("create instance without --yes error = %v, want explicit --yes requirement", err) + } + if caller.calls != 0 { + t.Fatalf("create instance without --yes made %d MCP calls", caller.calls) + } +} + +func TestCrossPlatformCoverageOAApprovalNewCommandValidationAndRequestModes(t *testing.T) { + validCases := []struct { + name string + args []string + tool string + }{ + { + name: "form schema", + args: []string{"approval", "form-schema", "--process-code", "PROC"}, + tool: "get_process_schema", + }, + { + name: "forecast simple mode", + args: []string{"approval", "forecast-process", "--process-code", "PROC", "--dept-id", "-1", "--form-values", `{"金额":"100"}`}, + tool: "forecast_process", + }, + { + name: "forecast request mode", + args: []string{"approval", "forecast-process", "--request", `{"processCode":"PROC"}`}, + tool: "forecast_process", + }, + { + name: "create request mode", + args: []string{"approval", "create-instance", "--request", `{"processCode":"PROC"}`, "--yes"}, + tool: "start_process_instance", + }, + } + for _, tc := range validCases { + t.Run(tc.name, func(t *testing.T) { + caller := &scriptedToolCaller{} + if err := executeOACommand(t, caller, tc.args...); err != nil { + t.Fatalf("execute %v: %v", tc.args, err) + } + if caller.tool != tc.tool || caller.calls != 1 { + t.Fatalf("called tool=%q calls=%d, want %q once", caller.tool, caller.calls, tc.tool) + } + }) + } + + invalidCases := [][]string{ + {"approval", "form-schema"}, + {"approval", "forecast-process"}, + {"approval", "forecast-process", "--request", `{"processCode":"PROC"}`, "--process-code", "PROC"}, + {"approval", "forecast-process", "--request", "{"}, + {"approval", "forecast-process", "--request", "null"}, + {"approval", "forecast-process", "--request", "{} {}"}, + {"approval", "forecast-process", "--process-code", "PROC", "--dept-id", "bad", "--form-values", `{"金额":"100"}`}, + {"approval", "forecast-process", "--process-code", "PROC", "--dept-id", "-1", "--form-values", "["}, + {"approval", "create-instance", "--process-code", "PROC", "--form-values", `{}`}, + {"approval", "create-instance", "--yes"}, + {"approval", "create-instance", "--request", "{", "--yes"}, + {"approval", "create-instance", "--request", "null", "--yes"}, + {"approval", "create-instance", "--request", "{} {}", "--yes"}, + {"approval", "create-instance", "--process-code", "PROC", "--form-values", "[", "--yes"}, + {"approval", "create-instance", "--process-code", "PROC", "--form-values", `{}`, "--dept-id", "bad", "--yes"}, + {"approval", "create-instance", "--process-code", "PROC", "--form-values", `{}`, "--approvers", "u", "--approvers-action-type", "bad", "--yes"}, + {"approval", "create-instance", "--process-code", "PROC", "--form-values", `{}`, "--cc-list", "u", "--cc-position", "bad", "--yes"}, + } + for _, args := range invalidCases { + caller := &scriptedToolCaller{} + if err := executeOACommand(t, caller, args...); err == nil { + t.Fatalf("invalid args %v returned nil", args) + } + if caller.calls != 0 { + t.Fatalf("invalid args %v made %d MCP calls", args, caller.calls) + } + } +} diff --git a/internal/helpers/polling_coverage_more_test.go b/internal/helpers/polling_coverage_more_test.go index aadbb3d7..5fa5fcda 100644 --- a/internal/helpers/polling_coverage_more_test.go +++ b/internal/helpers/polling_coverage_more_test.go @@ -23,10 +23,16 @@ type scriptedToolCaller struct { format string dry bool calls int + server string + tool string + args map[string]any } -func (c *scriptedToolCaller) CallTool(context.Context, string, string, map[string]any) (*edition.ToolResult, error) { +func (c *scriptedToolCaller) CallTool(_ context.Context, serverID, toolName string, args map[string]any) (*edition.ToolResult, error) { c.calls++ + c.server = serverID + c.tool = toolName + c.args = args if len(c.steps) == 0 { return &edition.ToolResult{}, nil } diff --git a/skills/mono/references/products/oa.md b/skills/mono/references/products/oa.md index 643b0ac7..46c32b36 100644 --- a/skills/mono/references/products/oa.md +++ b/skills/mono/references/products/oa.md @@ -117,6 +117,363 @@ Flags: --query string 关键字,匹配 processCode 或表单名称 (必填) ``` +### 按模板 processCode 查询表单 Schema 信息 + +> **说明:** 根据已知的 processCode 精确查询表单的完整 Schema,包括表单名称、状态、创建者、创建/修改时间以及表单组件 JSON(content 字段)。 + +``` +Usage: + dws oa approval form-schema [flags] +Example: + dws oa approval form-schema --process-code PROC-594AE140-6AA5-4BA4-AF0C-9E6F66DB1E0B +Flags: + --process-code string 表单模板 processCode (必填) +``` + +返回值字段: +- `result.processName` — 表单名称 +- `result.processCode` — 表单 processCode +- `result.processStatus` — 表单状态(如 `PUBLISHED`) +- `result.creator` — 创建者 userId +- `result.gmtCreate` / `result.gmtModified` — 创建/修改时间(毫秒时间戳) +- `result.processIconUrl` — 表单图标 URL +- `result.processDescription` — 表单描述 +- `result.content` — 表单组件 JSON 字符串,包含表单项(items)和标题等配置 + +### 流程预测 + +``` +Usage: + dws oa approval forecast-process [flags] +Example: + # 简单预测 + dws oa approval forecast-process --process-code PROC-xxx --dept-id -1 --form-values '{"单行输入框":"测试内容"}' + # 指定部门预测 + dws oa approval forecast-process --process-code PROC-xxx --dept-id 12345 --form-values '{"金额":"5000"}' + # 高级用法:传入完整 JSON + dws oa approval forecast-process --request '{"processCode":"PROC-xxx","deptId":-1,"formComponentValues":[[{"name":"单行输入框","value":"测试"}]]}' +Flags: + --process-code string 审批模板 processCode(简单模式必填) + --form-values string 表单值 JSON,格式 '{"控件名称":"值"}'(简单模式必填) + --dept-id string 发起人所在部门 ID,根部门填 -1(简单模式必填) + --request string 完整请求体 JSON(高级模式,与简单模式互斥) +``` + +> **注意:** forecast 接口的 `formComponentValues` 比 create-instance 多一层数组包裹(`[[{...}]]`),CLI 简单模式已自动处理,高级模式需自行包裹。`processCode`、`deptId`、`formComponentValues` 三个字段均为必填,`userId` 由系统从登录态自动填充。 + +#### 流程预测的作用 + +在 `create-instance` 之前调用 `forecast-process`,可以根据已填写的表单值预测审批流程走向,核心价值有两个: + +1. **展示流程路径** — 告诉用户这个审批会经过哪些节点(审批人、抄送人、条件分支),让用户在提交前就知道流程走向。 +2. **识别自选审批人节点** — 返回中 `targetSelect: true` 的节点需要用户手动选择审批人/抄送人,Agent 应提示用户选人,并将结果传入 `create-instance` 的 `targetSelectActioners`。 + +#### 返回值关键字段 + +| 字段 | 含义 | +|------|------| +| `result.forecastSuccess` | 预测是否成功 | +| `result.staticWorkflow` | 是否为静态流程(无条件分支) | +| `result.workflowForecastNodes` | 流程节点路径,每个节点包含 `activityId` 和 `outIds`(下一跳) | +| `result.workflowActivityRuleVOs` | **重点**:每个节点的详细规则,包含节点类型、审批人、是否自选等 | + +#### `workflowActivityRuleVOs` 节点字段解读 + +| 字段 | 含义 | +|------|------| +| `activityId` | 节点 ID | +| `workflowActor.actorKey` | 自选节点的规则 key,即 `targetSelectActioners` 中 `actionerKey` 的值 | +| `activityName` | 节点名称(如"审批人"、"抄送人") | +| `activityType` | 节点类型:`target_approval`(已指定审批人)、`target_select`(需自选)、`target_notifier`(抄送) | +| `targetSelect` | **`true` 表示需要用户自选审批人/抄送人** | +| `activityActioners` | 已确定的处理人列表(含 `emplId`、`name`) | +| `workflowActor.actorType` | 角色类型:`approver`(审批人)、`notifier`(抄送人) | +| `workflowActor.approvalMethod` | 多人审批方式:`ONE_BY_ONE`(依次审批) | +| `workflowActor.actorSelectionType` | 选人范围:`allStaff`(全员可选)等 | +| `prevActivityId` | 上一节点 ID | + +#### Agent 处理流程 + +``` +1. 调用 forecast-process,传入 processCode + form-values +2. 遍历 workflowActivityRuleVOs: + a. 向用户展示每个节点的名称、类型、已指定处理人 + b. 若 targetSelect == true: + - 提示用户"节点「{activityName}」需要您自选{actorType}人" + - 使用 dws aisearch person --keyword "<姓名>" --dimension name --format json 帮用户查找并选人 + - 记录 activityId 和用户选择的 userIds +3. 将自选结果组装为 targetSelectActioners,传入 create-instance 高级模式 --request +``` + +#### 自选节点 → `targetSelectActioners` 组装示例 + +假设 forecast 返回两个自选节点: + +```json +{ + "targetSelectActioners": [ + { + "actionerKey": "manual_33ff_89cb_da91_e3aa", + "actionerStaffIds": ["userId_选人A"] + }, + { + "actionerKey": "manual_a29e_9633_f8b7_7291", + "actionerStaffIds": ["userId_选人B"] + } + ] +} +``` + +此字段通过 `create-instance --request` 的高级模式传入。`actionerKey` 来自 forecast 返回的 `workflowActor.actorKey`。 + +### 发起审批实例 + +#### 执行摘要 + +- **如果用户未明确给出 `processCode`,必须固定走 `search-forms` → `form-schema` → 收集表单值 → `forecast-process` → 自选节点选人 → `create-instance`**,不要跳过 `form-schema` 直接拼请求。 +- **如果用户明确给出 `processCode`,固定走 `form-schema` → 收集表单值 → `forecast-process` → 自选节点选人 → `create-instance`**,不要跳过 `form-schema` 直接拼请求。 +- **`form-schema` 返回的 `content` 不是创建 payload 的原样模板。** 它主要用于识别控件 `label`(即 name)、`id`、控件类型(componentName)和选项值范围;真正的 `formComponentValues` 中 `value` 结构以本文的控件值格式表为准。 +- **`forecast-process` 返回的自选节点必须在发起前让用户选人。** 若 `workflowActivityRuleVOs` 中有 `targetSelect: true` 的节点,必须提示用户选择处理人,并将结果通过 `targetSelectActioners` 传入 `create-instance`。 +- **所有人员类参数使用 userId。** 若用户给的是姓名,先用 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 解析成 userId。**严禁把姓名直接写进** `approvers`、`ccList`、`directAppointedApprovers`、`targetSelectActioners` 或表单人员控件。 +- **创建实例前一次性汇总确认。** `create-instance` 是写操作,执行前一次性展示模板、表单值、流程预测结果和审批人/抄送人供用户确认。 + +#### 严禁行为 + +- **严禁跳过 `form-schema`。** 未拿到表单 Schema 前,不得调用 `create-instance`。 +- **严禁复用旧的 Schema 结果。** 每次发起实例前都必须重新调用 `form-schema`,模板可能已被修改。 +- **严禁在存在不支持必填控件时强行发起。** 若 `form-schema` 返回的必填控件中有不支持类型(如附件等),直接告知用户不支持通过 CLI 发起。 +- **严禁把 `form-schema` 返回的 `content` 当成可直接提交的 payload 模板。** +- **严禁把姓名直接写进 `approvers`、`ccList`、`directAppointedApprovers`、`targetSelectActioners` 或表单人员控件。** 必须先通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 转成 userId。 +- **严禁在未得到用户确认前直接执行真实提单。** +- **严禁猜测控件名称或选项值。** 必须从 `form-schema` 返回中提取。 +- **严禁跳过 `forecast-process` 中的自选节点选人。** 若预测返回 `targetSelect: true` 的节点,必须让用户选人后再发起。 + +#### 最小判断表 + +| 你手上有什么 | 下一步 | +|---|---| +| 只有口语需求,比如"帮我发起请假审批" | 先 `search-forms --query 请假` | +| 已拿到 `processCode` | 直接 `form-schema --process-code ` | +| 已拿到 Schema | 向用户展示控件列表,收集表单值 | +| 已收集表单值 | `forecast-process` 预测流程走向 | +| 预测返回有 `targetSelect: true` 节点 | 让用户为自选节点选人(`dws aisearch person --keyword "<姓名>" --dimension name --format json` 解析姓名) | +| 预测完成,自选节点已选人 | 汇总确认后 `create-instance --yes` | +| 用户明确说"不走模板流程,直接指定审批人" | 使用 `directAppointedApprovers`(高级模式) | + +#### 工作流 + +``` +1. search-forms --query <关键词> → 拿到 processCode(若已有则跳过) +2. form-schema --process-code → 拿到控件列表、类型、选项值 +3. 检查 Schema 中是否有不支持的必填控件 → 若有则直接告知用户不支持发起 +4. 收集表单值 → 向用户展示控件列表,收集用户填写的表单值 +5. forecast-process → 根据表单值预测流程走向,识别自选节点 +6. 自选节点选人 → 若预测返回 targetSelect=true 的节点,让用户选人(用 dws aisearch person --keyword "<姓名>" --dimension name --format json 解析姓名) +7. 汇总确认后 create-instance --yes → 展示完整信息(表单值 + 流程路径 + 审批人),用户确认后执行发起 +``` + +> **IMPORTANT:每次发起实例前都必须重新调用 `form-schema` 查询模板。** 即使用户之前查询过同一个 processCode,模板可能已被修改(控件增减、选项变更、必填属性调整等),不得复用旧的 Schema 结果。 + +#### 交互优化原则 + +> **核心目标:流程清晰,步骤有序,避免重复询问。** + +1. **先查 Schema 再收集表单值(步骤 2→4):** `form-schema` 后向用户展示需要填写的控件列表,然后一次性收集全部表单值。不要在未拿到 Schema 前就问用户填什么。 + +2. **流程预测后再选自选审批人(步骤 5→6):** `forecast-process` 返回流程路径和自选节点后: + - 先向用户展示完整的流程路径(经过哪些节点、各节点处理人) + - 对 `targetSelect: true` 的节点,提示用户"节点「{activityName}」需要您自选{actorType}人" + - 用 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 帮用户查找并选人 + - 若有多个自选节点,一次性收集所有自选节点的选人结果 + +3. **单次汇总确认(步骤 7):** 发起前一次性展示完整信息供用户确认: + - 审批模板名称 + - 表单各控件值 + - 流程预测结果(审批路径) + - 各节点审批人/抄送人(含自选节点选人结果) + +**反例(禁止):** +- 未查 Schema 就直接问用户填什么表单值 +- 流程预测后逐个节点分别询问选人,而非一次性收集 +- 用户确认前直接执行发起 + +``` +Usage: + dws oa approval create-instance [flags] +Example: + # 简单发起(Agent 在汇总确认后需加 --yes) + dws oa approval create-instance --process-code PROC-xxx --form-values '{"单行输入框":"测试内容"}' --yes + # 指定审批人(OR=或签,AND=会签,NONE=单人) + dws oa approval create-instance --process-code PROC-xxx --form-values '{"单行输入框":"测试"}' --approvers "userId1,userId2" --approvers-action-type OR --yes + # 指定抄送人 + dws oa approval create-instance --process-code PROC-xxx --form-values '{"单行输入框":"测试"}' --cc-list "userId1" --cc-position START --yes + # 高级用法:传入完整 JSON(支持 directAppointedApprovers、targetSelectActioners 等全部字段) + dws oa approval create-instance --request '{"processCode":"PROC-xxx","deptId":-1,"formComponentValues":[{"name":"单行输入框","value":"测试"}]}' --yes +Flags: + --process-code string 审批模板 processCode(简单模式必填) + --form-values string 表单值 JSON,格式 '{"控件名称":"值"}'(简单模式必填) + --dept-id string 发起人所在部门 ID,根部门填 -1(可选,默认 -1) + --originator-user-id string 审批发起人 userId(可选,MCP 工具可从登录态获取) + --approvers string 审批人 userId 列表,多个用逗号分隔(可选) + --approvers-action-type string 审批类型:AND(会签)、OR(或签)、NONE(单人)(可选,默认 OR) + --cc-list string 抄送人 userId 列表,多个用逗号分隔(可选) + --cc-position string 抄送时间点:START/FINISH/START_FINISH(可选,默认 START) + --request string 完整请求体 JSON(高级模式,与简单模式互斥) + --yes 显式确认并发起审批;未提供时命令直接拒绝,不进入交互确认(Agent 必须先汇总并获得用户确认) +``` + +#### 两种模式 + +- **简单模式:** 通过 `--process-code` + `--form-values` + 可选 flags 发起,适合大多数场景 +- **高级模式:** 通过 `--request` 传入完整 JSON 请求体,支持 `directAppointedApprovers`、`targetSelectActioners` 等复杂字段 + +#### 组装 form-values + +`form-values` 是简单模式下的核心入参;传入时必须是一个 JSON 对象字符串,key 为控件 label,value 为该控件的提交值。组装原则: + +- 先用 `form-schema` 识别有哪些控件、每个控件的 `label`(name)、`componentName`(type)、选项值范围以及明细子控件结构。 +- **`form-schema` 返回的 `content` 不是可直接提交的原样模板。** 它提供控件定义,`value` 结构须按下方控件值格式表组装。 +- 提交时必须保证每个控件的 `name`(即 label)与 Schema 中的 `props.label` **完全一致**。 +- 如果用户提供的是人员信息,先用 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 转成 userId 后再写入对应控件。 +- 单选/多选控件提交的是选项文本(option value),该值从 `form-schema` 返回的选项定义中取得。 +- `InnerContactField`、`DepartmentField`、`TableField`、`DDDateRangeField`、`DDAttachment` 等控件的 `value` 结构各不相同,必须按下方格式表单独组装,不要套用文本控件的写法。 +- `TextNote`(文字说明)不收集数据,**不要**出现在 `formComponentValues` 中。 + +#### 表单控件值格式速查 + +> **重要:** `formComponentValues` 中每条记录的 `name` 必须与审批模板中控件的 `label`(即 `form-schema` 返回的 `content.items[].props.label`)**完全一致**。`value` 为字符串类型,最大 65535 字符。 +> +> **详细参考:** 每种控件的完整属性、约束和示例见 [oa-form-components.md](oa/oa-form-components.md)。组装前**必须先阅读该文档**。 + +| 控件类型 | componentName | value 格式 | 示例 | 备注 | +|---------|---------------|-----------|------|-------------------------------------------------------| +| 单行输入框 | `TextField` | 纯文本 | `"测试内容"` | | +| 多行输入框 | `TextareaField` | 纯文本 | `"第一行\n第二行"` | | +| 数字输入框 | `NumberField` | 数字字符串 | `"100"` | | +| 单选框 | `DDSelectField` | 选项文本 | `"同意"` | 必须与模板 options 中的 value 完全匹配 | +| 多选框 | `DDMultiSelectField` | JSON 数组字符串 | `'["选项A","选项B"]'` | 每个选项须与模板 options 匹配; | +| 日期控件 | `DDDateField` | `yyyy-MM-dd` | `"2026-07-27"` | | +| 时间区间 | `DDDateRangeField` | JSON 数组字符串 | `'["2026-07-27","2026-07-30"]'` | label 为数组 `["开始","结束"]`,用开始时间 label 作 name | +| 金额控件 | `MoneyField` | 数字字符串 | `"1500.50"` | 自动显示大写金额 | +| 电话控件 | `PhoneField` | 手机号字符串 | `"13800138000"` | | +| 联系人控件 | `InnerContactField` | userId | `"user123"` | 多人时传 JSON 数组 `'["user1","user2"]'`;choice="0"单选/"1"多 | +| 部门控件 | `DepartmentField` | 部门 ID | `"12345"` | 多部门传 JSON 数组;multiple=true 时支持多选 | +| 省市区控件 | `AddressField` | JSON 数组字符串 | `'["浙江省","杭州市","西湖区"]'` | 三级联动;needDetail=true 时末尾加详细地址 | +| 图片控件 | `DDPhotoField` | URL 数组转义字符串 | `"[\"http://example.com/img1.jpg\"]"` | 支持 URL 直接提交;**不支持本地文件上传** | +| 附件控件 | `DDAttachment` | JSON 数组转义字符串 | `"[{\"spaceId\":\"xxx\",\"fileName\":\"a.pdf\",\"fileSize\":\"333\",\"fileType\":\"pdf\",\"fileId\":\"xxx\"}]"` | **当前不支持通过 CLI 提交**,需钉盘上传接口获取 fileId 等字段 | +| 评分控件 | `StarRatingField` | 数字字符串 | `"4"` | limit 控制最大星数(默认 5) | +| 关联审批单 | `RelateField` | 审批实例 ID | `"q-xxx"` | 须为当前组织下已存在的实例 | +| 明细控件 | `TableField` | JSON 数组字符串 | `'[{"子控件名":"值1"},{"子控件名":"值2"}]'` | 不可嵌套 TableField;不可含 DDMultiSelectField/DDPhotoField;最大 100 行 | +| 身份证控件 | `IdCardField` | 身份证号 | `"330102199001011234"` | 内置格式校验 | +| 文字说明 | `TextNote` | — | — | **不收集数据**,不会出现在 formComponentValues 中 | + +#### API 不支持的控件 + +以下控件**不支持**通过创建实例 API 提交: + +- `TextNote`(文字说明)— 纯展示,不收集数据 +- `CalculateField`(计算公式)— 由系统自动计算 +- `SeqNumberField`(流水号)— 由系统自动生成 +- `OcrTextField` / `OcrIdCardField`(OCR 识别)— 需要客户端交互 +- **`DDAttachment`(附件控件)— 当前不支持通过 CLI 提交**,value 需要 spaceId、fileName、fileSize、fileType、fileId 字段,须通过钉盘上传附件接口获取 +- **套件类控件(暂不支持)** — `InvoiceField`(发票)、`RecipientAccountField`(收款账户)等业务套件控件当前暂不支持通过 CLI 发起,包含这些控件的审批模板请直接在钉钉客户端操作 + +> **部分支持的控件:** `DDPhotoField`(图片控件)**支持通过 URL 直接提交**(见上方速查表),仅不支持本地文件上传(CLI 未封装钉盘 CDN 上传流程)。若用户只有本地文件,需告知在钉钉客户端补充。 + +如果目标审批模板包含上述控件,不要硬拼 `form-values`;应告知用户这些字段无需填写或需要在钉钉客户端补充。 + +> **必填不支持控件判断规则:** 检查 `form-schema` 返回的控件列表,若存在上述不支持控件且其 `props.required` 为 `true`(必填项),则**直接告知用户该审批模板不支持通过 CLI 发起**,请在钉钉客户端操作。只有不支持控件为非必填时,才可跳过该控件继续发起。 + +#### 高级模式请求体字段(`--request` JSON 完整结构) + +| 字段 | 类型 | 必填 | 说明 | +|-----|------|------|------| +| `processCode` | String | 是 | 审批模板唯一码 | +| `originatorUserId` | String | 是 | 发起人 userId(MCP 工具可从登录态自动获取) | +| `deptId` | Long | 否 | 发起人部门 ID,根部门填 -1;approvers 已传时可不填 | +| `formComponentValues` | Array | 是 | 表单控件值列表,最大 150 条 | +| `approvers` | Array | 否 | 指定审批人列表(覆盖模板流程),最大 20 条 | +| `approvers[].actionType` | String | 否 | `AND`(会签)/ `OR`(或签)/ `NONE`(单人) | +| `approvers[].userIds` | Array | 否 | 审批人 userId 列表 | +| `ccList` | Array | 否 | 抄送人 userId 列表,最大 50 | +| `ccPosition` | String | 否 | `START` / `FINISH` / `START_FINISH` | +| `directAppointedApprovers` | Array | 否 | 指定审批人组(覆盖模板流程),结构见下方 | +| `targetSelectActioners` | Array | 否 | 自选审批人(模板中有自选节点时必填),最大 20 条 | + +#### 节点参数组装 + +> **详细参考:** 流程节点类型、审批模式、条件分支和 10 种审批人选择规则的完整说明见 [oa-process-nodes.md](oa/oa-process-nodes.md)。 + +**directAppointedApprovers(指定审批人覆盖模板流程):** + +当用户明确说"不走模板默认流程"或"直接指定 XX 审批"时使用。 + +```json +[ + { + "staffIds": ["userId1", "userId2"], + "taskActionType": "NONE", + "staffId": "" + } +] +``` +- `staffIds`:审批人 userId 列表(必须通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 获取,严禁填姓名) +- `taskActionType`:`NONE`(单人审批)/ `AND`(会签)/ `OR`(或签) + +**targetSelectActioners(模板有自选审批节点时使用):** + +当 `form-schema` 返回的模板流程中存在自选审批节点(`target_select` 类型)时必填。 + +```json +[ + { + "actionerKey": "manual_nodeId_xxxx_yyyy", + "actionerStaffIds": ["userId1"] + } +] +``` +- `actionerKey`:自选节点的规则 key,可通过获取审批单流程节点信息接口获取 `actorKey` +- `actionerStaffIds`:操作人 userId 列表 + +**审批类型(approvers actionType)说明:** + +| 值 | 含义 | 说明 | +|----|------|------| +| `AND` | 会签 | 所有审批人都必须审批通过 | +| `OR` | 或签 | 任一审批人审批即可 | +| `NONE` | 单人审批 | 只有一个审批人 | + +**抄送时间点(ccPosition)说明:** + +| 值 | 含义 | +|----|------| +| `START` | 审批发起时抄送 | +| `FINISH` | 审批完成时抄送 | +| `START_FINISH` | 发起和完成时都抄送 | + +#### 表单控件约束 + +- 单个表单最多 200 个控件 +- 控件 label(name)和 placeholder 最大 50 字符 +- `DDSelectField` / `DDMultiSelectField` 的选项 value 必须与模板中配置的选项文本完全一致 +- `TableField`(明细)内不可嵌套 `TableField`,不可包含 `DDMultiSelectField` 和 `DDPhotoField` +- `TextNote`(文字说明)不收集数据,无需在 `formComponentValues` 中传入 +- `InnerContactField` 的 userId 应为当前组织下在职成员 +- `DepartmentField` 应传入当前组织下存在的部门 ID +- `RelateField` 传入的审批实例 ID 应为当前组织下已存在的实例 + +#### 返回结果 + +创建成功后,返回的 `result` 字段即为新审批实例的 `processInstanceId`。建议向用户展示: + +``` +审批已创建成功: + +- 审批模板: (来自 form-schema) +- 审批实例 ID: (来自 create-instance 返回的 result) +``` + +后续可用该 processInstanceId 执行 `detail`、`tasks`、`records`、`revoke` 等操作。 ### 获取审批任务的被催办人 userId > **催办必须两步串联:** ① `ding-info` 获取被催办人 `userId` → ② `ding message send` 发送催办消息。禁止跳过第一步直接猜测 userId。 @@ -316,6 +673,18 @@ Flags: 用户说"审批记录/操作历史" → `approval records` 用户说"我发起的审批" → `approval list-initiated`(需 --process-code,可从 list-forms / search-forms / detail 获取) 用户说"有哪些审批表单/可见表单" → `approval list-forms` +用户说"搜索审批表单/查找xx审批表单/有没有xx表单" → `approval search-forms`(需 --query) +用户说"查表单schema/查表单结构/表单模板信息/查表单组件/查表单定义/表单有哪些字段/表单的字段信息" → `approval form-schema`(需 --process-code,可从 list-forms / search-forms / detail 获取) +用户说"预测审批流程/流程预测/审批走向/这个审批走哪些人/审批流程预览" → `approval forecast-process`(需 --process-code、--dept-id、--form-values) + - 在 `form-schema` 之后、`create-instance` 之前调用 + - 返回的 `workflowActivityRuleVOs` 中 `targetSelect: true` 的节点需要用户自选审批人 + - 自选结果组装为 `targetSelectActioners` 传入 `create-instance` +用户说"发起审批/提交审批/帮我发起XX审批/新建审批单/提一个XX审批/帮我提XX申请" → 五步流程:① `search-forms --query XX` 获取 processCode → ② `form-schema --process-code ` 获取表单字段定义 → ③ 阅读 [oa-form-components.md](oa/oa-form-components.md) 和 [oa-process-nodes.md](oa/oa-process-nodes.md) 后组装表单值 → ④ `forecast-process` 预测流程走向并识别自选节点 → ⑤ 若有自选节点让用户选人,确认后 `create-instance --yes` 发起 + - 如果用户已知 processCode,可跳过第①步 + - `--form-values` 的 key 必须与 `form-schema` 返回的控件 label 一致 + - `forecast-process` 返回自选节点时必须让用户选人,不得跳过 + - 执行前**必须向用户确认**表单内容、流程预测结果、审批人和抄送人 + - 示例:"帮我发起一个AI审批单" → ① `search-forms --query AI` → ② `form-schema --process-code ` → ③ 组装表单值 → ④ `forecast-process` → ⑤ 向用户确认流程走向和自选审批人后 `create-instance --yes` 用户说"我有哪些待审的任务" → `approval tasks` 用户说"我发起的审批单" -> `approval list-submitted` 用户说"我审批/处理过的审批单" -> `approval list-executed` @@ -351,6 +720,12 @@ dws oa approval records --instance-id --format json # 7. 获取可见审批表单(得到 processCode) dws oa approval list-forms --cursor 0 --limit 100 --format json +# 7b. 按关键字模糊搜索表单(快速定位 processCode) +dws oa approval search-forms --query AI --format json + +# 7c. 按 processCode 查询表单 Schema(获取表单结构、组件定义) +dws oa approval form-schema --process-code --format json + # 8. 查看自己发起的审批列表(--process-code 来自 list-forms / search-forms / detail) dws oa approval list-initiated --process-code \ --start "2026-03-10T00:00:00+08:00" --end "2026-03-10T23:59:59+08:00" \ @@ -373,6 +748,22 @@ dws oa approval oa-comments --instance-id --content "同意 # 14. 对审批实例进行抄送(processInstanceId 来自 list-pending 或 detail) dws oa approval oa-cc-noticer --instance-id --users "68674200835816" --format json dws oa approval oa-cc-noticer --instance-id --users "userId1,userId2" --format json + +# 18. 发起审批(完整流程:搜表单 → 查 Schema → 收集表单值 → 流程预测 → 自选节点选人 → 发起) +# 18a. 模糊搜索表单获取 processCode +dws oa approval search-forms --query AI --format json +# 18b. 查询表单 Schema 获取字段定义 +dws oa approval form-schema --process-code --format json +# 18c. 收集表单值(向用户展示控件列表,用户填写后组装 form-values) +# 18d. 流程预测(根据表单值预测审批走向,识别自选审批人节点;processCode/deptId/formValues 必填,userId 由登录态自动填充) +dws oa approval forecast-process --process-code --dept-id -1 --form-values '{"单行输入框":"测试内容"}' --format json +# 18e. 若 forecast 返回 targetSelect=true 的节点,用 dws aisearch person --keyword "<姓名>" --dimension name --format json 帮用户选人 +# 18f. 发起审批实例(form-values 的 key 须与 Schema 中控件 label 一致) +dws oa approval create-instance --process-code --form-values '{"单行输入框":"测试内容"}' --yes --format json +# 18g. 发起并指定审批人和抄送人 +dws oa approval create-instance --process-code --form-values '{"单行输入框":"测试"}' --approvers "userId1,userId2" --approvers-action-type OR --cc-list "userId3" --cc-position START --yes --format json +# 18h. 发起并使用 forecast 自选审批人结果(高级模式) +dws oa approval create-instance --request '{"processCode":"PROC-xxx","deptId":-1,"formComponentValues":[{"name":"单行输入框","value":"测试"}],"targetSelectActioners":[{"actionerKey":"manual_33ff_89cb_da91_e3aa","actionerStaffIds":["userId_选人A"]}]}' --yes --format json ``` ## 上下文传递表 @@ -384,6 +775,11 @@ dws oa approval oa-cc-noticer --instance-id --users "userId1 | `detail` | `processCode` | list-initiated 的 --process-code | | `list-forms` | `processCode` | list-initiated 的 --process-code | | `search-forms` | `processCode` | list-initiated 的 --process-code | +| `form-schema` | `processCode`, `processName`, `content` | 查看表单结构定义;`content` 字段包含表单组件 JSON,可解析获取字段列表;**控件 label 作为 create-instance --form-values 的 key** | +| `search-forms` → `form-schema` | `processCode` → 表单字段定义 | forecast-process / create-instance 的 --process-code 和 --form-values 填写依据 | +| `forecast-process` | `workflowActivityRuleVOs`(`activityId`, `targetSelect`, `activityActioners`, `workflowActor`) | ① 向用户展示流程走向和各节点处理人;② `targetSelect: true` 的节点需用户自选审批人,`workflowActor.actorKey` 作为 `targetSelectActioners` 的 `actionerKey` 传入 create-instance | +| `search-forms` → `form-schema` → `forecast-process` | `processCode` → 字段定义 → 流程走向 + 自选节点 | create-instance 的完整上下文:表单值 + 流程路径 + targetSelectActioners | +| `create-instance` | `result`(processInstanceId) | detail / tasks / records / revoke 等的 --instance-id,可跟踪已发起的审批 | ## 注意事项 @@ -395,6 +791,16 @@ dws oa approval oa-cc-noticer --instance-id --users "userId1 - `list-initiated` 的 `--process-code` 可从 `list-forms`、`search-forms` 或 `detail` 返回中提取。当 `list-forms` 返回 `processCodeList` 为空(`totalCount -1`)时,用 `search-forms --query <表单名>`(如 `--query 报销`)按名称精准拿 `processCode` 更稳 - `list-initiated` 的 `--start` / `--end` 区间有后端上限(约 120 天)。超过上限会返回误导性的 `business_error: 时间戳无效`(实为区间过长,不是时间格式问题)。跨度大时请拆成多段短区间分别查询 +- `form-schema` 的 `--process-code` 可从 `list-forms`、`search-forms` 或 `detail` 返回中提取;返回的 `content` 字段为 JSON 字符串,需解析后查看表单组件(items)定义。 +- `create-instance` 发起前**必须先阅读** [oa-form-components.md](oa/oa-form-components.md)(控件值格式)和 [oa-process-nodes.md](oa/oa-process-nodes.md)(流程节点规则),再调用 `form-schema` 获取表单字段定义,确保 `--form-values` 中的 key 与控件 label 完全一致。 +- `create-instance` 发起前**应先调用 `forecast-process`** 预测流程走向,识别自选审批人节点(`targetSelect: true`),让用户选人后再提交。 +- `create-instance` 的 `--form-values` 接受 JSON 格式 `'{"控件名称":"值"}'`,代码会自动转为 `[{"name":"控件名称","value":"值"}]`。 +- `create-instance` 简单模式适合常见场景;如需 `directAppointedApprovers`(指定审批人覆盖模板流程)或 `targetSelectActioners`(自选审批节点)等高级字段,使用 `--request` 传完整 JSON。`--request` 与简单模式 flags 互斥。 +- `create-instance` 会创建真实审批数据;Agent 只有在用户确认模板、表单值、流程路径和人员后才能传入 `--yes`。 +- `create-instance` 返回的 processInstanceId 可用于 `detail`、`tasks`、`records`、`revoke` 等后续操作。 +- `forecast-process` 的 `processCode`、`deptId`、`formComponentValues` 三个字段均为必填(`userId` 由系统自动填充);`formComponentValues` 比 `create-instance` 多一层数组包裹(`[[{...}]]`),CLI 简单模式已自动处理。 +- `forecast-process` 返回 `workflowActivityRuleVOs` 中 `targetSelect: true` 的节点,其 `workflowActor.actorKey` 必须作为 `targetSelectActioners` 的 `actionerKey` 传入 `create-instance`。 + ## 自动化脚本 | 脚本 | 场景 | 用法 | diff --git a/skills/mono/references/products/oa/oa-form-components.md b/skills/mono/references/products/oa/oa-form-components.md new file mode 100644 index 00000000..9deb57d3 --- /dev/null +++ b/skills/mono/references/products/oa/oa-form-components.md @@ -0,0 +1,346 @@ +# OA 审批表单控件参考 + +本文档详细描述钉钉 OA 审批中每种表单控件(componentName)在**发起审批实例**时 `formComponentValues` 的 `value` 格式、约束和注意事项。 + +> **核心原则:** `formComponentValues[].name` 必须与审批模板中控件的 `props.label` **完全一致**,`value` 为字符串类型(最大 65535 字符)。 + +--- + +## 通用约束 + +| 约束 | 说明 | +|------|------| +| 单表单最大控件数 | 200 | +| label / placeholder 最大长度 | 50 字符 | +| value 最大长度 | 65535 字符 | +| ID / bizAlias 唯一性 | 同一表单内不可重复 | +| TextNote | 不收集数据,不出现在 formComponentValues 中 | + +--- + +## 基础控件 + +### TextField(单行输入框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `TextField` | +| value 格式 | 纯文本字符串 | +| 示例 | `"测试内容"` | +| 约束 | 无特殊约束 | + +```json +{ "name": "单行输入框", "value": "测试内容" } +``` + +### TextareaField(多行输入框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `TextareaField` | +| value 格式 | 纯文本字符串,支持换行 | +| 示例 | `"第一行\n第二行"` | +| 约束 | 无 `ratio` 属性 | + +```json +{ "name": "多行输入框", "value": "第一行\n第二行\n第三行" } +``` + +### NumberField(数字输入框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `NumberField` | +| value 格式 | 数字字符串 | +| 示例 | `"100"` | +| 约束 | 适合数量、天数等纯数字场景 | + +```json +{ "name": "加班天数", "value": "3" } +``` + +### DDSelectField(单选框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDSelectField` | +| value 格式 | 选项文本字符串 | +| 示例 | `"同意"` | +| 约束 | **必须与模板 `options[].value` 完全匹配**,不可自行编造选项 | + +模板中的选项结构(从 `form-schema` 获取): +```json +"options": [ + { "key": "option_0", "value": "同意" }, + { "key": "option_1", "value": "不同意" } +] +``` + +提交时传选项的 `value` 文本: +```json +{ "name": "审批意见", "value": "同意" } +``` + +### DDMultiSelectField(多选框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDMultiSelectField` | +| value 格式 | JSON 数组字符串,每个元素为选项文本 | +| 示例 | `'["选项A","选项B"]'` | +| 约束 | 每个选项须与模板 `options[].value` 匹配;| + +```json +{ "name": "兴趣爱好", "value": "[\"阅读\",\"运动\"]" } +``` + +### DDDateField(日期控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDDateField` | +| value 格式 | `yyyy-MM-dd` 格式字符串 | +| 示例 | `"2026-07-27"` | +| 约束 | 格式固定,不可传其他日期格式 | + +```json +{ "name": "请假日期", "value": "2026-07-27" } +``` + +### DDDateRangeField(时间区间控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDDateRangeField` | +| value 格式 | JSON 数组字符串 `[开始日期, 结束日期]` | +| 示例 | `'["2026-07-27","2026-07-30"]'` | +| 约束 | `props.label` 为数组 `["开始时间","结束时间"]`;提交时 `name` 使用**开始时间的 label** | + +模板中的 label 结构(从 `form-schema` 获取): +```json +"props": { "label": ["开始时间", "结束时间"] } +``` + +提交时用**开始时间 label** 作为 name: +```json +{ "name": "开始时间", "value": "[\"2026-07-27\",\"2026-07-30\"]" } +``` + +### PhoneField(电话控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `PhoneField` | +| value 格式 | 手机号字符串 | +| 示例 | `"13800138000"` | +| 约束 | `mode: "phone"` 为手机号 | + +```json +{ "name": "联系电话", "value": "13800138000" } +``` + +### IdCardField(身份证控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `IdCardField` | +| value 格式 | 身份证号字符串 | +| 示例 | `"330102199001011234"` | +| 约束 | 内置格式校验,须传合法身份证号 | + +```json +{ "name": "身份证号", "value": "330102199001011234" } +``` + +### TextNote(文字说明) + +| 属性 | 说明 | +|------|------| +| `componentName` | `TextNote` | +| value 格式 | — | +| 约束 | **不收集数据**,不出现在 formComponentValues 中 | + +> 遇到 TextNote 控件时直接跳过,不要尝试为它填写值。 + +--- + +## 增强控件 + +### MoneyField(金额控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `MoneyField` | +| value 格式 | 数字字符串 | +| 示例 | `"1500.50"` | +| 约束 | 系统自动显示大写金额(`notUpper: "0"` 时显示) | + +```json +{ "name": "报销金额", "value": "1500.50" } +``` + +### InnerContactField(联系人控件) + +| 属性 | 说明 | +|------|----------------------------------------------------| +| `componentName` | `InnerContactField` | +| value 格式 | userId 字符串,多人时为 JSON 数组字符串 | +| 示例(单选) | `"user123"` | +| 示例(多选) | `'["userId1","userId2"]'` | +| 约束 | `choice: "0"` 单选 / `"1"` 多选;userId 须为**当前组织下在职成员** | + +```json +{ "name": "项目负责人", "value": "[\"userId1\",\"userId2\"]" } +``` + +> **严禁直接写姓名。** 必须先通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 查询获取 userId;多结果时须让用户消歧确认。 + +### DepartmentField(部门控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DepartmentField` | +| value 格式 | 部门 ID 字符串,多部门时为 JSON 数组字符串 | +| 示例(单选) | `"12345"` | +| 示例(多选) | `'["12345","67890"]'` | +| 约束 | `multiple: boolean` 控制单选/多选;部门 ID 须为**当前组织下存在的部门** | + +```json +{ "name": "所属部门", "value": "12345" } +``` + +### AddressField(省市区控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `AddressField` | +| value 格式 | JSON 数组字符串 `["省","市","区"]` | +| 示例 | `'["浙江省","杭州市","西湖区"]'` | +| 约束 | 三级联动选择器;`needDetail: true` 时末尾追加详细地址文本 | + +```json +{ "name": "办公地点", "value": "[\"浙江省\",\"杭州市\",\"西湖区\"]" } +``` + +### DDPhotoField(图片控件) + +> **支持通过图片 URL 提交,不支持本地文件上传。** 如果用户已有图片 URL(如公网可访问的图片链接),可直接填入 value 提交。CLI 尚未封装本地文件上传到钉盘 CDN 的流程,若用户只有本地文件而非 URL,需告知用户在钉钉客户端补充。 + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDPhotoField` | +| value 格式 | URL 数组转义字符串,即使只有一个 URL 也需数组形式 | +| 示例 | `"[\"http://example.com/img1.jpg\",\"http://example.com/img2.jpg\"]"` | +| 约束 | 支持 URL 直接提交;**不支持本地文件上传**(CLI 未封装钉盘上传流程); | + +```json +{ "name": "图片", "value": "[\"http://example.com/photo.jpg\"]" } +``` + +### DDAttachment(附件控件) + +> **[注意] 当前暂不支持通过 CLI 提交附件控件。** 附件控件的 value 需要包含 spaceId、fileName、fileSize、fileType 和 fileId 字段,这些字段需要通过调用钉盘的上传附件接口获取,CLI 尚未封装此流程。包含附件控件的审批模板请在钉钉客户端操作。 + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDAttachment` | +| value 格式 | JSON 数组转义字符串,每个元素包含 spaceId、fileName、fileSize、fileType、fileId | +| 示例(参考) | `"[{\"spaceId\":\"163xxx\",\"fileName\":\"2644.JPG\",\"fileSize\":\"333\",\"fileType\":\"jpg\",\"fileId\":\"643xxx\"}]"` | +| 约束 | **当前不支持通过 CLI 提交**;各字段需通过钉盘上传附件接口获取 | + +### StarRatingField(评分控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `StarRatingField` | +| value 格式 | 数字字符串 | +| 示例 | `"4"` | +| 约束 | `limit` 控制最大星数(默认 5) | + +```json +{ "name": "满意度评分", "value": "4" } +``` + +### RelateField(关联审批单) + +| 属性 | 说明 | +|------|------| +| `componentName` | `RelateField` | +| value 格式 | 审批实例 ID 字符串 | +| 示例 | `"q-ZZ1sQaTIuYFpKI9aNC1g"` | +| 约束 | 须为**当前组织下已存在的审批实例 ID** | + +```json +{ "name": "关联审批单", "value": "q-ZZ1sQaTIuYFpKI9aNC1g" } +``` + +### SignatureField(签名控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `SignatureField` | +| value 格式 | 签名图片 mediaId | +| 约束 | 需要客户端交互签名,通常不支持 API 直接提交 | + +--- + +## 复合控件 + +### TableField(明细控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `TableField` | +| value 格式 | JSON 数组字符串,每个元素为一行数据的键值对 | +| 示例 | `'[{"商品名":"笔记本","数量":"2"},{"商品名":"钢笔","数量":"1"}]'` | +| 约束 | **不可嵌套 TableField**;**不可包含 DDMultiSelectField 和 DDPhotoField**;最大 100 行;总长度不超过 65535 字符 | + +模板结构(从 `form-schema` 获取): +```json +{ + "componentName": "TableField", + "props": { "label": "采购明细" }, + "children": [ + { "componentName": "TextField", "props": { "label": "商品名", "id": "TextField_XXX" } }, + { "componentName": "NumberField", "props": { "label": "数量", "id": "NumberField_YYY" } } + ] +} +``` + +提交时每行用子控件 label 作 key: +```json +{ + "name": "采购明细", + "value": "[{\"商品名\":\"笔记本\",\"数量\":\"2\"},{\"商品名\":\"钢笔\",\"数量\":\"1\"}]" +} +``` + +--- + +## API 不支持的控件 + +以下控件**不支持**通过创建实例 API 提交,遇到时应告知用户需在钉钉客户端补充: + +| 控件 | componentName | 原因 | +|------|---------------|------| +| 文字说明 | `TextNote` | 纯展示,不收集数据 | +| 计算公式 | `CalculateField` | 由系统自动计算,不可手动填写 | +| 流水号 | `SeqNumberField` | 由系统自动生成 | +| OCR 文本识别 | `OcrTextField` | 需要客户端 OCR 交互 | +| OCR 身份证识别 | `OcrIdCardField` | 需要客户端 OCR 交互 | +| 附件控件 | `DDAttachment` | value 需要 spaceId、fileName、fileSize、fileType、fileId,须通过钉盘上传接口获取,CLI 尚未封装 | + +> **部分支持的控件:** `DDPhotoField`(图片控件)**支持通过 URL 直接提交**,但不支持本地文件上传(CLI 未封装钉盘 CDN 上传流程)。若用户只有本地文件,需告知在钉钉客户端补充。详见本文 [DDPhotoField](#ddphotofield图片控件) 章节。 + +> **套件类控件(暂不支持)** — `InvoiceField`(发票)、`RecipientAccountField`(收款账户)等业务套件控件当前暂不支持通过 CLI 发起,包含这些控件的审批模板请直接在钉钉客户端操作。 + +--- + +## 组装优先级 + +1. **每次发起前都重新调用 `form-schema`**,不得复用旧结果(模板可能已被修改) +2. 先读 `form-schema` 返回的 `content`,识别所有控件的 `label`、`componentName`、`options`、`props.required` +3. **检查是否存在不支持控件且为必填项(`props.required: true`)**,若有则直接告知用户该模板不支持通过 CLI 发起,请在钉钉客户端操作 +4. 按本文档中每种控件的 value 格式组装 `formComponentValues` +5. **不要把 `form-schema` 的 `content` 当成可直接提交的模板** +6. 遇到 API 不支持的控件(非必填),跳过并告知用户 diff --git a/skills/mono/references/products/oa/oa-process-nodes.md b/skills/mono/references/products/oa/oa-process-nodes.md new file mode 100644 index 00000000..b3a93ce9 --- /dev/null +++ b/skills/mono/references/products/oa/oa-process-nodes.md @@ -0,0 +1,374 @@ +# OA 审批流程节点与审批人规则参考 + +本文档描述钉钉 OA 审批的流程节点类型、审批模式、条件分支和审批人选择规则,用于理解审批模板结构和正确填写 `create-instance` 的节点参数。 + +--- + +## 流程结构概览 + +审批流程是一个嵌套树结构: + +- **根节点**:发起人节点(`type: "start"`,`nodeId: "sid-startevent"`),固定不可删除 +- **后续节点**:通过 `childNode` 链接形成链式结构 +- **分支节点**:条件分支(`route` + `condition`)或并行分支(`parallel`) +- 当没有后续节点时,`childNode` 字段**必须省略**(不可设为 `null`) + +--- + +## 7 种节点类型 + +### 1. 发起人节点(start) + +| 属性 | 值 | +|------|-----| +| `type` | `start` | +| `nodeId` | `sid-startevent`(固定) | +| `properties` | `{}`(空对象) | + +唯一、不可删除。是流程的起点。 + +### 2. 审批人节点(approver) + +核心决策节点,有审批/拒绝权限。 + +| 属性 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `actionerRules` | Array | 是 | 审批人选择规则,至少一条 | +| `activateType` | String | 是 | 多人审批模式(见下方) | +| `approvalType` | String | 是 | 固定 `"MANUAL"` | +| `agreeAll` | Boolean | 是 | `true` 全部通过 / `false` 任一通过 | +| `noneActionerAction` | String | 否 | 如 `"admin"`(找不到审批人时转管理员) | + +支持全部 10 种 actionerRules 类型。 + +### 3. 办理人节点(handler) + +执行工作,无审批决策权。 + +| 属性 | 类型 | 必填 | +|------|------|------| +| `actionerRules` | Array | 是 | +| `activateType` | String | 是 | + +支持 9 种 actionerRules(不支持 `target_matrix_approval`)。 + +### 4. 抄送人节点(notifier) + +仅接收通知,无决策权。 + +| 属性 | 类型 | 必填 | +|------|------|------| +| `actionerRules` | Array | 是 | + +支持多条 actionerRules 组合在一个节点中,实现同时抄送多类人员。 + +### 5. 条件分支(route + condition) + +条件路由节点,包含多个条件分支。 + +**route 节点:** +- `type: "route"` +- `conditionNodes[]`:分支数组,按优先级排序,**默认分支必须在最后** +- `properties: {}` + +**condition 节点(conditionNodes 的每个元素):** +- `type: "condition"` +- `isdefault: true`:标记默认分支 +- `properties.conditions`:二维条件数组 + - 外层数组:多个条件组,**OR 关系** + - 内层数组:多个条件对象,**AND 关系** + - 默认分支:`[[]]`(一个空组) + +### 6. 并行分支(parallel) + +多个分支同时执行,全部完成后才继续。 + +| 属性 | 说明 | +|------|------| +| `branches[]` | 分支数组 | +| `branches[].name` | 分支名称 | +| `branches[].childNode` | 该分支的第一个节点 | + +### 7. 付款人节点(payer) + +财务付款节点。 + +| 属性 | 说明 | +|------|------| +| `actionerRules` | 审批人规则 | +| `paymentConfig.amountField` | 金额控件 ID | +| `paymentConfig.accountField` | 收款账户控件 ID | + +--- + +## 多人审批模式 + +| 模式 | `activateType` | `agreeAll` | 说明 | +|------|---------------|-----------|------| +| 会签 | `"ALL"` | `true` | 所有审批人都必须审批通过 | +| 或签 | `"ALL"` | `false` | 任一审批人审批即可 | +| 依次审批 | `"ONE_BY_ONE"` | `true` | 按顺序逐级审批 | + +--- + +## 10 种审批人选择规则(actionerRules) + +### 1. 指定成员(target_approval) + +明确指定具体人员。 + +```json +{ + "type": "target_approval", + "approvals": [ + { "userName": "张三", "workNo": "manager123" } + ], + "isEmpty": false +} +``` + +- `workNo` 必须通过 `dws aisearch person --keyword "<工号>" --dimension jobNumber --format json` 获取,**严禁编造** +- 在 `create-instance` 中对应 `directAppointedApprovers` 的 `staffIds` + +### 2. 直属主管(target_formula / reportLineManager) + +按汇报线找到直属主管。 + +```json +{ + "type": "target_formula", + "subType": "reportLineManager", + "formula": "ReportLineManager(corpId,originator,1)", + "isEmpty": false +} +``` + +- `formula` 中最后的数字 N 表示第 N 级主管 +- **重要区分:** 用户说"直属主管/直属领导/汇报线主管"才用此规则;用户说"主管审批/leader审批"(模糊)时默认用 `target_management`(部门主管) + +### 3. 发起人自己(target_originator) + +发起人自行审批。 + +```json +{ + "type": "target_originator", + "isEmpty": false +} +``` + +最简单的规则,只有 `type` 和 `isEmpty`。 + +### 4. 部门主管(target_management) + +从发起人所在部门层级找主管。 + +```json +{ + "type": "target_management", + "level": 1, + "autoUp": true, + "isEmpty": false +} +``` + +- `level: 1`:直接部门主管 +- `autoUp: true`:找不到时向上级部门搜索 +- **这是"主管审批/leader审批"模糊场景的默认选择** + +### 5. 表单部门主管(target_formula / managerOfDept) + +根据表单中部门控件选择的主管。 + +```json +{ + "type": "target_formula", + "subType": "managerOfDept", + "formula": "ManagerOfDept(corpId,$('DepartmentField_XXX'),1)", + "isEmpty": false +} +``` + +- `formula` 中引用表单中的 `DepartmentField` 控件 ID + +### 6. 发起人自选(target_select) + +发起人在提单时自行选择审批人。 + +```json +{ + "type": "target_select", + "select": ["allStaff"], + "range": {}, + "key": "manual_nodeId_xxxx_yyyy", + "multi": 1, + "isEmpty": false +} +``` + +- `select: ["allStaff"]`:可选全组织人员 +- `multi: 1`:单选 +- `key`:格式 `manual_{nodeId}_{hex}_{hex}` +- 在 `create-instance` 中对应 `targetSelectActioners` 的 `actionerKey` + +### 7. 角色标签主管(target_managers_labels) + +按角色标签找多级主管。 + +```json +{ + "type": "target_managers_labels", + "labelNames": ["项目经理"], + "labels": ["labelId123"], + "levels": [1], + "isEmpty": false +} +``` + +- `labels` 中的 ID 必须通过 `dws contact label get --names "<角色名>" --format json` 获取;已知角色名时直接查询,否则先 `dws contact label list --format json` 获取全部角色列表后匹配 + +### 8. 表单联系人(target_formcomponent_approval) + +从表单中的联系人控件读取审批人。 + +```json +{ + "type": "target_formcomponent_approval", + "paramKey": "InnerContactField_XXX", + "label": "项目负责人", + "isEmpty": false +} +``` + +- `paramKey` 指向表单中的 `InnerContactField` 控件 ID +- 该控件中填写的人即为审批人 + +### 9. 角色标签(target_label) + +按角色标签找人(如"财务"、"HR")。 + +```json +{ + "type": "target_label", + "labelNames": "财务", + "labels": "459272424", + "isEmpty": false +} +``` + +- `labels`:角色标签 ID(字符串),必须通过 `dws contact label get --names "<角色名>" --format json` 获取;未知角色名时先 `dws contact label list --format json` +- `labelNames`:角色显示名称 +- **严禁编造 label ID** + +### 10. 审批矩阵(target_matrix_approval) + +按审批矩阵规则确定审批人。 + +```json +{ + "type": "target_matrix_approval", + "matrixId": "xxx", + "roleColumnId": "yyy", + "expression": { + "subFilters": [...], + "operator": "AND" + } +} +``` + +- 仅适用于审批人节点 +- 目前尚在完善中 + +--- + +## 条件分支详解 + +### 条件类型 + +| `type` | 依据 | 关键字段 | +|--------|------|---------| +| `dingtalk_actioner_dept_condition` | 发起人部门/人员/角色 | `paramKey: "dingtalk_origin_dept"`, `conds[]` | +| `dingtalk_actioner_dept_component_condition` | 表单部门控件 | `paramKey: 控件ID`, `conds[]` | +| `dingtalk_actioner_range_condition` | 数值/金额/时长范围 | `lowerBound`(>=) / `lowerBoundNotEqual`(>) / `upperBoundEqual`(<=) / `upperBound`(<) / `boundEqual`(=) | +| `dingtalk_actioner_value_condition` | 单选匹配 | `paramKey: 控件ID`, `paramValues[]`(选项 key) | +| `dingtalk_multi_value_condition` | 多选匹配 | `paramKey: 控件ID`, `paramValues[]`, `matchType`(1=精确/2=全选/3=任一) | +| `dingtalk_actioner_cascade_component_condition` | 级联控件 | `paramValues[]`, `displayValues[]` | +| `dingtalk_actioner_boolean_condition` | 布尔值 | `boundEqual: true/false` | +| `dingtalk_rule_template` | 节假日判断 | `template`, `outVars` | +| `dingtalk_formula` | 公式 | `formula`, `formulaDisplay` | +| `dingtalk_biz_var_condition` | 业务变量 | `dsKey`, `conds[]` | +| `dingtalk_table_condition` | 明细内字段 | `parentFieldId`, `componentName`, `paramValue` | + +### 范围条件操作符 + +| 字段 | 含义 | +|------|------| +| `lowerBound` | >= (大于等于) | +| `lowerBoundNotEqual` | > (大于) | +| `upperBoundEqual` | <= (小于等于) | +| `upperBound` | < (小于) | +| `boundEqual` | = (等于) | + +### 默认分支 + +- `isdefault: true` +- `conditions: [[]]`(一个空的条件组) +- **必须放在 `conditionNodes[]` 的最后** + +--- + +## create-instance 中的节点参数映射 + +### directAppointedApprovers(指定审批人覆盖模板流程) + +当需要**不使用模板默认流程、直接指定审批人**时使用。 + +```json +{ + "directAppointedApprovers": [ + { + "staffIds": ["userId1", "userId2"], + "taskActionType": "NONE", + "staffId": "" + } + ] +} +``` + +| 字段 | 说明 | +|------|------| +| `staffIds` | 审批人 userId 列表(通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 获取;多结果须消歧) | +| `taskActionType` | `NONE`(单人)/ `AND`(会签)/ `OR`(或签) | +| `staffId` | 留空字符串 | + +### targetSelectActioners(自选审批人) + +当模板流程中存在**自选审批节点**(`target_select` 类型)时必填。 + +```json +{ + "targetSelectActioners": [ + { + "actionerKey": "manual_nodeId_xxxx_yyyy", + "actionerStaffIds": ["userId1"] + } + ] +} +``` + +| 字段 | 说明 | +|------|------| +| `actionerKey` | 自选节点的规则 key,从审批流程节点信息接口获取 `actorKey` | +| `actionerStaffIds` | 操作人 userId 列表 | + +--- + +## 组装优先级 + +1. 先用 `forecast-process` 获取模板的流程节点结构(`workflowActivityRuleVOs`) +2. 根据节点中的 `activityType` 和 `targetSelect` 判断是否需要传入 `directAppointedApprovers` 或 `targetSelectActioners` +3. 如果预测返回 `targetSelect: true` 的自选节点,`targetSelectActioners` 必填 +4. 如果用户要求覆盖默认流程,使用 `directAppointedApprovers` +5. **所有 userId 必须通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 获取,严禁填姓名;多结果须消歧** + +> **交互优化:** 若用户在 `forecast-process` 前已指定审批人/抄送人姓名,`forecast-process` 返回自选节点后应自动映射,仅对未覆盖的自选节点追问,不要重复询问。详见 [oa.md](../oa.md) 交互优化原则。 diff --git a/skills/multi/dingtalk-misc/references/oa.md b/skills/multi/dingtalk-misc/references/oa.md index 054df385..d5dea401 100644 --- a/skills/multi/dingtalk-misc/references/oa.md +++ b/skills/multi/dingtalk-misc/references/oa.md @@ -120,6 +120,363 @@ Flags: --query string 关键字,匹配 processCode 或表单名称 (必填) ``` +### 按模板 processCode 查询表单 Schema 信息 + +> **说明:** 根据已知的 processCode 精确查询表单的完整 Schema,包括表单名称、状态、创建者、创建/修改时间以及表单组件 JSON(content 字段)。 + +``` +Usage: + dws oa approval form-schema [flags] +Example: + dws oa approval form-schema --process-code PROC-594AE140-6AA5-4BA4-AF0C-9E6F66DB1E0B +Flags: + --process-code string 表单模板 processCode (必填) +``` + +返回值字段: +- `result.processName` — 表单名称 +- `result.processCode` — 表单 processCode +- `result.processStatus` — 表单状态(如 `PUBLISHED`) +- `result.creator` — 创建者 userId +- `result.gmtCreate` / `result.gmtModified` — 创建/修改时间(毫秒时间戳) +- `result.processIconUrl` — 表单图标 URL +- `result.processDescription` — 表单描述 +- `result.content` — 表单组件 JSON 字符串,包含表单项(items)和标题等配置 + +### 流程预测 + +``` +Usage: + dws oa approval forecast-process [flags] +Example: + # 简单预测 + dws oa approval forecast-process --process-code PROC-xxx --dept-id -1 --form-values '{"单行输入框":"测试内容"}' + # 指定部门预测 + dws oa approval forecast-process --process-code PROC-xxx --dept-id 12345 --form-values '{"金额":"5000"}' + # 高级用法:传入完整 JSON + dws oa approval forecast-process --request '{"processCode":"PROC-xxx","deptId":-1,"formComponentValues":[[{"name":"单行输入框","value":"测试"}]]}' +Flags: + --process-code string 审批模板 processCode(简单模式必填) + --form-values string 表单值 JSON,格式 '{"控件名称":"值"}'(简单模式必填) + --dept-id string 发起人所在部门 ID,根部门填 -1(简单模式必填) + --request string 完整请求体 JSON(高级模式,与简单模式互斥) +``` + +> **注意:** forecast 接口的 `formComponentValues` 比 create-instance 多一层数组包裹(`[[{...}]]`),CLI 简单模式已自动处理,高级模式需自行包裹。`processCode`、`deptId`、`formComponentValues` 三个字段均为必填,`userId` 由系统从登录态自动填充。 + +#### 流程预测的作用 + +在 `create-instance` 之前调用 `forecast-process`,可以根据已填写的表单值预测审批流程走向,核心价值有两个: + +1. **展示流程路径** — 告诉用户这个审批会经过哪些节点(审批人、抄送人、条件分支),让用户在提交前就知道流程走向。 +2. **识别自选审批人节点** — 返回中 `targetSelect: true` 的节点需要用户手动选择审批人/抄送人,Agent 应提示用户选人,并将结果传入 `create-instance` 的 `targetSelectActioners`。 + +#### 返回值关键字段 + +| 字段 | 含义 | +|------|------| +| `result.forecastSuccess` | 预测是否成功 | +| `result.staticWorkflow` | 是否为静态流程(无条件分支) | +| `result.workflowForecastNodes` | 流程节点路径,每个节点包含 `activityId` 和 `outIds`(下一跳) | +| `result.workflowActivityRuleVOs` | **重点**:每个节点的详细规则,包含节点类型、审批人、是否自选等 | + +#### `workflowActivityRuleVOs` 节点字段解读 + +| 字段 | 含义 | +|------|------| +| `activityId` | 节点 ID | +| `workflowActor.actorKey` | 自选节点的规则 key,即 `targetSelectActioners` 中 `actionerKey` 的值 | +| `activityName` | 节点名称(如"审批人"、"抄送人") | +| `activityType` | 节点类型:`target_approval`(已指定审批人)、`target_select`(需自选)、`target_notifier`(抄送) | +| `targetSelect` | **`true` 表示需要用户自选审批人/抄送人** | +| `activityActioners` | 已确定的处理人列表(含 `emplId`、`name`) | +| `workflowActor.actorType` | 角色类型:`approver`(审批人)、`notifier`(抄送人) | +| `workflowActor.approvalMethod` | 多人审批方式:`ONE_BY_ONE`(依次审批) | +| `workflowActor.actorSelectionType` | 选人范围:`allStaff`(全员可选)等 | +| `prevActivityId` | 上一节点 ID | + +#### Agent 处理流程 + +``` +1. 调用 forecast-process,传入 processCode + form-values +2. 遍历 workflowActivityRuleVOs: + a. 向用户展示每个节点的名称、类型、已指定处理人 + b. 若 targetSelect == true: + - 提示用户"节点「{activityName}」需要您自选{actorType}人" + - 使用 dws aisearch person --keyword "<姓名>" --dimension name --format json 帮用户查找并选人 + - 记录 activityId 和用户选择的 userIds +3. 将自选结果组装为 targetSelectActioners,传入 create-instance 高级模式 --request +``` + +#### 自选节点 → `targetSelectActioners` 组装示例 + +假设 forecast 返回两个自选节点: + +```json +{ + "targetSelectActioners": [ + { + "actionerKey": "manual_33ff_89cb_da91_e3aa", + "actionerStaffIds": ["userId_选人A"] + }, + { + "actionerKey": "manual_a29e_9633_f8b7_7291", + "actionerStaffIds": ["userId_选人B"] + } + ] +} +``` + +此字段通过 `create-instance --request` 的高级模式传入。`actionerKey` 来自 forecast 返回的 `workflowActor.actorKey`。 + +### 发起审批实例 + +#### 执行摘要 + +- **如果用户未明确给出 `processCode`,必须固定走 `search-forms` → `form-schema` → 收集表单值 → `forecast-process` → 自选节点选人 → `create-instance`**,不要跳过 `form-schema` 直接拼请求。 +- **如果用户明确给出 `processCode`,固定走 `form-schema` → 收集表单值 → `forecast-process` → 自选节点选人 → `create-instance`**,不要跳过 `form-schema` 直接拼请求。 +- **`form-schema` 返回的 `content` 不是创建 payload 的原样模板。** 它主要用于识别控件 `label`(即 name)、`id`、控件类型(componentName)和选项值范围;真正的 `formComponentValues` 中 `value` 结构以本文的控件值格式表为准。 +- **`forecast-process` 返回的自选节点必须在发起前让用户选人。** 若 `workflowActivityRuleVOs` 中有 `targetSelect: true` 的节点,必须提示用户选择处理人,并将结果通过 `targetSelectActioners` 传入 `create-instance`。 +- **所有人员类参数使用 userId。** 若用户给的是姓名,先用 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 解析成 userId。**严禁把姓名直接写进** `approvers`、`ccList`、`directAppointedApprovers`、`targetSelectActioners` 或表单人员控件。 +- **创建实例前一次性汇总确认。** `create-instance` 是写操作,执行前一次性展示模板、表单值、流程预测结果和审批人/抄送人供用户确认。 + +#### 严禁行为 + +- **严禁跳过 `form-schema`。** 未拿到表单 Schema 前,不得调用 `create-instance`。 +- **严禁复用旧的 Schema 结果。** 每次发起实例前都必须重新调用 `form-schema`,模板可能已被修改。 +- **严禁在存在不支持必填控件时强行发起。** 若 `form-schema` 返回的必填控件中有不支持类型(如附件等),直接告知用户不支持通过 CLI 发起。 +- **严禁把 `form-schema` 返回的 `content` 当成可直接提交的 payload 模板。** +- **严禁把姓名直接写进 `approvers`、`ccList`、`directAppointedApprovers`、`targetSelectActioners` 或表单人员控件。** 必须先通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 转成 userId。 +- **严禁在未得到用户确认前直接执行真实提单。** +- **严禁猜测控件名称或选项值。** 必须从 `form-schema` 返回中提取。 +- **严禁跳过 `forecast-process` 中的自选节点选人。** 若预测返回 `targetSelect: true` 的节点,必须让用户选人后再发起。 + +#### 最小判断表 + +| 你手上有什么 | 下一步 | +|---|---| +| 只有口语需求,比如"帮我发起请假审批" | 先 `search-forms --query 请假` | +| 已拿到 `processCode` | 直接 `form-schema --process-code ` | +| 已拿到 Schema | 向用户展示控件列表,收集表单值 | +| 已收集表单值 | `forecast-process` 预测流程走向 | +| 预测返回有 `targetSelect: true` 节点 | 让用户为自选节点选人(`dws aisearch person --keyword "<姓名>" --dimension name --format json` 解析姓名) | +| 预测完成,自选节点已选人 | 汇总确认后 `create-instance --yes` | +| 用户明确说"不走模板流程,直接指定审批人" | 使用 `directAppointedApprovers`(高级模式) | + +#### 工作流 + +``` +1. search-forms --query <关键词> → 拿到 processCode(若已有则跳过) +2. form-schema --process-code → 拿到控件列表、类型、选项值 +3. 检查 Schema 中是否有不支持的必填控件 → 若有则直接告知用户不支持发起 +4. 收集表单值 → 向用户展示控件列表,收集用户填写的表单值 +5. forecast-process → 根据表单值预测流程走向,识别自选节点 +6. 自选节点选人 → 若预测返回 targetSelect=true 的节点,让用户选人(用 dws aisearch person --keyword "<姓名>" --dimension name --format json 解析姓名) +7. 汇总确认后 create-instance --yes → 展示完整信息(表单值 + 流程路径 + 审批人),用户确认后执行发起 +``` + +> **IMPORTANT:每次发起实例前都必须重新调用 `form-schema` 查询模板。** 即使用户之前查询过同一个 processCode,模板可能已被修改(控件增减、选项变更、必填属性调整等),不得复用旧的 Schema 结果。 + +#### 交互优化原则 + +> **核心目标:流程清晰,步骤有序,避免重复询问。** + +1. **先查 Schema 再收集表单值(步骤 2→4):** `form-schema` 后向用户展示需要填写的控件列表,然后一次性收集全部表单值。不要在未拿到 Schema 前就问用户填什么。 + +2. **流程预测后再选自选审批人(步骤 5→6):** `forecast-process` 返回流程路径和自选节点后: + - 先向用户展示完整的流程路径(经过哪些节点、各节点处理人) + - 对 `targetSelect: true` 的节点,提示用户"节点「{activityName}」需要您自选{actorType}人" + - 用 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 帮用户查找并选人 + - 若有多个自选节点,一次性收集所有自选节点的选人结果 + +3. **单次汇总确认(步骤 7):** 发起前一次性展示完整信息供用户确认: + - 审批模板名称 + - 表单各控件值 + - 流程预测结果(审批路径) + - 各节点审批人/抄送人(含自选节点选人结果) + +**反例(禁止):** +- 未查 Schema 就直接问用户填什么表单值 +- 流程预测后逐个节点分别询问选人,而非一次性收集 +- 用户确认前直接执行发起 + +``` +Usage: + dws oa approval create-instance [flags] +Example: + # 简单发起(Agent 在汇总确认后需加 --yes) + dws oa approval create-instance --process-code PROC-xxx --form-values '{"单行输入框":"测试内容"}' --yes + # 指定审批人(OR=或签,AND=会签,NONE=单人) + dws oa approval create-instance --process-code PROC-xxx --form-values '{"单行输入框":"测试"}' --approvers "userId1,userId2" --approvers-action-type OR --yes + # 指定抄送人 + dws oa approval create-instance --process-code PROC-xxx --form-values '{"单行输入框":"测试"}' --cc-list "userId1" --cc-position START --yes + # 高级用法:传入完整 JSON(支持 directAppointedApprovers、targetSelectActioners 等全部字段) + dws oa approval create-instance --request '{"processCode":"PROC-xxx","deptId":-1,"formComponentValues":[{"name":"单行输入框","value":"测试"}]}' --yes +Flags: + --process-code string 审批模板 processCode(简单模式必填) + --form-values string 表单值 JSON,格式 '{"控件名称":"值"}'(简单模式必填) + --dept-id string 发起人所在部门 ID,根部门填 -1(可选,默认 -1) + --originator-user-id string 审批发起人 userId(可选,MCP 工具可从登录态获取) + --approvers string 审批人 userId 列表,多个用逗号分隔(可选) + --approvers-action-type string 审批类型:AND(会签)、OR(或签)、NONE(单人)(可选,默认 OR) + --cc-list string 抄送人 userId 列表,多个用逗号分隔(可选) + --cc-position string 抄送时间点:START/FINISH/START_FINISH(可选,默认 START) + --request string 完整请求体 JSON(高级模式,与简单模式互斥) + --yes 显式确认并发起审批;未提供时命令直接拒绝,不进入交互确认(Agent 必须先汇总并获得用户确认) +``` + +#### 两种模式 + +- **简单模式:** 通过 `--process-code` + `--form-values` + 可选 flags 发起,适合大多数场景 +- **高级模式:** 通过 `--request` 传入完整 JSON 请求体,支持 `directAppointedApprovers`、`targetSelectActioners` 等复杂字段 + +#### 组装 form-values + +`form-values` 是简单模式下的核心入参;传入时必须是一个 JSON 对象字符串,key 为控件 label,value 为该控件的提交值。组装原则: + +- 先用 `form-schema` 识别有哪些控件、每个控件的 `label`(name)、`componentName`(type)、选项值范围以及明细子控件结构。 +- **`form-schema` 返回的 `content` 不是可直接提交的原样模板。** 它提供控件定义,`value` 结构须按下方控件值格式表组装。 +- 提交时必须保证每个控件的 `name`(即 label)与 Schema 中的 `props.label` **完全一致**。 +- 如果用户提供的是人员信息,先用 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 转成 userId 后再写入对应控件。 +- 单选/多选控件提交的是选项文本(option value),该值从 `form-schema` 返回的选项定义中取得。 +- `InnerContactField`、`DepartmentField`、`TableField`、`DDDateRangeField`、`DDAttachment` 等控件的 `value` 结构各不相同,必须按下方格式表单独组装,不要套用文本控件的写法。 +- `TextNote`(文字说明)不收集数据,**不要**出现在 `formComponentValues` 中。 + +#### 表单控件值格式速查 + +> **重要:** `formComponentValues` 中每条记录的 `name` 必须与审批模板中控件的 `label`(即 `form-schema` 返回的 `content.items[].props.label`)**完全一致**。`value` 为字符串类型,最大 65535 字符。 +> +> **详细参考:** 每种控件的完整属性、约束和示例见 [oa-form-components.md](oa/oa-form-components.md)。组装前**必须先阅读该文档**。 + +| 控件类型 | componentName | value 格式 | 示例 | 备注 | +|---------|---------------|-----------|------|-------------------------------------------------------| +| 单行输入框 | `TextField` | 纯文本 | `"测试内容"` | | +| 多行输入框 | `TextareaField` | 纯文本 | `"第一行\n第二行"` | | +| 数字输入框 | `NumberField` | 数字字符串 | `"100"` | | +| 单选框 | `DDSelectField` | 选项文本 | `"同意"` | 必须与模板 options 中的 value 完全匹配 | +| 多选框 | `DDMultiSelectField` | JSON 数组字符串 | `'["选项A","选项B"]'` | 每个选项须与模板 options 匹配; | +| 日期控件 | `DDDateField` | `yyyy-MM-dd` | `"2026-07-27"` | | +| 时间区间 | `DDDateRangeField` | JSON 数组字符串 | `'["2026-07-27","2026-07-30"]'` | label 为数组 `["开始","结束"]`,用开始时间 label 作 name | +| 金额控件 | `MoneyField` | 数字字符串 | `"1500.50"` | 自动显示大写金额 | +| 电话控件 | `PhoneField` | 手机号字符串 | `"13800138000"` | | +| 联系人控件 | `InnerContactField` | userId | `"user123"` | 多人时传 JSON 数组 `'["user1","user2"]'`;choice="0"单选/"1"多 | +| 部门控件 | `DepartmentField` | 部门 ID | `"12345"` | 多部门传 JSON 数组;multiple=true 时支持多选 | +| 省市区控件 | `AddressField` | JSON 数组字符串 | `'["浙江省","杭州市","西湖区"]'` | 三级联动;needDetail=true 时末尾加详细地址 | +| 图片控件 | `DDPhotoField` | URL 数组转义字符串 | `"[\"http://example.com/img1.jpg\"]"` | 支持 URL 直接提交;**不支持本地文件上传** | +| 附件控件 | `DDAttachment` | JSON 数组转义字符串 | `"[{\"spaceId\":\"xxx\",\"fileName\":\"a.pdf\",\"fileSize\":\"333\",\"fileType\":\"pdf\",\"fileId\":\"xxx\"}]"` | **当前不支持通过 CLI 提交**,需钉盘上传接口获取 fileId 等字段 | +| 评分控件 | `StarRatingField` | 数字字符串 | `"4"` | limit 控制最大星数(默认 5) | +| 关联审批单 | `RelateField` | 审批实例 ID | `"q-xxx"` | 须为当前组织下已存在的实例 | +| 明细控件 | `TableField` | JSON 数组字符串 | `'[{"子控件名":"值1"},{"子控件名":"值2"}]'` | 不可嵌套 TableField;不可含 DDMultiSelectField/DDPhotoField;最大 100 行 | +| 身份证控件 | `IdCardField` | 身份证号 | `"330102199001011234"` | 内置格式校验 | +| 文字说明 | `TextNote` | — | — | **不收集数据**,不会出现在 formComponentValues 中 | + +#### API 不支持的控件 + +以下控件**不支持**通过创建实例 API 提交: + +- `TextNote`(文字说明)— 纯展示,不收集数据 +- `CalculateField`(计算公式)— 由系统自动计算 +- `SeqNumberField`(流水号)— 由系统自动生成 +- `OcrTextField` / `OcrIdCardField`(OCR 识别)— 需要客户端交互 +- **`DDAttachment`(附件控件)— 当前不支持通过 CLI 提交**,value 需要 spaceId、fileName、fileSize、fileType、fileId 字段,须通过钉盘上传附件接口获取 +- **套件类控件(暂不支持)** — `InvoiceField`(发票)、`RecipientAccountField`(收款账户)等业务套件控件当前暂不支持通过 CLI 发起,包含这些控件的审批模板请直接在钉钉客户端操作 + +> **部分支持的控件:** `DDPhotoField`(图片控件)**支持通过 URL 直接提交**(见上方速查表),仅不支持本地文件上传(CLI 未封装钉盘 CDN 上传流程)。若用户只有本地文件,需告知在钉钉客户端补充。 + +如果目标审批模板包含上述控件,不要硬拼 `form-values`;应告知用户这些字段无需填写或需要在钉钉客户端补充。 + +> **必填不支持控件判断规则:** 检查 `form-schema` 返回的控件列表,若存在上述不支持控件且其 `props.required` 为 `true`(必填项),则**直接告知用户该审批模板不支持通过 CLI 发起**,请在钉钉客户端操作。只有不支持控件为非必填时,才可跳过该控件继续发起。 + +#### 高级模式请求体字段(`--request` JSON 完整结构) + +| 字段 | 类型 | 必填 | 说明 | +|-----|------|------|------| +| `processCode` | String | 是 | 审批模板唯一码 | +| `originatorUserId` | String | 是 | 发起人 userId(MCP 工具可从登录态自动获取) | +| `deptId` | Long | 否 | 发起人部门 ID,根部门填 -1;approvers 已传时可不填 | +| `formComponentValues` | Array | 是 | 表单控件值列表,最大 150 条 | +| `approvers` | Array | 否 | 指定审批人列表(覆盖模板流程),最大 20 条 | +| `approvers[].actionType` | String | 否 | `AND`(会签)/ `OR`(或签)/ `NONE`(单人) | +| `approvers[].userIds` | Array | 否 | 审批人 userId 列表 | +| `ccList` | Array | 否 | 抄送人 userId 列表,最大 50 | +| `ccPosition` | String | 否 | `START` / `FINISH` / `START_FINISH` | +| `directAppointedApprovers` | Array | 否 | 指定审批人组(覆盖模板流程),结构见下方 | +| `targetSelectActioners` | Array | 否 | 自选审批人(模板中有自选节点时必填),最大 20 条 | + +#### 节点参数组装 + +> **详细参考:** 流程节点类型、审批模式、条件分支和 10 种审批人选择规则的完整说明见 [oa-process-nodes.md](oa/oa-process-nodes.md)。 + +**directAppointedApprovers(指定审批人覆盖模板流程):** + +当用户明确说"不走模板默认流程"或"直接指定 XX 审批"时使用。 + +```json +[ + { + "staffIds": ["userId1", "userId2"], + "taskActionType": "NONE", + "staffId": "" + } +] +``` +- `staffIds`:审批人 userId 列表(必须通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 获取,严禁填姓名) +- `taskActionType`:`NONE`(单人审批)/ `AND`(会签)/ `OR`(或签) + +**targetSelectActioners(模板有自选审批节点时使用):** + +当 `form-schema` 返回的模板流程中存在自选审批节点(`target_select` 类型)时必填。 + +```json +[ + { + "actionerKey": "manual_nodeId_xxxx_yyyy", + "actionerStaffIds": ["userId1"] + } +] +``` +- `actionerKey`:自选节点的规则 key,可通过获取审批单流程节点信息接口获取 `actorKey` +- `actionerStaffIds`:操作人 userId 列表 + +**审批类型(approvers actionType)说明:** + +| 值 | 含义 | 说明 | +|----|------|------| +| `AND` | 会签 | 所有审批人都必须审批通过 | +| `OR` | 或签 | 任一审批人审批即可 | +| `NONE` | 单人审批 | 只有一个审批人 | + +**抄送时间点(ccPosition)说明:** + +| 值 | 含义 | +|----|------| +| `START` | 审批发起时抄送 | +| `FINISH` | 审批完成时抄送 | +| `START_FINISH` | 发起和完成时都抄送 | + +#### 表单控件约束 + +- 单个表单最多 200 个控件 +- 控件 label(name)和 placeholder 最大 50 字符 +- `DDSelectField` / `DDMultiSelectField` 的选项 value 必须与模板中配置的选项文本完全一致 +- `TableField`(明细)内不可嵌套 `TableField`,不可包含 `DDMultiSelectField` 和 `DDPhotoField` +- `TextNote`(文字说明)不收集数据,无需在 `formComponentValues` 中传入 +- `InnerContactField` 的 userId 应为当前组织下在职成员 +- `DepartmentField` 应传入当前组织下存在的部门 ID +- `RelateField` 传入的审批实例 ID 应为当前组织下已存在的实例 + +#### 返回结果 + +创建成功后,返回的 `result` 字段即为新审批实例的 `processInstanceId`。建议向用户展示: + +``` +审批已创建成功: + +- 审批模板: (来自 form-schema) +- 审批实例 ID: (来自 create-instance 返回的 result) +``` + +后续可用该 processInstanceId 执行 `detail`、`tasks`、`records`、`revoke` 等操作。 ### 获取审批任务的被催办人 userId > **催办必须两步串联:** ① `ding-info` 获取被催办人 `userId` → ② `ding message send` 发送催办消息。禁止跳过第一步直接猜测 userId。 @@ -320,6 +677,17 @@ Flags: 用户说"我发起的审批" → `approval list-initiated`(需 --process-code,可从 list-forms 或 detail 获取) 用户说"有哪些审批表单/可见表单" → `approval list-forms` 用户说"搜索审批表单/查找xx审批表单/有没有xx表单" → `approval search-forms`(需 --query) +用户说"查表单schema/查表单结构/表单模板信息/查表单组件/查表单定义/表单有哪些字段/表单的字段信息" → `approval form-schema`(需 --process-code,可从 list-forms / search-forms / detail 获取) +用户说"预测审批流程/流程预测/审批走向/这个审批走哪些人/审批流程预览" → `approval forecast-process`(需 --process-code、--dept-id、--form-values) + - 在 `form-schema` 之后、`create-instance` 之前调用 + - 返回的 `workflowActivityRuleVOs` 中 `targetSelect: true` 的节点需要用户自选审批人 + - 自选结果组装为 `targetSelectActioners` 传入 `create-instance` +用户说"发起审批/提交审批/帮我发起XX审批/新建审批单/提一个XX审批/帮我提XX申请" → 五步流程:① `search-forms --query XX` 获取 processCode → ② `form-schema --process-code ` 获取表单字段定义 → ③ 阅读 [oa-form-components.md](oa/oa-form-components.md) 和 [oa-process-nodes.md](oa/oa-process-nodes.md) 后组装表单值 → ④ `forecast-process` 预测流程走向并识别自选节点 → ⑤ 若有自选节点让用户选人,确认后 `create-instance --yes` 发起 + - 如果用户已知 processCode,可跳过第①步 + - `--form-values` 的 key 必须与 `form-schema` 返回的控件 label 一致 + - `forecast-process` 返回自选节点时必须让用户选人,不得跳过 + - 执行前**必须向用户确认**表单内容、流程预测结果、审批人和抄送人 + - 示例:"帮我发起一个AI审批单" → ① `search-forms --query AI` → ② `form-schema --process-code ` → ③ 组装表单值 → ④ `forecast-process` → ⑤ 向用户确认流程走向和自选审批人后 `create-instance --yes` 用户说"催办审批/DING 一下审批人/提醒审批/催一下审批/催批/提醒审批人" → 先 `approval ding-info`(拿到被催办人 `userId`),再 `ding message send`(将 userId 作为 `--users` 传入;`--robot-code` 优先走 `$DINGTALK_DING_ROBOT_CODE` 或向用户确认;`--content` 由 agent 根据审批上下文撰写) - **禁止跳过 ding-info:** 不得自行猜测或编造 userId,必须先调用 `ding-info` 获取 - **机器人编码获取顺序:** ① `$DINGTALK_DING_ROBOT_CODE` 环境变量 → ② 用户显式提供 → ③ 询问用户 @@ -378,6 +746,9 @@ dws oa approval list-forms --cursor 0 --limit 100 --format json # 7b. 按关键字模糊搜索表单(快速定位 processCode) dws oa approval search-forms --query AI --format json +# 7c. 按 processCode 查询表单 Schema(获取表单结构、组件定义) +dws oa approval form-schema --process-code --format json + # 8. 查看自己发起的审批列表(--process-code 来自 list-forms 或 detail) dws oa approval list-initiated --process-code \ --start "2026-03-10T00:00:00+08:00" --end "2026-03-10T23:59:59+08:00" \ @@ -420,6 +791,22 @@ dws oa approval revert-activities --task-id --format json dws oa approval revert-task --instance-id --task-id --target-activity-id sid-startevent --action REVERT_FOR_RESUBMIT --remark "补充说明后重提" --format json # 17c. 退回到某个审批节点重新审批(targetActivityId 和 action 从 revert-activities 返回中获取) dws oa approval revert-task --instance-id --task-id --target-activity-id --action REVERT_FOR_APPROVAL --remark "重新审批" --format json + +# 18. 发起审批(完整流程:搜表单 → 查 Schema → 收集表单值 → 流程预测 → 自选节点选人 → 发起) +# 18a. 模糊搜索表单获取 processCode +dws oa approval search-forms --query AI --format json +# 18b. 查询表单 Schema 获取字段定义 +dws oa approval form-schema --process-code --format json +# 18c. 收集表单值(向用户展示控件列表,用户填写后组装 form-values) +# 18d. 流程预测(根据表单值预测审批走向,识别自选审批人节点;processCode/deptId/formValues 必填,userId 由登录态自动填充) +dws oa approval forecast-process --process-code --dept-id -1 --form-values '{"单行输入框":"测试内容"}' --format json +# 18e. 若 forecast 返回 targetSelect=true 的节点,用 dws aisearch person --keyword "<姓名>" --dimension name --format json 帮用户选人 +# 18f. 发起审批实例(form-values 的 key 须与 Schema 中控件 label 一致) +dws oa approval create-instance --process-code --form-values '{"单行输入框":"测试内容"}' --yes --format json +# 18g. 发起并指定审批人和抄送人 +dws oa approval create-instance --process-code --form-values '{"单行输入框":"测试"}' --approvers "userId1,userId2" --approvers-action-type OR --cc-list "userId3" --cc-position START --yes --format json +# 18h. 发起并使用 forecast 自选审批人结果(高级模式) +dws oa approval create-instance --request '{"processCode":"PROC-xxx","deptId":-1,"formComponentValues":[{"name":"单行输入框","value":"测试"}],"targetSelectActioners":[{"actionerKey":"manual_33ff_89cb_da91_e3aa","actionerStaffIds":["userId_选人A"]}]}' --yes --format json ``` ## 上下文传递表 @@ -431,6 +818,11 @@ dws oa approval revert-task --instance-id --task-id | `detail` | `processCode` | list-initiated 的 --process-code | | `list-forms` | `processCode` | list-initiated 的 --process-code | | `search-forms` | `processCode` | list-initiated 的 --process-code | +| `form-schema` | `processCode`, `processName`, `content` | 查看表单结构定义;`content` 字段包含表单组件 JSON,可解析获取字段列表;**控件 label 作为 create-instance --form-values 的 key** | +| `search-forms` → `form-schema` | `processCode` → 表单字段定义 | forecast-process / create-instance 的 --process-code 和 --form-values 填写依据 | +| `forecast-process` | `workflowActivityRuleVOs`(`activityId`, `targetSelect`, `activityActioners`, `workflowActor`) | ① 向用户展示流程走向和各节点处理人;② `targetSelect: true` 的节点需用户自选审批人,`workflowActor.actorKey` 作为 `targetSelectActioners` 的 `actionerKey` 传入 create-instance | +| `search-forms` → `form-schema` → `forecast-process` | `processCode` → 字段定义 → 流程走向 + 自选节点 | create-instance 的完整上下文:表单值 + 流程路径 + targetSelectActioners | +| `create-instance` | `result`(processInstanceId) | detail / tasks / records / revoke 等的 --instance-id,可跟踪已发起的审批 | | `ding-info` | `userId` | ding message send 的 --users(多个逗号拼接);**robotCode 优先走 `$DINGTALK_DING_ROBOT_CODE` 环境变量,content 由 agent 根据审批上下文撰写;返回空时报错并停止** | | `revert-activities` | `activityId`, `revertAction`, `activityName` | revert-task 的 --target-activity-id 和 --action;**返回空时必须告知用户"无可回退节点"** | @@ -459,6 +851,16 @@ dws oa approval revert-task --instance-id --task-id - `ding-info` 返回空或报错时,必须明确告知用户"无法获取该任务的被催办人信息"并停止 - DING 默认发应用内提醒(无成本);如需短信/电话提醒可加 `--type sms` 或 `--type call`(有成本,建议向用户确认) +- `form-schema` 的 `--process-code` 可从 `list-forms`、`search-forms` 或 `detail` 返回中提取;返回的 `content` 字段为 JSON 字符串,需解析后查看表单组件(items)定义。 +- `create-instance` 发起前**必须先阅读** [oa-form-components.md](oa/oa-form-components.md)(控件值格式)和 [oa-process-nodes.md](oa/oa-process-nodes.md)(流程节点规则),再调用 `form-schema` 获取表单字段定义,确保 `--form-values` 中的 key 与控件 label 完全一致。 +- `create-instance` 发起前**应先调用 `forecast-process`** 预测流程走向,识别自选审批人节点(`targetSelect: true`),让用户选人后再提交。 +- `create-instance` 的 `--form-values` 接受 JSON 格式 `'{"控件名称":"值"}'`,代码会自动转为 `[{"name":"控件名称","value":"值"}]`。 +- `create-instance` 简单模式适合常见场景;如需 `directAppointedApprovers`(指定审批人覆盖模板流程)或 `targetSelectActioners`(自选审批节点)等高级字段,使用 `--request` 传完整 JSON。`--request` 与简单模式 flags 互斥。 +- `create-instance` 会创建真实审批数据;Agent 只有在用户确认模板、表单值、流程路径和人员后才能传入 `--yes`。 +- `create-instance` 返回的 processInstanceId 可用于 `detail`、`tasks`、`records`、`revoke` 等后续操作。 +- `forecast-process` 的 `processCode`、`deptId`、`formComponentValues` 三个字段均为必填(`userId` 由系统自动填充);`formComponentValues` 比 `create-instance` 多一层数组包裹(`[[{...}]]`),CLI 简单模式已自动处理。 +- `forecast-process` 返回 `workflowActivityRuleVOs` 中 `targetSelect: true` 的节点,其 `workflowActor.actorKey` 必须作为 `targetSelectActioners` 的 `actionerKey` 传入 `create-instance`。 + ## 自动化脚本 | 脚本 | 场景 | 用法 | diff --git a/skills/multi/dingtalk-misc/references/oa/oa-form-components.md b/skills/multi/dingtalk-misc/references/oa/oa-form-components.md new file mode 100644 index 00000000..e92d79a5 --- /dev/null +++ b/skills/multi/dingtalk-misc/references/oa/oa-form-components.md @@ -0,0 +1,346 @@ +# OA 审批表单控件参考 + +本文档详细描述钉钉 OA 审批中每种表单控件(componentName)在**发起审批实例**时 `formComponentValues` 的 `value` 格式、约束和注意事项。 + +> **核心原则:** `formComponentValues[].name` 必须与审批模板中控件的 `props.label` **完全一致**,`value` 为字符串类型(最大 65535 字符)。 + +--- + +## 通用约束 + +| 约束 | 说明 | +|------|------| +| 单表单最大控件数 | 200 | +| label / placeholder 最大长度 | 50 字符 | +| value 最大长度 | 65535 字符 | +| ID / bizAlias 唯一性 | 同一表单内不可重复 | +| TextNote | 不收集数据,不出现在 formComponentValues 中 | + +--- + +## 基础控件 + +### TextField(单行输入框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `TextField` | +| value 格式 | 纯文本字符串 | +| 示例 | `"测试内容"` | +| 约束 | 无特殊约束 | + +```json +{ "name": "单行输入框", "value": "测试内容" } +``` + +### TextareaField(多行输入框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `TextareaField` | +| value 格式 | 纯文本字符串,支持换行 | +| 示例 | `"第一行\n第二行"` | +| 约束 | 无 `ratio` 属性 | + +```json +{ "name": "多行输入框", "value": "第一行\n第二行\n第三行" } +``` + +### NumberField(数字输入框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `NumberField` | +| value 格式 | 数字字符串 | +| 示例 | `"100"` | +| 约束 | 适合数量、天数等纯数字场景 | + +```json +{ "name": "加班天数", "value": "3" } +``` + +### DDSelectField(单选框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDSelectField` | +| value 格式 | 选项文本字符串 | +| 示例 | `"同意"` | +| 约束 | **必须与模板 `options[].value` 完全匹配**,不可自行编造选项 | + +模板中的选项结构(从 `form-schema` 获取): +```json +"options": [ + { "key": "option_0", "value": "同意" }, + { "key": "option_1", "value": "不同意" } +] +``` + +提交时传选项的 `value` 文本: +```json +{ "name": "审批意见", "value": "同意" } +``` + +### DDMultiSelectField(多选框) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDMultiSelectField` | +| value 格式 | JSON 数组字符串,每个元素为选项文本 | +| 示例 | `'["选项A","选项B"]'` | +| 约束 | 每个选项须与模板 `options[].value` 匹配; | + +```json +{ "name": "兴趣爱好", "value": "[\"阅读\",\"运动\"]" } +``` + +### DDDateField(日期控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDDateField` | +| value 格式 | `yyyy-MM-dd` 格式字符串 | +| 示例 | `"2026-07-27"` | +| 约束 | 格式固定,不可传其他日期格式 | + +```json +{ "name": "请假日期", "value": "2026-07-27" } +``` + +### DDDateRangeField(时间区间控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDDateRangeField` | +| value 格式 | JSON 数组字符串 `[开始日期, 结束日期]` | +| 示例 | `'["2026-07-27","2026-07-30"]'` | +| 约束 | `props.label` 为数组 `["开始时间","结束时间"]`;提交时 `name` 使用**开始时间的 label** | + +模板中的 label 结构(从 `form-schema` 获取): +```json +"props": { "label": ["开始时间", "结束时间"] } +``` + +提交时用**开始时间 label** 作为 name: +```json +{ "name": "开始时间", "value": "[\"2026-07-27\",\"2026-07-30\"]" } +``` + +### PhoneField(电话控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `PhoneField` | +| value 格式 | 手机号字符串 | +| 示例 | `"13800138000"` | +| 约束 | `mode: "phone"` 为手机号 | + +```json +{ "name": "联系电话", "value": "13800138000" } +``` + +### IdCardField(身份证控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `IdCardField` | +| value 格式 | 身份证号字符串 | +| 示例 | `"330102199001011234"` | +| 约束 | 内置格式校验,须传合法身份证号 | + +```json +{ "name": "身份证号", "value": "330102199001011234" } +``` + +### TextNote(文字说明) + +| 属性 | 说明 | +|------|------| +| `componentName` | `TextNote` | +| value 格式 | — | +| 约束 | **不收集数据**,不出现在 formComponentValues 中 | + +> 遇到 TextNote 控件时直接跳过,不要尝试为它填写值。 + +--- + +## 增强控件 + +### MoneyField(金额控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `MoneyField` | +| value 格式 | 数字字符串 | +| 示例 | `"1500.50"` | +| 约束 | 系统自动显示大写金额(`notUpper: "0"` 时显示) | + +```json +{ "name": "报销金额", "value": "1500.50" } +``` + +### InnerContactField(联系人控件) + +| 属性 | 说明 | +|------|----------------------------------------------------| +| `componentName` | `InnerContactField` | +| value 格式 | userId 字符串,多人时为 JSON 数组字符串 | +| 示例(单选) | `"user123"` | +| 示例(多选) | `'["userId1","userId2"]'` | +| 约束 | `choice: "0"` 单选 / `"1"` 多选;userId 须为**当前组织下在职成员** | + +```json +{ "name": "项目负责人", "value": "[\"userId1\",\"userId2\"]" } +``` + +> **严禁直接写姓名。** 必须先通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 查询获取 userId;多结果时须让用户消歧确认。 + +### DepartmentField(部门控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `DepartmentField` | +| value 格式 | 部门 ID 字符串,多部门时为 JSON 数组字符串 | +| 示例(单选) | `"12345"` | +| 示例(多选) | `'["12345","67890"]'` | +| 约束 | `multiple: boolean` 控制单选/多选;部门 ID 须为**当前组织下存在的部门** | + +```json +{ "name": "所属部门", "value": "12345" } +``` + +### AddressField(省市区控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `AddressField` | +| value 格式 | JSON 数组字符串 `["省","市","区"]` | +| 示例 | `'["浙江省","杭州市","西湖区"]'` | +| 约束 | 三级联动选择器;`needDetail: true` 时末尾追加详细地址文本 | + +```json +{ "name": "办公地点", "value": "[\"浙江省\",\"杭州市\",\"西湖区\"]" } +``` + +### DDPhotoField(图片控件) + +> **支持通过图片 URL 提交,不支持本地文件上传。** 如果用户已有图片 URL(如公网可访问的图片链接),可直接填入 value 提交。CLI 尚未封装本地文件上传到钉盘 CDN 的流程,若用户只有本地文件而非 URL,需告知用户在钉钉客户端补充。 + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDPhotoField` | +| value 格式 | URL 数组转义字符串,即使只有一个 URL 也需数组形式 | +| 示例 | `"[\"http://example.com/img1.jpg\",\"http://example.com/img2.jpg\"]"` | +| 约束 | 支持 URL 直接提交;**不支持本地文件上传**(CLI 未封装钉盘上传流程); | + +```json +{ "name": "图片", "value": "[\"http://example.com/photo.jpg\"]" } +``` + +### DDAttachment(附件控件) + +> **[注意] 当前暂不支持通过 CLI 提交附件控件。** 附件控件的 value 需要包含 spaceId、fileName、fileSize、fileType 和 fileId 字段,这些字段需要通过调用钉盘的上传附件接口获取,CLI 尚未封装此流程。包含附件控件的审批模板请在钉钉客户端操作。 + +| 属性 | 说明 | +|------|------| +| `componentName` | `DDAttachment` | +| value 格式 | JSON 数组转义字符串,每个元素包含 spaceId、fileName、fileSize、fileType、fileId | +| 示例(参考) | `"[{\"spaceId\":\"163xxx\",\"fileName\":\"2644.JPG\",\"fileSize\":\"333\",\"fileType\":\"jpg\",\"fileId\":\"643xxx\"}]"` | +| 约束 | **当前不支持通过 CLI 提交**;各字段需通过钉盘上传附件接口获取 | + +### StarRatingField(评分控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `StarRatingField` | +| value 格式 | 数字字符串 | +| 示例 | `"4"` | +| 约束 | `limit` 控制最大星数(默认 5) | + +```json +{ "name": "满意度评分", "value": "4" } +``` + +### RelateField(关联审批单) + +| 属性 | 说明 | +|------|------| +| `componentName` | `RelateField` | +| value 格式 | 审批实例 ID 字符串 | +| 示例 | `"q-ZZ1sQaTIuYFpKI9aNC1g"` | +| 约束 | 须为**当前组织下已存在的审批实例 ID** | + +```json +{ "name": "关联审批单", "value": "q-ZZ1sQaTIuYFpKI9aNC1g" } +``` + +### SignatureField(签名控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `SignatureField` | +| value 格式 | 签名图片 mediaId | +| 约束 | 需要客户端交互签名,通常不支持 API 直接提交 | + +--- + +## 复合控件 + +### TableField(明细控件) + +| 属性 | 说明 | +|------|------| +| `componentName` | `TableField` | +| value 格式 | JSON 数组字符串,每个元素为一行数据的键值对 | +| 示例 | `'[{"商品名":"笔记本","数量":"2"},{"商品名":"钢笔","数量":"1"}]'` | +| 约束 | **不可嵌套 TableField**;**不可包含 DDMultiSelectField 和 DDPhotoField**;最大 100 行;总长度不超过 65535 字符 | + +模板结构(从 `form-schema` 获取): +```json +{ + "componentName": "TableField", + "props": { "label": "采购明细" }, + "children": [ + { "componentName": "TextField", "props": { "label": "商品名", "id": "TextField_XXX" } }, + { "componentName": "NumberField", "props": { "label": "数量", "id": "NumberField_YYY" } } + ] +} +``` + +提交时每行用子控件 label 作 key: +```json +{ + "name": "采购明细", + "value": "[{\"商品名\":\"笔记本\",\"数量\":\"2\"},{\"商品名\":\"钢笔\",\"数量\":\"1\"}]" +} +``` + +--- + +## API 不支持的控件 + +以下控件**不支持**通过创建实例 API 提交,遇到时应告知用户需在钉钉客户端补充: + +| 控件 | componentName | 原因 | +|------|---------------|------| +| 文字说明 | `TextNote` | 纯展示,不收集数据 | +| 计算公式 | `CalculateField` | 由系统自动计算,不可手动填写 | +| 流水号 | `SeqNumberField` | 由系统自动生成 | +| OCR 文本识别 | `OcrTextField` | 需要客户端 OCR 交互 | +| OCR 身份证识别 | `OcrIdCardField` | 需要客户端 OCR 交互 | +| 附件控件 | `DDAttachment` | value 需要 spaceId、fileName、fileSize、fileType、fileId,须通过钉盘上传接口获取,CLI 尚未封装 | + +> **部分支持的控件:** `DDPhotoField`(图片控件)**支持通过 URL 直接提交**,但不支持本地文件上传(CLI 未封装钉盘 CDN 上传流程)。若用户只有本地文件,需告知在钉钉客户端补充。详见本文 [DDPhotoField](#ddphotofield图片控件) 章节。 + +> **套件类控件(暂不支持)** — `InvoiceField`(发票)、`RecipientAccountField`(收款账户)等业务套件控件当前暂不支持通过 CLI 发起,包含这些控件的审批模板请直接在钉钉客户端操作。 + +--- + +## 组装优先级 + +1. **每次发起前都重新调用 `form-schema`**,不得复用旧结果(模板可能已被修改) +2. 先读 `form-schema` 返回的 `content`,识别所有控件的 `label`、`componentName`、`options`、`props.required` +3. **检查是否存在不支持控件且为必填项(`props.required: true`)**,若有则直接告知用户该模板不支持通过 CLI 发起,请在钉钉客户端操作 +4. 按本文档中每种控件的 value 格式组装 `formComponentValues` +5. **不要把 `form-schema` 的 `content` 当成可直接提交的模板** +6. 遇到 API 不支持的控件(非必填),跳过并告知用户 diff --git a/skills/multi/dingtalk-misc/references/oa/oa-process-nodes.md b/skills/multi/dingtalk-misc/references/oa/oa-process-nodes.md new file mode 100644 index 00000000..b3a93ce9 --- /dev/null +++ b/skills/multi/dingtalk-misc/references/oa/oa-process-nodes.md @@ -0,0 +1,374 @@ +# OA 审批流程节点与审批人规则参考 + +本文档描述钉钉 OA 审批的流程节点类型、审批模式、条件分支和审批人选择规则,用于理解审批模板结构和正确填写 `create-instance` 的节点参数。 + +--- + +## 流程结构概览 + +审批流程是一个嵌套树结构: + +- **根节点**:发起人节点(`type: "start"`,`nodeId: "sid-startevent"`),固定不可删除 +- **后续节点**:通过 `childNode` 链接形成链式结构 +- **分支节点**:条件分支(`route` + `condition`)或并行分支(`parallel`) +- 当没有后续节点时,`childNode` 字段**必须省略**(不可设为 `null`) + +--- + +## 7 种节点类型 + +### 1. 发起人节点(start) + +| 属性 | 值 | +|------|-----| +| `type` | `start` | +| `nodeId` | `sid-startevent`(固定) | +| `properties` | `{}`(空对象) | + +唯一、不可删除。是流程的起点。 + +### 2. 审批人节点(approver) + +核心决策节点,有审批/拒绝权限。 + +| 属性 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `actionerRules` | Array | 是 | 审批人选择规则,至少一条 | +| `activateType` | String | 是 | 多人审批模式(见下方) | +| `approvalType` | String | 是 | 固定 `"MANUAL"` | +| `agreeAll` | Boolean | 是 | `true` 全部通过 / `false` 任一通过 | +| `noneActionerAction` | String | 否 | 如 `"admin"`(找不到审批人时转管理员) | + +支持全部 10 种 actionerRules 类型。 + +### 3. 办理人节点(handler) + +执行工作,无审批决策权。 + +| 属性 | 类型 | 必填 | +|------|------|------| +| `actionerRules` | Array | 是 | +| `activateType` | String | 是 | + +支持 9 种 actionerRules(不支持 `target_matrix_approval`)。 + +### 4. 抄送人节点(notifier) + +仅接收通知,无决策权。 + +| 属性 | 类型 | 必填 | +|------|------|------| +| `actionerRules` | Array | 是 | + +支持多条 actionerRules 组合在一个节点中,实现同时抄送多类人员。 + +### 5. 条件分支(route + condition) + +条件路由节点,包含多个条件分支。 + +**route 节点:** +- `type: "route"` +- `conditionNodes[]`:分支数组,按优先级排序,**默认分支必须在最后** +- `properties: {}` + +**condition 节点(conditionNodes 的每个元素):** +- `type: "condition"` +- `isdefault: true`:标记默认分支 +- `properties.conditions`:二维条件数组 + - 外层数组:多个条件组,**OR 关系** + - 内层数组:多个条件对象,**AND 关系** + - 默认分支:`[[]]`(一个空组) + +### 6. 并行分支(parallel) + +多个分支同时执行,全部完成后才继续。 + +| 属性 | 说明 | +|------|------| +| `branches[]` | 分支数组 | +| `branches[].name` | 分支名称 | +| `branches[].childNode` | 该分支的第一个节点 | + +### 7. 付款人节点(payer) + +财务付款节点。 + +| 属性 | 说明 | +|------|------| +| `actionerRules` | 审批人规则 | +| `paymentConfig.amountField` | 金额控件 ID | +| `paymentConfig.accountField` | 收款账户控件 ID | + +--- + +## 多人审批模式 + +| 模式 | `activateType` | `agreeAll` | 说明 | +|------|---------------|-----------|------| +| 会签 | `"ALL"` | `true` | 所有审批人都必须审批通过 | +| 或签 | `"ALL"` | `false` | 任一审批人审批即可 | +| 依次审批 | `"ONE_BY_ONE"` | `true` | 按顺序逐级审批 | + +--- + +## 10 种审批人选择规则(actionerRules) + +### 1. 指定成员(target_approval) + +明确指定具体人员。 + +```json +{ + "type": "target_approval", + "approvals": [ + { "userName": "张三", "workNo": "manager123" } + ], + "isEmpty": false +} +``` + +- `workNo` 必须通过 `dws aisearch person --keyword "<工号>" --dimension jobNumber --format json` 获取,**严禁编造** +- 在 `create-instance` 中对应 `directAppointedApprovers` 的 `staffIds` + +### 2. 直属主管(target_formula / reportLineManager) + +按汇报线找到直属主管。 + +```json +{ + "type": "target_formula", + "subType": "reportLineManager", + "formula": "ReportLineManager(corpId,originator,1)", + "isEmpty": false +} +``` + +- `formula` 中最后的数字 N 表示第 N 级主管 +- **重要区分:** 用户说"直属主管/直属领导/汇报线主管"才用此规则;用户说"主管审批/leader审批"(模糊)时默认用 `target_management`(部门主管) + +### 3. 发起人自己(target_originator) + +发起人自行审批。 + +```json +{ + "type": "target_originator", + "isEmpty": false +} +``` + +最简单的规则,只有 `type` 和 `isEmpty`。 + +### 4. 部门主管(target_management) + +从发起人所在部门层级找主管。 + +```json +{ + "type": "target_management", + "level": 1, + "autoUp": true, + "isEmpty": false +} +``` + +- `level: 1`:直接部门主管 +- `autoUp: true`:找不到时向上级部门搜索 +- **这是"主管审批/leader审批"模糊场景的默认选择** + +### 5. 表单部门主管(target_formula / managerOfDept) + +根据表单中部门控件选择的主管。 + +```json +{ + "type": "target_formula", + "subType": "managerOfDept", + "formula": "ManagerOfDept(corpId,$('DepartmentField_XXX'),1)", + "isEmpty": false +} +``` + +- `formula` 中引用表单中的 `DepartmentField` 控件 ID + +### 6. 发起人自选(target_select) + +发起人在提单时自行选择审批人。 + +```json +{ + "type": "target_select", + "select": ["allStaff"], + "range": {}, + "key": "manual_nodeId_xxxx_yyyy", + "multi": 1, + "isEmpty": false +} +``` + +- `select: ["allStaff"]`:可选全组织人员 +- `multi: 1`:单选 +- `key`:格式 `manual_{nodeId}_{hex}_{hex}` +- 在 `create-instance` 中对应 `targetSelectActioners` 的 `actionerKey` + +### 7. 角色标签主管(target_managers_labels) + +按角色标签找多级主管。 + +```json +{ + "type": "target_managers_labels", + "labelNames": ["项目经理"], + "labels": ["labelId123"], + "levels": [1], + "isEmpty": false +} +``` + +- `labels` 中的 ID 必须通过 `dws contact label get --names "<角色名>" --format json` 获取;已知角色名时直接查询,否则先 `dws contact label list --format json` 获取全部角色列表后匹配 + +### 8. 表单联系人(target_formcomponent_approval) + +从表单中的联系人控件读取审批人。 + +```json +{ + "type": "target_formcomponent_approval", + "paramKey": "InnerContactField_XXX", + "label": "项目负责人", + "isEmpty": false +} +``` + +- `paramKey` 指向表单中的 `InnerContactField` 控件 ID +- 该控件中填写的人即为审批人 + +### 9. 角色标签(target_label) + +按角色标签找人(如"财务"、"HR")。 + +```json +{ + "type": "target_label", + "labelNames": "财务", + "labels": "459272424", + "isEmpty": false +} +``` + +- `labels`:角色标签 ID(字符串),必须通过 `dws contact label get --names "<角色名>" --format json` 获取;未知角色名时先 `dws contact label list --format json` +- `labelNames`:角色显示名称 +- **严禁编造 label ID** + +### 10. 审批矩阵(target_matrix_approval) + +按审批矩阵规则确定审批人。 + +```json +{ + "type": "target_matrix_approval", + "matrixId": "xxx", + "roleColumnId": "yyy", + "expression": { + "subFilters": [...], + "operator": "AND" + } +} +``` + +- 仅适用于审批人节点 +- 目前尚在完善中 + +--- + +## 条件分支详解 + +### 条件类型 + +| `type` | 依据 | 关键字段 | +|--------|------|---------| +| `dingtalk_actioner_dept_condition` | 发起人部门/人员/角色 | `paramKey: "dingtalk_origin_dept"`, `conds[]` | +| `dingtalk_actioner_dept_component_condition` | 表单部门控件 | `paramKey: 控件ID`, `conds[]` | +| `dingtalk_actioner_range_condition` | 数值/金额/时长范围 | `lowerBound`(>=) / `lowerBoundNotEqual`(>) / `upperBoundEqual`(<=) / `upperBound`(<) / `boundEqual`(=) | +| `dingtalk_actioner_value_condition` | 单选匹配 | `paramKey: 控件ID`, `paramValues[]`(选项 key) | +| `dingtalk_multi_value_condition` | 多选匹配 | `paramKey: 控件ID`, `paramValues[]`, `matchType`(1=精确/2=全选/3=任一) | +| `dingtalk_actioner_cascade_component_condition` | 级联控件 | `paramValues[]`, `displayValues[]` | +| `dingtalk_actioner_boolean_condition` | 布尔值 | `boundEqual: true/false` | +| `dingtalk_rule_template` | 节假日判断 | `template`, `outVars` | +| `dingtalk_formula` | 公式 | `formula`, `formulaDisplay` | +| `dingtalk_biz_var_condition` | 业务变量 | `dsKey`, `conds[]` | +| `dingtalk_table_condition` | 明细内字段 | `parentFieldId`, `componentName`, `paramValue` | + +### 范围条件操作符 + +| 字段 | 含义 | +|------|------| +| `lowerBound` | >= (大于等于) | +| `lowerBoundNotEqual` | > (大于) | +| `upperBoundEqual` | <= (小于等于) | +| `upperBound` | < (小于) | +| `boundEqual` | = (等于) | + +### 默认分支 + +- `isdefault: true` +- `conditions: [[]]`(一个空的条件组) +- **必须放在 `conditionNodes[]` 的最后** + +--- + +## create-instance 中的节点参数映射 + +### directAppointedApprovers(指定审批人覆盖模板流程) + +当需要**不使用模板默认流程、直接指定审批人**时使用。 + +```json +{ + "directAppointedApprovers": [ + { + "staffIds": ["userId1", "userId2"], + "taskActionType": "NONE", + "staffId": "" + } + ] +} +``` + +| 字段 | 说明 | +|------|------| +| `staffIds` | 审批人 userId 列表(通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 获取;多结果须消歧) | +| `taskActionType` | `NONE`(单人)/ `AND`(会签)/ `OR`(或签) | +| `staffId` | 留空字符串 | + +### targetSelectActioners(自选审批人) + +当模板流程中存在**自选审批节点**(`target_select` 类型)时必填。 + +```json +{ + "targetSelectActioners": [ + { + "actionerKey": "manual_nodeId_xxxx_yyyy", + "actionerStaffIds": ["userId1"] + } + ] +} +``` + +| 字段 | 说明 | +|------|------| +| `actionerKey` | 自选节点的规则 key,从审批流程节点信息接口获取 `actorKey` | +| `actionerStaffIds` | 操作人 userId 列表 | + +--- + +## 组装优先级 + +1. 先用 `forecast-process` 获取模板的流程节点结构(`workflowActivityRuleVOs`) +2. 根据节点中的 `activityType` 和 `targetSelect` 判断是否需要传入 `directAppointedApprovers` 或 `targetSelectActioners` +3. 如果预测返回 `targetSelect: true` 的自选节点,`targetSelectActioners` 必填 +4. 如果用户要求覆盖默认流程,使用 `directAppointedApprovers` +5. **所有 userId 必须通过 `dws aisearch person --keyword "<姓名>" --dimension name --format json` 获取,严禁填姓名;多结果须消歧** + +> **交互优化:** 若用户在 `forecast-process` 前已指定审批人/抄送人姓名,`forecast-process` 返回自选节点后应自动映射,仅对未覆盖的自选节点追问,不要重复询问。详见 [oa.md](../oa.md) 交互优化原则。 diff --git a/test/scripts/changelog_pr_gate_test.go b/test/scripts/changelog_pr_gate_test.go index 8418b64a..af42e8c8 100644 --- a/test/scripts/changelog_pr_gate_test.go +++ b/test/scripts/changelog_pr_gate_test.go @@ -537,6 +537,16 @@ func TestChangelogPRFastPathWorkflowContract(t *testing.T) { t.Error("full race shards must retain enough package-level time for internal/app") } + darwinStart := strings.Index(admission, "\n test-darwin:\n") + darwinEnd := strings.Index(admission, "\n test-windows:\n") + if darwinStart < 0 || darwinEnd <= darwinStart { + t.Fatal("Code Admission workflow missing macOS test job boundaries") + } + darwinJob := admission[darwinStart:darwinEnd] + if !strings.Contains(darwinJob, `go test -v -race -count=1 -timeout=12m ./internal/keychain ./internal/auth ./internal/app`) { + t.Error("macOS race tests must retain enough package-level time for internal/app") + } + coverageStart := strings.Index(admission, "\n coverage:\n") coverageEnd := strings.Index(admission, "\n policy:\n") if coverageStart < 0 || coverageEnd <= coverageStart {