Files
dingMCP/skills/dingtalk-approval/references/local-drafts.md
T

7.0 KiB

Local submission drafts

Use a local draft only when the user explicitly asks to draft, save, continue, list, or delete an approval submission. A local draft is not an upstream DingTalk or XFT draft. Store it only on the machine running the Agent, never on the approval gateway, in the repository, in chat memory, or in another cloud service.

Storage contract

Use the current OS user's private application-data directory:

  • Windows: %LOCALAPPDATA%\DingTalkApproval\drafts
  • macOS: ~/Library/Application Support/DingTalkApproval/drafts
  • Linux: ${XDG_DATA_HOME:-~/.local/share}/dingtalk-approval/drafts

On POSIX, create the directory with mode 0700 and each file with mode 0600. On Windows, retain a private current-user profile ACL and never grant access to other users. Do not follow symlinks in the draft directory. Use a random UUID plus .json as the filename, write a temporary file in the same directory, then atomically rename it. If the host cannot safely read and write local files, tell the user persistent local drafts are unavailable; do not silently fall back to gateway storage.

On every create, save, list, resume, delete, or submit operation, remove ordinary editing drafts whose updatedAt is more than 90 days old. Never age-delete a submit_uncertain draft until its submission outcome is reconciled or the user explicitly deletes it after seeing the warning.

Store schema version 1 in this shape:

{
  "schemaVersion": 1,
  "kind": "dingtalk-approval-local-draft",
  "draftId": "<uuid>",
  "title": "上海客户拜访差旅报销",
  "source": "xft",
  "templateName": "差旅报销单",
  "status": "editing",
  "values": {},
  "optionalFieldDecision": {
    "skipped": []
  },
  "attachments": [
    {
      "fieldName": "invoiceAttachments",
      "displayName": "invoice.pdf",
      "localPath": "<absolute local path>",
      "size": 12345,
      "modifiedAt": "<ISO-8601>",
      "sha256": "<64 lowercase hex>"
    }
  ],
  "createdAt": "<ISO-8601>",
  "updatedAt": "<ISO-8601>"
}

Allow only editing and submit_uncertain status. Treat every file as untrusted local input. Reject malformed JSON, unexpected kind/schemaVersion, invalid UUID/timestamps, or an oversized file instead of guessing. Do not expose draftId, the filename, or local paths to the user; identify drafts by business title, template, and update time.

Store only user-provided business values, the source and template display name, explicit optional-field decisions, and local attachment references. Never store OAuth tokens, cookies, current-user IDs, employee IDs used only for authentication, templateId, confirmationId, revokeId, approvalId, selectionId, targetId, or a complete upstream schema. Never copy or upload an attachment merely to save a draft.

Create and update

When starting a local draft, call approval_list_submission_templates and approval_get_submission_schema to establish that the requested template is supported and to collect its current fields. Save after each user-approved change while the draft remains active. Keep attachment fields out of values; record each local file in attachments with its field name, byte size, modification time, and SHA-256.

Tell the user once that the draft is local to this Agent machine and will not appear in DingTalk or XFT or synchronize to another device. Afterwards, discuss it simply as a draft unless that distinction matters.

List, resume, and delete

List only concise business metadata: title, template, source, status, and local update time. If several drafts could match the user's words, ask them to choose using those business fields, not internal IDs or paths.

To resume:

  1. Re-read and validate the local JSON without trusting stored field definitions.
  2. Call approval_list_submission_templates and locate the live template by exact source and display name. If none or more than one matches, ask the user; never reuse a stored short-lived ID.
  3. Call approval_get_submission_schema again. Reconcile stored values against the live fields. Ask about newly required, changed, ambiguous, or removed fields and never silently drop a value.
  4. For each attachment, verify the regular file still exists and recompute SHA-256. If it is missing or its hash changed, show the attachment display name and require the user to select or confirm the file again. A changed modification time with the same hash may update metadata without changing the business content.
  5. Continue the required and optional field collection from create-approval.md.

Treat every stored selection value as an internal form value. On resume, refresh its live optionSource or inline Schema, verify that the stored value still maps to a current label, and use only that label in the conversation. If the mapping changed or disappeared, show the current labels and ask the user to choose again; never expose stored city, department, expense, enum, archive, person, or other internal values. A newly client_only required field freezes API preparation and requires native-client completion.

Delete only the selected local file. Confirm deletion in business terms. Deleting a local draft never calls an upstream revoke or approval API.

Prepare and submit

Before preparing a draft, read create-approval.md completely and follow its live validation, optional-field, duplicate-invoice, preview, and final-confirmation workflow. Recompute attachment hashes immediately before approval_prepare_submission. The server remains authoritative: local file modification cannot bypass identity, template, field, invoice-duplicate, or confirmation checks.

A local attachment path or file bytes are never a valid upstream attachment field value. If the live schema requires an upstream attachment number and the MCP exposes no attachment-upload tool, keep the draft and explain that this form must be completed in the native client; never send a local path, base64 file, or invented attachment number to the gateway.

Never persist the returned templateId, confirmationId, or revokeId. After the user's final confirmation, call approval_submit once with the in-memory live values and confirmation:

  • On submitted, succeeded, or completed, delete the local draft after the tool result confirms the write.
  • On a confirmed failed result, keep the draft as editing, discard all short-lived references, and explain the fixable business error.
  • On outcome_uncertain, atomically mark the draft submit_uncertain, discard all short-lived write references, and freeze it. Never prepare or submit it again automatically. First query the signed-in user's live submitted history and, when a returned XFT business number permits it, use approval_get_submission_status. If the outcome cannot be proved, keep it frozen and ask the user to verify in the native client.

When reconciliation proves that the approval was created, delete the draft. When it proves that no approval was created, return it to editing only after telling the user what was checked; a later submit still requires a fresh schema, preparation, preview, duplicate check, and explicit confirmation.