NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Go modules · #496 by repository stars
Last release 9 days ago
23 Sep 2026
Ships on a steady schedule
a new release about every 8 days
Nearly every release is documented
notes for 58 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
6 months old
207 releases · first in 2026
One column per month.
This beta validates long-running access-token recovery and the faster, recoverable guarded release path introduced after v1.0.53-beta.4.
This beta validates long-running access-token recovery and the faster, recoverable guarded release path introduced after v1.0.53-beta.4.
main, and stable baseline, so the subsequent guarded --publish invocation revalidates authority without repeating tests and packaging. A default-branch governance smoke uses the same dedicated immutable-release credential as the tag workflow before any tag is allocated.dws-release recover <version> can resume a failed, unpublished annotated tag through the normal contract, build, Developer ID signing, immutable GitHub Release, Homebrew, npm, and OSS jobs. Recovery requires the exact tag object, peeled commit, failed tag-push run, typed version confirmation, and the protected release-recovery environment; successful runs are accepted as future beta/stable delivery evidence.GET /releases/tags/{tag} 404 without allowing the release identity to drift during recovery.dws binary, release preflight explicitly rebuilds before policy checks, and the full-suite runner gives the growing script package a non-flaky five-minute per-suite budget.…instead of parsing .data | fromjson. This is a breaking change for scripts using the former transport envelope; the original server payload remains av…
This beta validates the expanded personal IM event subscriptions and the flattened event consume structured output introduced after v1.0.53-beta.3.
--user or an --open-dingtalk-id. Event Schema now exposes these alternatives through machine-readable parameter constraints.event consume projects NDJSON/JSON/pretty/compact output into event-specific top-level DTOs, so consumers read fields such as content, sender, and conversation_id directly instead of parsing .data | fromjson. This is a breaking change for scripts using the former transport envelope; the original server payload remains available through -f raw, while --debug-raw-events preserves the full diagnostic envelope.This beta validates multi-account profile support and the post-v1.0.53-beta.2 compatibility fixes for Windows portable authentication, IM shortcuts, a
This beta validates multi-account profile support and the post-v1.0.53-beta.2 compatibility fixes for Windows portable authentication, IM shortcuts, and Aitable import uploads.
corpId:userId, --profile accepts organization IDs/names plus user IDs/names, and organization-only selection uses its explicitly remembered current account or asks for an exact account when ambiguous.dws auth logout --profile can remove one account or every account in an organization, while identity token slots remain the source of truth and legacy organization/global mirrors stay compatible without overwriting newer account credentials.dws auth export and dws auth import now fail early without reading credentials, bundles, or writing files instead of claiming portable-bundle support for DPAPI-protected HKCU Registry credentials.chat message send by default, support --ai-tag=false to opt out, and preserve compatible search, conversation-ID, and page-size aliases.dws aitable import upload and dws aitable +import-upload now require a positive --file-size and always send it to the upload-preparation API, preventing invalid requests without the actual file size.This beta validates the accumulated post-v1.0.52 command surface, release automation, and runtime hardening changes, including enterprise contact onbo
This beta validates the accumulated post-v1.0.52 command surface, release automation, and runtime hardening changes, including enterprise contact onboarding, declarative shortcuts, Sheet/Aitable writes, multi-platform Homebrew formulas, and credential and target-validation fixes.
contact org create, contact user invite, and contact account create for creating a DingTalk enterprise, inviting an employee by mobile, and provisioning an enterprise login account, with reviewed Schema contracts and mono/multi Skill routing.dws <service> +<command> shortcuts across 16 services, including one-to-one MCP wrappers and multi-step smart workflows. Shortcuts publish stable Agent-visible contracts with named flags, validation and confirmation metadata, dry-run protection for writes, catalog/help routing, and optional local YAML extensions and usage recording.dws sheet import / sheet import create for converting local xlsx/xls files into new online sheets, sheet import get for polling import tasks, and dws aitable workflow create/update for applying validated workflow-dsl/v1 definitions, with matching reviewed Agent Schema and bundled Skill guidance.Formula/dingtalk-workspace-cli.rb and keg-only Formula/dingtalk-workspace-cli-beta.rb live in this repository and select signed macOS Intel/Apple Silicon or Linux amd64/arm64 artifacts at install time. Stable and beta releases open isolated Formula update PRs after final artifact signing, so beta never replaces the stable Formula. Agent Skills stay under pkgshare without mutating the user's home directory, and both tracks are covered by the six-channel post-release verifier.dws-release entry for one-command CHANGELOG preparation, validation-only and annotated-tag publication flows; promotes only an explicitly validated beta; verifies command-tree compatibility and all six packaged binaries; and serializes immutable GitHub Release, npm channel, OSS, Homebrew, and optional Gitee delivery with fail-closed recovery checks.PAT_ORG_POLICY_DENIED now remains terminal even if a backend also returns flowId, authorization URLs, or client credentials; the CLI does not mutate process credentials, open a browser, poll, or retry until an organization administrator changes the policy.sheet range read/get now rejects a null cell-info response instead of printing null and exiting successfully, while task completion and attachment listing verify that a task exists before calling lenient backend endpoints. Attachment listing is also published through Runtime Schema for schema-first Agent discovery.Nothing published for this version
Nothing published for this version
This release seals the v1.0.52 line with personal event subscriptions, a deterministic 22-product Agent command catalog, local user-operation auditing
This release seals the v1.0.52 line with personal event subscriptions, a deterministic 22-product Agent command catalog, local user-operation auditing, expanded Open product commands, safer macOS credentials and release signing, and more reliable Connect and IM delivery.
dws event list/schema/consume/status/stop for user @ mentions, selected one-to-one chats, and selected group chats. consume can create or reuse a personal subscription, multiple local consumers share one bus while keeping outputs isolated by event type and subscription, and the mono/multi event Skills ship with the binary.dws now produce redacted daily JSONL records with actor, command and endpoint, result or error category, duration, CLI/platform metadata, and a SHA-256 previous-hash chain for tamper evidence. Writers coordinate through a cross-process file lock and rotate logs safely; dws audit tail inspects recent records, dws audit export emits date-filtered JSONL or CSV, and dws audit verify reports the first broken link in a file's hash chain.dws schema now ships a deterministic 22-product / 564-tool catalog generated from the executable Cobra tree, with progressive product/group/leaf queries, complete parameter contracts, reviewed command identity and aliases, safety/confirmation metadata, field provenance, and final-delivery completeness/drift gates. The catalog is embedded at build time and does not require runtime MCP tools/list discovery.event consume/list/schema/status/stop and audit export/tail/verify enter the reviewed CommandRegistry, bind to the real Cobra tree at generation time, and ship through the same typed ToolSpec and embedded Catalog path as public MCP-backed commands. Leaf, group, product, and --all queries are projections of that single delivered model.dws auth migrate-keychain --to file-dek preflights every legacy/profile auth entry before rewriting, ignores unrelated application secrets, supports side-effect-free --dry-run, requires explicit --yes, and lets sandboxed and normal processes share an existing login without exposing tokens.event consume AI-subprocess contract (#609) — emits a fixed ready line and a final controlled-exit summary, supports parent-pipe stdin EOF as graceful shutdown, forwards --profile to the detached bus, surfaces bus startup errors, and cleans up subscriptions according to ownership so orchestrators can drive event streams without sleeps or leaked server-side subscriptions.chat message list preserves quoted merged-forward and image context; message-search entitlement failures retain the server-provided friendly hint and action URL; and ding message list exposes each DING's content alongside its ID and status.chat category create-smart now maps category names, group-name keywords, and member OpenDingTalk IDs to the live MCP contract, rejects blank or empty supplied values locally, and reports runtime tools/call connection failures as actionable API/network errors instead of internal discovery failures.chatRecord pictures/audio/video/files can be recovered from message APIs after Stream ACK, and OpenCode uses a full-duration storyboard for large videos to avoid base64 OOMs while preserving the original download for the turn.auth status reports ciphertext/key mismatches instead of treating them as ordinary logout. Dedicated macOS race and Windows DPAPI coverage protect the cross-platform paths.Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
This release promotes the sealed v1.0.51-beta.1 contents to stable. It syncs the hardcoded Wukong command surface, prevents dev connect conversations
This release promotes the sealed v1.0.51-beta.1 contents to stable. It syncs the hardcoded Wukong command surface, prevents dev connect conversations from blocking on messages received mid-turn, and makes local credential failures diagnosable without mutating key material.
dws agoal strategy, contract, scorecard, user-objective, report, and objective-template command groups, together with static routing and the bundled mono/multi Agoal skills.chat group notice create|edit|get|list, group share-invite, text translate, category create-smart, and message list-emotion-replies.doc import for starting imports and doc import get for querying import tasks.sheet group-dimension and sheet ungroup-dimension for whole-row or whole-column ranges.dws doctor now includes a keychain check, while dws auth status distinguishes ordinary logged-out state from keychain_unavailable and dek_missing failures and returns remediation hints in table and JSON output.dws pat chmod defaults to permanent grants (#584) — running dws pat chmod <scope> without --grant-type now requests a permanent grant instead of session, aligning the direct CLI path with the recommend-authorization helper. Session grants remain available by passing --grant-type session --session-id <id>.dev connect --channel gemini path now uses the Gemini generateContent API (#587) — configure it with GEMINI_API_KEY or GOOGLE_API_KEY, optionally override the compatible endpoint with GEMINI_API_BASE_URL or GOOGLE_GEMINI_API_BASE_URL, and select a model with --agent-model or GEMINI_MODEL; a local gemini executable is no longer required.dev connect turn scheduling (#587) — stream and @-poll callbacks no longer wait for the active turn to finish. Turns stay serialized per conversation, messages received mid-turn are coalesced into one pending follow-up, and different conversations can continue in parallel.Nothing published for this version
Nothing published for this version
This release fixes a long-standing gap where the global --jq / --fields output filters were silently ignored on product commands, lands a JSON-mode ou
This release fixes a long-standing gap where the global --jq / --fields output filters were silently ignored on product commands, lands a JSON-mode output path for the sheet batch-style command, and aligns the bundled skill surface with the real command semantics uncovered by the round-2 real-machine QA sweep.
--jq / --fields are honored on product commands (#575) — Formatter.PrintJSON / PrintJSONUnescaped now route through output.WriteFiltered when either flag is set, so product commands accept the same filters that dws api has always supported. The tool-caller adapter exposes Fields() / JQ() so helpers can read the flags without re-parsing.skill setup --dry-run is a no-op preview (#575) — it now prints what would be written without touching the skill directory, the registry, or the agent config. Help text and docs are updated to match.--fields projects top-level / list keys only (use --jq for nested paths); minutes_extract_todos.py, calendar_free_slot_finder.py, chat_export_messages.py / chat_history_with_user.py, and contact_dept_members.py are rewritten against the current response shapes; aisearch / aitable / attendance / calendar / chat / contact / dev / doc / doc-comment / doc-file-ops / doc-list / doc-search / drive / mail / minutes / oa / sheet / sheet-export / url-patterns / best_practices/lite-recipes.md / global-reference.md / intent-guide.md are re-synced; the QA voice ("真机" phrasing) and environment-specific quirks stated as absolute rules are removed from the docs.sheet range batch-set-style emits per-row JSON in JSON mode (#575) — when --format json is set, each update is reported as {index, sheetId, range, ok, error} instead of only the final aggregate, so callers can programmatically track partial failures under --continue-on-error.pkg/cmdutil.LeafMerge* and the provenance helpers are now public so downstream command trees can reuse the same merge semantics.…via fileId; report scripts migrated off the deprecated report list/report detail.
This release lands a full real-machine QA sweep across the CLI, helper scripts, and skill docs (#572), and hardens the release pipeline so npm publishing can no longer be blocked by Gitee mirror issues (#570).
aitable chart/dashboard share update --enabled now takes a string so --enabled false disables; chat conversation-info --user resolves openDingTalkId and registers --id/--conversation-id/--chat aliases; chat list-all-conversations --limit is capped at 100 and rejects larger values; custom-robot webhook failures surface errcode instead of masquerading as success; contact registers --dept/--depts as the primary flags so the documented spelling actually works; sheet media-upload and sheet export emit clean JSON under --format json (progress lines no longer leak); wiki node create --type enum is corrected (drops unsupported asheet, adds axls/able/appt/adraw/amind); ding message list --type defaults to ALL since the server rejects empty type.--limit, contact dept response keys (deptList/deptUserList) and userInfo nesting; attendance_my_record whoami compatibility; calendar_schedule_meeting event-id unwrapping; drive_tree_list recursion via fileId; report scripts migrated off the deprecated report list/report detail.success always true, --jq/--fields currently no-op) are documented.npm/latest. GitHub→Gitee attachment upload is disabled by default (unreliable from US runners) and only runs when ENABLE_GITEE_UPLOAD_FALLBACK=true; the legacy upload fallback path is guarded with timeout and retry so it fails fast when re-enabled.This release promotes the sealed remove-discovery delivery from the beta line to the stable v1.0.48 package. It removes dynamic service discovery from
This release promotes the sealed remove-discovery delivery from the beta line to the stable v1.0.48 package. It removes dynamic service discovery from the open-edition runtime, keeps legacy CLI compatibility aliases, syncs the open command/help/skill surface with the dws-wukong baseline, and includes the dev connect default-yolo behavior on the stable upgrade track.
v1.0.48; normal stable channels (dws upgrade, GitHub releases/latest, install scripts, and npm latest) should receive this release after the official tag is published.contact label is restored as real wukong-compatible functionality — dws contact label list/get/list-members now call get_org_labels, search_label_by_name, and get_label_members_by_labelId; contact role remains an alias, and the common top-level compatibility entries (contact search/find/list/get/self/me/whoami/get-self) now dispatch to real user/dept/label tools where unambiguous.contact label three-step role lookup flow; video-conference start/invite/share flows remain explicitly unsupported and point users to the DingTalk client.calendar event list --dry-run no longer executes the real list call — the sorted event-list wrapper now respects dry-run and prints the list_calendar_events preview instead of calling the backend.chat file upload is downlined — the hidden compatibility entry now returns a clear downline message and never calls chat/upload_conversation_file_by_url; the supported file path remains chat message send --msg-type file --file-path.nextCursor, mail helper scripts handle {result:{emailAccounts:[...]}}, and the generated attendance .xlsx fixture is removed from the skill scripts.contact label/role dry-runs, hidden top-level contact compatibility entries, chat file upload downline behavior, and calendar event list --dry-run.This release adds connector supervision & health monitoring (dev connect list/status/restart/stop) and fixes bot-to-bot @-mention delivery end-to-end.
This release adds connector supervision & health monitoring (dev connect list/status/restart/stop) and fixes bot-to-bot @-mention delivery end-to-end.
dev connect list — PM2-style colored table enumerating all local connectors with state (healthy / degraded / down / not_running), PID, channel, and uptime.dev connect status — panel view with heartbeat, last recv timestamp, session webhook age, and --json for external monitoring.dev connect restart — restarts a daemon via persisted daemon-state.json (unified-app-id credential fetch, no local secret storage).dev connect stop — graceful SIGTERM shutdown releasing the single-instance lock and Stream connection.heartbeat.json; status/list derive state from heartbeat freshness + process liveness + pid-reuse detection.--alwayson flag — opt-in auto-restart: supervisor relaunches the worker on crash (requires --daemon).--notify-staff-id — state-change notifications (start / stop / crash) sent as DingTalk messages to the specified staffId.--unified-app-id credential flow for dev connect — fetches clientId/clientSecret at startup via dev app credentials get, keeping secrets off the command line and out of daemon-state.json.feat(connect): download API-sent files via storage v2 API) — file messages sent via dws chat message send --msg-type file --dentry-id --space-id are now downloaded by the connector through the storage v2 getDownloadInfo API (dentryId + spaceId → presigned URL → local temp file), so file-based Q&A works regardless of how the file was sent.--at-open-dingtalk-ids for chat message send-by-bot — @-mention bots or cross-org users by openDingTalkId in group messages.atOpendingtalkIds (the server's lowercase spelling) is now used instead of the camelCase atOpenDingTalkIds which was silently ignored. The unnecessary openDingTalkId → userId reverse lookup (always failed for bots) is removed; the id is forwarded verbatim.interactiveCard messages (how DingTalk delivers a bot @-mentioning another bot) are now parsed: extractInteractiveCardText flattens cardContent[].children[].value leaves and strips the leading @-mention by leaf boundary. The emotion/reply reaction (which 500s on bot-sent cards) is skipped for interactiveCard turns.extractCallbackText gains a cardContent fallback so structured-text messages are no longer silently dropped.<@id> placeholders in the markdown body are rewritten to @id for both userIds and openDingTalkIds so the mention chip renders in all cases.sendBySession retries on transient failures instead of dropping the reply.updatedUnix so a connector with no inbound traffic is not marked degraded.checkFDLimit split into platform files for Windows cross-compilation.ulimit check for multi-agent stability.PAT agentCode grants no longer split from follow-up command checks (internal/auth/agent_code_detect.go, internal/app/runner.go, internal/pat/chmod_tes
internal/auth/agent_code_detect.go, internal/app/runner.go, internal/pat/chmod_test.go) — explicit DINGTALK_DWS_AGENTCODE declarations are now forwarded verbatim as the common cross-host contract, and unknown hosts no longer synthesize custom into x-dingtalk-dws-agent-code / x-dws-agent-instance-id. pat chmod --agentCode remains the highest-priority grant target and still wins over the env fallback.This release adds multi-organization (profile) support (#500): dws can stay logged in to several DingTalk organizations at once and switch between the
This release adds multi-organization (profile) support (#500): dws can stay logged in to several DingTalk organizations at once and switch between them, while staying fully backward/forward compatible with the previous single-org token. A profile is one logged-in organization (corp); the current profile decides which org a command runs against. The release also hardens the new credential store for concurrency and corruption recovery, documents the capability in both the mono and multi skill sets, and flips --ai-tag on by default so messages sent through dws carry the DingTalk 「通过AI发送」 badge (#524).
profile management (internal/auth/profiles.go, internal/app/profile_command.go) — dws auth login against a new organization adds a profile (the first login becomes the primary); dws profile list shows logged-in orgs with primary / current markers, status and validity; dws profile switch <name|corpId|-> persistently switches the default org (- toggles back to the previous one, no-arg opens a TUI selector on a terminal); dws profile use is an alias of switch. dws auth status [--profile <name>] reports a specific profile. Credentials are stored per organization in keychain slots keyed by corpId (auth-token:<corpId>), with a plaintext profiles.json registry holding only metadata and the primary/current/previous pointers (no tokens).--profile <name|corpId> flag — run a single command against a specific organization without changing the default (one-shot; does not move currentProfile). Cross-org reads are orchestrated by the agent (list profiles → query each with --profile → merge); there is intentionally no built-in --all-orgs.auth-token:<corpId> and marked primary on first multi-profile use; the current (or primary) profile's token is mirrored back into the legacy slot so older binaries and the embedded host keep working. profiles.json is additive and ignored by older versions.dingtalk-profile and dws-shared skills + multi-org documentation (skills/) — a standalone dingtalk-profile skill plus a new dws-shared skill that carries auth, global flags and the multi-org rule, so every multi-mode product skill's PREREQUISITE resolves and all read/search skills inherit cross-org behavior. The mono skill gains a "multi-org / profile" section, trigger conditions, a decision-tree entry and a corrected logout danger note. Multi-mode install now always ships dws-shared even when --skill / --exclude narrows the set.--ai-tag now defaults on — DingTalk 「通过AI发送」 badge for dws-sent messages (internal/helpers/chat.go, #524) — chat message send / reply flip the --ai-tag default from false to true, attaching the AI clawType by default so messages sent through dws (and by AI agents) transparently carry the 「通过AI发送」 badge; pass --ai-tag=false to send as the user with no badge.profiles.json (internal/auth/profiles.go, internal/auth/token.go) — every read-modify-write on profiles.json and the legacy mirror is serialized under the existing dual-layer (process + cross-process) lock, split into public (locking) entry points and lock-free *Locked variants so the non-reentrant lock is never re-acquired (the refresh path and the load-path migration use the lock-free savers). profiles.json and the token marker are written via per-write random temp names + atomic rename so concurrent writers can no longer corrupt a fixed .tmp. An unparseable profiles.json is quarantined (*.corrupt-*) and rebuilt empty so the CLI self-heals; auth reset / logout proceed even when it cannot be read and sweep the quarantined files.internal/auth/token.go) — when the resolved current/primary profile's keychain slot fails to read and no --profile was given, the loader now only falls back to the legacy single slot if it belongs to the same organization; otherwise it surfaces the error instead of acting as a different org.internal/auth/profiles.go) — SyncLegacyTokenMirror distinguishes "token genuinely absent" from "keychain momentarily unreadable" and keeps the existing mirror in the latter case, so a host app's login state is not dropped by a transient failure.…was two versions behind and still taught the deprecated flat aliases (report create / sent / list / detail / stats) and falsely claimed report inbox w…
This release hardens the dynamic-command surface and finishes the dws-wukong parity pass for structured input. Phantom override commands whose backing MCP tool isn't deployed are hidden from --help; report entry submit reads --contents-file / stdin natively; structured JSON flags accept @file / @-; and sheet range update / range read now accept the same plain shapes wukong does (scalar cells, flat values, null-clears-cell, a --hyperlinks flag). On the wukong01 sandbox this lifts the full open-edition cli_to_mcp pass rate from 77.6% to 95.5% (sheet 28.5% → 99.8%, report → 100%); the remaining failures are account / org / out-of-scope, not CLI defects.
dingtalk-dev skill: image-upload → mediaId recipe + per-resource command discovery (skills/multi/dingtalk-dev/references/) — documents how to obtain a mediaId for app / robot icons via the DingTalk OpenAPI (credentials get → gettoken → /media/upload?type=image → --icon-media-id → read back), since the dev command set has no upload command; and adds a "discovering commands" block to all 10 product refs pointing at each group's --help and dws schema dev.app.<group>.<method> (dws schema dev.connect for connect), so agents inspect commands instead of relying on memory.report entry submit --contents-file <path> / --contents - (stdin) read natively (#514, internal/compat/report_hooks.go) — the envelope publishes entry submit (MCP create_report) with a --contents (json_parse, required) flag plus a sibling --contents-file that had no transform / mapsTo, so a --contents-file-only submit silently sent contents: [null] and the report failed (only inline --contents worked, which is why report create succeeded while report entry submit --contents-file did not). A build-time compat hook now resolves the file / stdin natively (10MB cap, UTF-8 check, wukong priority --contents-file > --contents - > inline) and relaxes the individual required on --contents into a contents / contents-file one-of group. No discovery-config change needed.@file / @- input for structured JSON flags (internal/compat/transform.go) — json_parse / json_parse_strict now expand a leading @ before parsing (@- reads stdin, @<path> reads a file), so long / complex payloads (many records, big 2D cell ranges, filter criteria) skip shell-quoting hell. A JSON / YAML value never starts with @, so the sentinel is unambiguous; the error hint that already advertised @path/to/file.json is now truthful. sheet's shared sheetParseJSONFlag routes through cli.ResolveInputSource so the same support reaches --values / --criteria / --sort-keys.sheet range update --hyperlinks (internal/helpers/sheet.go) — a wukong-shaped 2D hyperlink grid ([[{"type":"path","link":"...","text":"..."}]]) overlaid onto the cells grid as each cell's hyperlink field; --values or --hyperlinks is now required (at least one).--help (#515, internal/compat/dynamic_commands.go) — override leaves whose backing MCP tool isn't actually deployed used to render in dws <svc> --help and then fail at invocation with tool not found. A tool-existence guard now hides them, and command groups left empty by the hidden leaves are collapsed, so --help reflects only invokable commands. Skill references are re-aligned to the real CLI surface (phantom commands dropped; role/duty "who is responsible" queries routed to aisearch, not contact).sheet range update accepts scalar cells; sheet range read projects a flat values; --values '[[null]]' clears a cell (internal/helpers/sheet.go, internal/helpers/sheet_cell_validation.go) — dws-wukong parity. range update (set_cell_range) auto-wraps a scalar cell (string / number / bool) into {type:text,text:"..."} instead of rejecting it, so the plain [["姓名","部门"]] shape that sheet append and wukong's update_range accept now works; a null cell clears content (matching wukong); {} still means keep-original. range read (get_cell_infos) now also exposes a flat values 2D array next to the rich cells payload, matching wukong's get_range shape without dropping cell styles.entry submit / inbox list / outbox list (skills/multi/dingtalk-report/, skills/mono/references/intent-guide.md) — the multi skill tree was two versions behind and still taught the deprecated flat aliases (report create / sent / list / detail / stats) and falsely claimed report inbox was unimplemented. Re-aligned to the canonical resource.verb commands consistently (old aliases still execute with a stderr deprecation notice).This release aligns the open edition's CLI surface with dws-wukong across the communication domain (chat / mail / minutes / todo / calendar / contact
This release aligns the open edition's CLI surface with dws-wukong across the communication domain (chat / mail / minutes / todo / calendar / contact / aisearch / live / report / ding) and the structured-office domain (aitable / sheet / drive / wiki / doc), and switches the discovery version code from bamboo to cedar so the aligned command tree is served from its own discovery config.
calendar book get|search and calendar acl list (cedar discovery overrides) — query a specific calendar (primary via --id primary), fuzzy-search calendars by name, and list a calendar's access-control entries. Maps to the calendar MCP get_calendar / search_calendar / list_acls tools.calendar attendee list|add|delete (internal/helpers/calendar_commands.go) — manage event participants under the wukong-aligned attendee naming (equivalent to the legacy participant group; calls get/add/remove_calendar_participant).minutes tag list and minutes tag query --tag-id — list a user's AI-minutes tags and query minutes by tag (query_user_tag_list / query_minutes_by_tag_id).minutes list mine|shared|all (internal/helpers/minutes_commands.go) — list own / shared / all minutes with renamed output fields.mail folder create|update|delete, mail template create|list|get|update|delete, mail contact create|list|update|batch-delete, and mail message list — full mail folder / message-template / contact CRUD plus folder-scoped message listing.chat file upload (internal/helpers/chat_file.go) — upload a local file (init/PUT/commit) or a remote URL to a conversation's file space.todo task add-attachment (internal/helpers/todo_commands.go) — attach a local file to a todo (multi-step upload).internal/helpers/aitable_extra.go) — advanced permission / roles, view sub-commands (lock / duplicate / frozen-cols / row-height / fill-color-rule / card / timebar), section node management, workflow enable/disable, record upsert / share-url / history-list / primary-doc, and field search-options. Helper tools route to the hardcoded aitable-helper supplement endpoint.internal/helpers/sheet.go, drive.go, wiki.go, doc.go).bamboo → cedar (internal/market/registry.go; discoveryAPIPath = "/cli/discovery/apis/cedar") — version codes step by first letter (bamboo → cedar → …); cedar carries the dws-wukong alignment. Older binaries keep reading bamboo, so the change is isolated to this release line. All test/mock/generator fixtures updated to the cedar path.internal/app/runner.go, internal/compat/registry.go) — dry-run prints a DRY-RUN Arguments: line, successful results carry success: true, missing-required-flag wording is unified to missing required flag(s): --x, and OutputTransform applies to the response content layer.internal/compat/transform.go) — parse_bool (explicit boolean strings so --flag false is honoured) and attendance_class_check_time (HH:mm → UTC+8 milliseconds for shift check-times).--calendar-id accepted on calendar event / participant / room / attachment commands so calendars other than the primary can be targeted.Nothing published for this version
This release rounds out dws dev connect — bridge a DingTalk robot to your local AI (Claude Code / Codex / opencode / Qoder / …): a generic custom chan
This release rounds out dws dev connect — bridge a DingTalk robot to your local AI (Claude Code / Codex / opencode / Qoder / …): a generic custom channel for any headless CLI tool, in-chat /new / /clear session commands aligned to each agent's real session op, and a fix for long opencode turns being cut at 30 seconds.
dws devapp robot connect — generic custom channel for self-built / unsupported AI tools (issue #37; internal/helpers/devapp_connect.go, internal/helpers/connect_stream.go) — a new --agent-cmd "<command>" flag (and custom channel) lets the bot forward to any headless AI CLI that takes a question as its trailing argument and prints the answer to stdout, so tools that aren't built-in (e.g. 网易有道龙虾 LobsterAI) or self-built agents can be onboarded without code changes. --agent-cmd forces the custom channel unless --channel is set explicitly; detection also falls back to custom when DWS_AGENT_CMD is present.robot connect now hints how to match terminal answer quality (issue #39; internal/helpers/devapp_connect.go) — when neither a work dir nor a knowledge source is configured, the connector prints a one-time note that the bot runs in a clean temp dir without local project context, pointing at --agent-workdir / --knowledge-dir / --knowledge-source / --agent-model. The robot quickstart gains matching FAQ entries, plus a clarification that step 3 (robot connect) produces no approval ticket (issue #19).
robot connect session commands /new vs /clear now use each channel's real session op (PR #20; internal/helpers/connect_opencode.go, internal/helpers/connect_stream.go) — /new (and /start, /reset) opens a fresh session and leaves the previous one intact (resumable where the agent supports it); /clear actively disposes the current session through the agent's real delete primitive — opencode issues DELETE /session/:id. Channels whose agent exposes no delete in the mode DWS drives it (Codex app-server, Qoder stream, Claude-family exec) fall back to a reset, so /clear behaves like /new there. Previously both commands only dropped the local conversationId → sessionId mapping, so the two were indistinguishable and opencode sessions were never disposed (they leaked).
robot connect no longer aborts long opencode turns at 30 seconds (PR #19; internal/helpers/connect_opencode.go) — the shared opencode HTTP client hard-coded a 30s Timeout that covered every request, including POST /session/{id}/message, so a long agent turn (e.g. a multi-minute research report) was killed mid-flight with context deadline exceeded (Client.Timeout exceeded while awaiting headers) even though the per-turn budget (DWS_AGENT_TIMEOUT_MS, default 300s) was far larger. The client-level deadline is removed so the per-request ctx governs the round-trip; only the /global/health probe keeps a short 10s timeout so startup detection stays snappy.This release makes the installers work from mainland China out of the box (no env var) and keeps the Gitee mirror in sync automatically.
This release makes the installers work from mainland China out of the box (no env var) and keeps the Gitee mirror in sync automatically.
scripts/install.sh, scripts/install.ps1, scripts/install-skills.sh) — the installers probe GitHub Releases on startup and, when it is unreachable (typical in mainland China), automatically resolve the version and download every asset (binary, checksums.txt, dws-skills.zip) from the Gitee mirror instead. A plain curl … | sh now works in China with no DWS_GITEE_REPO needed. Explicit DWS_GITEE_REPO still wins, DWS_NO_FALLBACK=1 forces GitHub, and local source-checkout installs skip the probe..github/workflows/mirror-to-gitee.yml) — the mirror workflow now pushes main + tags to the Gitee mirror over HTTPS using GITEE_TOKEN (no SSH key), on every push to main and every tag, keeping the Gitee raw/main install scripts and tags in sync without any manual git push. Gated on GITEE_TOKEN; skips cleanly when unset.This release adds China-accessible install mirrors so the CLI installs reliably from mainland China, where GitHub raw + Releases are slow or fail.
This release adds China-accessible install mirrors so the CLI installs reliably from mainland China, where GitHub raw + Releases are slow or fail.
scripts/install.sh, scripts/install.ps1, scripts/install-skills.sh, scripts/release/sync-to-gitee.sh, .github/workflows/release.yml, .github/workflows/mirror-to-gitee.yml) — an opt-in DWS_GITEE_REPO env var makes all three installers resolve the latest version and every release asset (binary, checksums.txt, dws-skills.zip) from the Gitee OpenAPI v5 instead of GitHub; with it unset, installation defaults to GitHub (fully backward compatible). The release pipeline mirrors release attachments to the matching Gitee release after each tag (gated on GITEE_TOKEN/GITEE_REPO), and a hub-mirror workflow keeps the repo code in sync (gated on GITEE_PRIVATE_KEY). README documents three China install channels: Gitee raw script, Gitee release binaries, and the npm package via registry.npmmirror.com.skills_embed.go, internal/app/skill_setup.go, internal/app/skill_setup_embed.go) — the skills/ tree (mono + multi) is embedded into the dws binary via go:embed and dws skill setup defaults to the embedded copy, refreshing the installed skill instead of silently reusing a stale copy probed from the current working directory — so skills install offline with no separate download.This release makes the AI-sent indicator opt-in. 1.0.38 unconditionally tagged every user-identity send/reply with the edition claw identity, so the I
This release makes the AI-sent indicator opt-in. 1.0.38 unconditionally tagged every user-identity send/reply with the edition claw identity, so the IM server rendered a "Send from AI" badge under every message — and on the open edition a stale hardcoded value even leaked the Wukong-branded label (「悟空AI发送」) to external users. The badge is now off by default and shown only when the caller explicitly asks for it.
--ai-tag opt-in flag for chat message send / chat message reply (#477; internal/helpers/chat.go) — by default no clawType tool argument is attached, so delivered messages carry no "Send from AI" badge. Passing --ai-tag attaches edition.ClawType() so the IM server renders the badge (open edition openClaw → 「通过AI发送」; the wukong overlay sets its own value → 「悟空AI发送」). Covers the text/Markdown, rich-media, and --user/--open-dingtalk-id direct send paths plus reply. Bot (send-by-bot) and webhook sends are intentionally untouched — they already render as bot messages. The badge is opt-in so dws does not brand every message a user sends.dws chat message reply no longer leaks the Wukong AI label on the open edition (#475, fixes #474; internal/helpers/chat.go, pkg/edition/edition.go) — the reply path hardcoded clawType: "wukong", so open-source quoted replies were tagged 「悟空AI发送」 by the IM server, leaking Wukong branding to external users (reported by an external customer integrating via openclaw). The value now derives from the edition via the new edition.ClawType() accessor (open → DefaultOSSClawType = openClaw), and — together with #477 — is only attached when --ai-tag is passed. The earlier fix existed on a branch (PR #450) but was never merged to main; #475 cherry-picked it.This release adds client-side agent attribution for usage stats, fixes two commands that silently misbehaved (dws sheet export hanging, dws upgrade --
This release adds client-side agent attribution for usage stats, fixes two commands that silently misbehaved (dws sheet export hanging, dws upgrade --dry-run actually upgrading), hardens the document write path against server-rejected characters, and makes the long-broken --no-browser login flag actually work.
agent_code detection + per-channel agent instance id for usage stats (#467; internal/auth/agent_code_detect.go, internal/auth/identity.go, docs/agent-code.md) — every MCP request now carries x-dingtalk-dws-agent-code (which agent host is driving dws — e.g. claudecode / codex / qoder / cursor / hermes / openclaw, falling back to custom), x-dws-agent-instance-id (a per-machine×channel id, dwsa_<base62(sha256(machineId|agent_code))>), the existing machine-level x-dws-agent-id, and X-Cli-Version. Detection is a confidence ladder, each signature verified on real hosts / official docs (never guessed; anything unrecognized resolves to custom): T0 explicit DINGTALK_DWS_AGENTCODE, T1 per-agent env signatures, T2 VSCODE_BRAND covering the whole VS Code fork family, T3 the macOS __CFBundleIdentifier map, T4 custom. identity.json migrates v1 → v2 transparently and keeps x-dws-agent-id machine-level for continuity. Trust boundary: agent_code and both ids are client self-reported and forgeable — they are for stats / observability only and must not be used for auth, authorization, rate-limiting, billing, or revocation. Server-side gateway work (header passthrough allowlist + logging the fields into the warehouse) is required before the data lands and is tracked separately.dws sheet export no longer hangs for the full ~5-minute poll timeout (#462; internal/compat/pipeline.go) — the pipeline poll loop compared the API status against pollUntilValue with case-sensitive ==, but the API returns "success" while the pipeline config declares "SUCCESS", so the match never fired and the loop spun until timeout. Switched to strings.EqualFold, aligning with the case-insensitive normalizeAsyncStatus helper already used for doc export / aitable export.dws upgrade --dry-run now previews instead of performing a real upgrade (#416, fixes #364; internal/app/upgrade.go) — newUpgradeCommand registered no --dry-run flag and never read the global persistent one, so --dry-run fell through and ran a real, irreversible upgrade (download + binary replace), directly contradicting the flag's documented 预览操作内容,不实际执行 contract. It now resolves the target release and platform asset (so "already latest" / "no build for this platform" is still surfaced), prints the 1–5 steps it would perform via the side-effect-free writeDryRunPlan, and returns before any backup / download / replace. Covered by TestWriteDryRunPlan_* and an updated help test.dws doc create / dws doc update strip server-rejected characters instead of failing (#465; internal/helpers/doc.go, internal/helpers/doc_jsonml.go) — the Markdown write path sent raw content straight through, and the dangerous-Unicode strip only ran on the JSONML branch, so content carrying C0 control characters (anything < 0x20 except \t / \n), DEL (0x7F), or zero-width / line-separator codepoints (U+200D, U+2028, U+2029) — common in LLM-generated or copy-pasted text — was rejected by the server-side RejectControlChars validator and the command failed. stripDocDangerousUnicode is renamed to stripDocInputUnsafe, extended to match the authoritative apiclient.rejectDangerousChars set, and applied on both the Markdown and JSONML node write paths. Tab and newline are preserved. Ported from dws-wukong.dws auth login --no-browser is now honored (#365; internal/app/auth_command.go, internal/auth/device_flow.go, internal/auth/oauth_provider.go) — the flag was already defined (and hidden) but never wired to the login providers, so the browser always opened regardless. The value is now passed into DeviceFlowProvider.NoBrowser / OAuthProvider.NoBrowser and gates the openBrowser call; the flag is also unhidden so headless / remote sessions can discover it.This release realigns the npm channel and hardens PAT batch grants. Background on the npm realignment: 1.0.36 was re-cut on GitHub on 2026-06-11 to fo
This release realigns the npm channel and hardens PAT batch grants. Background on the npm realignment: 1.0.36 was re-cut on GitHub on 2026-06-11 to fold in the canonical-tree poisoned-cache guard (#454), but the npm registry permanently forbids republishing a version number, so the npm package stayed on the original, unguarded cut. 1.0.37 is therefore the first version where every distribution channel — GitHub releases, dws upgrade, the install scripts, and npm — ships the same guarded build. If you installed 1.0.36 from npm, upgrade to this version.
internal/pat/chmod.go, internal/auth/channel.go, internal/app/runner.go) — an explicit --agentCode flag or the DINGTALK_DWS_AGENTCODE env var is now carried into PAT batch plan/grant arguments instead of being dropped, and a missing agentCode is forwarded as absent so the PAT core can apply the server-side default rather than failing. Batch grants now refuse to execute without an explicit --yes (dry-run and single-scope grants keep their existing behavior), closing the gap where a multi-scope grant could fire without a deliberate confirmation. Only the canonical env name DINGTALK_DWS_AGENTCODE is recognized; draft/reversed spellings from earlier iterations are ignored. Verified against prepub: dry-run, single grant, flag-priority grant, and batch grant all resolve the target agentCode, with the granted rows confirmed server-side. Tests: internal/pat/chmod_test.go, internal/pat/browser_policy_test.go, test/unit/pat_host_owned_signal_test.go.This release closes out the poisoned-discovery-cache lock-out for good, with four layers of defense landing together. The lock-out class (seen again o
This release closes out the poisoned-discovery-cache lock-out for good, with four layers of defense landing together. The lock-out class (seen again on 2026-06-09 as chat_permission_grant flag redefined: params): the dynamic command tree is built from cached discovery data before Cobra dispatches any command, so a pflag panic fed by a poisoned cache aborted every invocation — including dws cache refresh and dws upgrade, the very commands that could repair it. Now: (1) any panic during the build is recovered instead of crashing (#447), (2) the four known envelope shapes that made pflag panic are skipped at registration so they never fire (#449), (3) when an unknown panic class does fire, the CLI quarantines the poisoned cache and rebuilds itself from a fresh fetch — and dws upgrade clears the discovery caches after every binary swap, so simply getting this version onto a machine is enough to escape, no manual cache surgery (#452), and (4) the same guards now also cover the canonical dws mcp tree, which is built even earlier and sat outside all three defenses as originally cut (#454 — this release was re-cut on 2026-06-11 to include it; verified against the preserved real poisoned cache from the 2026-05-25 incident). Also in this release: dws devdoc gains RAG-backed Open Platform doc search and a new error-diagnosis command (#434), and dws doc create stops producing documents with two identical titles (#448).
Escaping a locked-out older binary: a binary ≤1.0.35 bricked by a poisoned cache cannot run dws upgrade. Either bypass the cache for one invocation with DWS_CACHE_DIR=$(mktemp -d) dws upgrade, or delete ~/.dws/cache/<partition>/tools/ by hand, or reinstall via the install script. Once 1.0.36 is on the machine this never needs doing again.
dws devdoc — RAG-backed Open Platform doc search and error diagnosis (#434; internal/helpers/devdoc.go, internal/transport/client.go) — dws devdoc article search now routes to the upstream search_open_platform_docs_rag tool, returning structured RAG/reference payloads (the CLI stays a thin invoker; no extra AI analysis layer). New dws devdoc error diagnose (alias troubleshoot) routes to search_open_error_code_rag for diagnosing DingTalk Open Platform API errors, with --request-id (hidden --trace-id kept for compatibility), --error-code, --error-message, --api, --context, --query, --page, --size. Transport-side: query parameters required by DingTalk MCP gateway URLs are preserved on the wire but their values are redacted from debug logs. Default MCP / skill hosts stay on production https://mcp.dingtalk.com (prepub remains runtime-configurable). Skill docs (mono + multi dingtalk-devdoc) and docs/command-index.md updated alongside.internal/app/legacy.go) — buildEnvelopeCommandsSafe wraps the envelope-driven build in a local recover(). On panic the CLI logs it, prints a stderr hint, and falls back to the hardcoded helper commands, so auth / cache / doctor / version / upgrade and the helpers stay alive and dws cache refresh can rebuild the poisoned cache. Before this, the only recovery from the pre-1.0.32 lock-out class was manually deleting cache files; the duplicate-flag class itself had been fixed at the builder level, but any future panic class in the cache-driven build would have bricked the CLI again. Tests: TestNewLegacyPublicCommandsPanicFallsBackToHelpers, TestNewLegacyPublicCommandsNoPanicKeepsDynamicPath.internal/compat/registry.go) — while reproducing the lock-out byte-for-byte, four envelope shapes were found still forwarded to pflag calls that panic, each bricking every invocation: a flag named params / json colliding with the reserved payload flags (the original flag redefined: params — earlier dedup fixes covered the alias list and Detail-schema path but not the primary name); two bindings resolving to the same long flag name across bindings; two flags claiming the same shorthand; and a multi-character shorthand. Two small guards applied at every registration site (ApplyBindings, registerPositionalAliasFlags): canRegisterFlag skips duplicate/reserved long names (the value stays reachable via --params), and safeShorthand drops an invalid or already-taken shorthand while keeping the long flag. The trailing --json / --params registration is now idempotent. Defense in depth with #447: the escape hatch should never trigger for these known vectors. Test: TestBuildDynamicCommandsSurvivesMalformedFlagEnvelope (5 table-driven vectors).dws upgrade clears discovery caches (#452; internal/app/legacy.go, internal/app/upgrade.go, internal/cache/store.go) — #447's recovery is upgraded from "degrade and ask the user to run dws cache refresh" to a two-stage self-heal: on the first build panic the partition's discovery cache is moved aside to <partition>.quarantined (kept on disk for inspection; a previous quarantine is replaced so nothing accumulates — new Store.QuarantinePartition) and the build retried once against a fresh fetch. If the retry succeeds the user gets the full dynamic command tree with zero manual steps; only a second panic (remote envelope itself still poisoned, or offline) degrades to helper commands with the cache refresh hint. Additionally dws upgrade purges discovery-derived caches (market / tools / detail across all partitions — new Store.PurgeDiscoveryData) after a successful binary swap, leaving the co-located downloads/ cache untouched, so an upgraded binary always rebuilds its command tree from fresh data instead of inheriting snapshots written by the old version. Tests: internal/cache/store_quarantine_test.go, rewritten internal/app/legacy_panic_fallback_test.go (self-heal success, double-panic degradation, no-cache no-op, happy path).dws mcp tree no longer escapes the poisoned-cache guards (#454; internal/cli/canonical.go, internal/app/root.go) — the canonical tree is assembled from cached catalog data before the legacy command build, so a pflag panic there — a tool schema property named after the reserved --params flag, exactly what the 2026-05-25 incident cache contained — bypassed #447/#449/#452 entirely and still bricked every invocation, including on this release as originally cut. Two layers, mirroring the existing guards: applyFlagSpecs skips reserved (--json/--params), duplicate, and alias-colliding flag names and sanitizes shorthands (canRegisterToolFlag / safeToolShorthand; a skipped property stays reachable through the reserved JSON payload flags), and newMCPCommand wraps the build in the #452 recover → quarantine → retry-once → degrade-to-stub sequence. Verified against the preserved real poisoned cache: the original cut locks out on --version / cache refresh / doctor; this build self-heals on first run and cache refresh clears the poison. Tests: internal/cli/canonical_flag_guard_test.go (4 cases), internal/app/canonical_panic_fallback_test.go (4 cases mirroring the legacy fallback suite).dws doc create no longer produces a document with two identical headings (#448; internal/helpers/doc.go) — the platform renders the document name as the page title, and LLM agents habitually repeat # <title> as the markdown body's first line despite the skill docs saying not to, so duplicate-heading documents kept appearing. The doc create helper (which wins the envelope merge via preferLegacyLeaf) now strips a leading ATX H1 whose text exactly equals --name (trimmed, case-insensitive) before forwarding to create_document, printing a stderr note so agents learn the convention. Deliberately conservative: only an exact match is removed (# 背景 stays), ATX closing hashes are handled without over-trimming names ending in # (e.g. C#), H2+/setext headings are never touched, and a body that is nothing but the duplicate H1 omits the markdown param instead of sending an empty string. JSONML bodies are out of scope. Tests: TestStripLeadingDuplicateTitleHeading (9 cases) plus three end-to-end cobra tests asserting the exact markdown param sent.`chat message send` @-mentions not rendered in group / direct chat (#433, internal/helpers/chat.go) — when sending a group message or an openDingTalkI
chat message send @-mentions not rendered in group / direct chat (#433, internal/helpers/chat.go) — when sending a group message or an openDingTalkId direct message (send_personal_message) as the current user, the content body was packed with json.Marshal, whose default HTML escaping turns the < > in <@openDingTalkId> / <@all> into < >. The DingTalk client renders @-mentions by matching the literal <@...> token, so after escaping the match fails and the mention shows as plain text — while the API still returns success, masking the bug. Fix: add marshalMessageContent, which serializes {title,text} with json.Encoder + SetEscapeHTML(false); both the group and openDingTalkId-direct send_personal_message paths now use it, preserving the literal <@...>. Added regression test TestChatMessageSendContentNotHTMLEscaped asserting the content keeps the literal token and is never HTML-escaped. Verified on a real device: @someone and @all both render as clickable blue mentions.chat skill docs & scripts aligned to direct-chat list-direct (#424) — chat message list now supports group chats only (--user / --open-dingtalk-id removed); reading a direct chat moves to the dedicated list-direct command, but the skill docs and scripts still taught chat message list --user, which now errors with unknown flag: --user, also breaking chat_history_with_user.py (listed as the "preferred" way to query direct chats). This update: skills/{mono,multi/dingtalk-chat}/references/products/chat.md switches message list to group-only and documents the new list-direct command, syncing the intent routing / key-distinction / context-passing tables / caveats; skills/mono/references/best_practices/01-messaging.md changes query-private-chat from list --user to list-direct (the multi version was already updated); chat_history_with_user.py (mono + multi) now calls list-direct and fixes response parsing (unwraps result.messages, aligns createTime/content/sender fields — it previously crashed on 'str' object has no attribute 'get'). Direct-chat sending still uses chat message send --user (since v1.0.34 the direct-send rpc is folded into the send command; there is no separate send-direct). Docs/scripts only; no change to CLI binary behavior.pat chmod batch authorization did not pass through agentCode (#414, internal/pat/chmod.go) — the batch plan / grant paths (buildBatchPlanArgs / batchArgs) previously carried agentCode only in the single-grant toolArgs; batch calls omitted it, so a batch authorization with an explicit agentCode was processed under the default agent. Fix: the batch plan / grant args now also carry agentCode, matching the single-grant path.pat JSON output escaped the authorization URL into an unreadable form (#401, internal/pat) — the authorization URL attached to PAT error messages, after default HTML escaping, turned & into &, breaking the link when copied / recognized on mobile. Fix: the PAT error-enrichment JSON output now uses SetEscapeHTML(false) (scoped to PAT JSON only), preserving the readable & separators.Service discovery path now carries a version-coded segment (internal/market/registry.go) — the server-list endpoint moves from /cli/discovery/apis to
internal/market/registry.go) — the server-list endpoint moves from /cli/discovery/apis to /cli/discovery/apis/bamboo. The path is now a single discoveryAPIPath constant so future version bumps touch one place. Only the path changes; the MCP base host stays on production https://mcp.dingtalk.com and the auth / skill / doctor endpoints are untouched. Discovery via the edition DiscoveryURL hook (full-URL FetchServersFromURL) is unaffected. Server side must serve the new path.dws aiapp — AI application product taken offline — removed the aiapp product surface (create / query / modify) from the CLI: deleted internal/helpers/aiapp.go, dropped it from the generator coverage targets and knownRegistryProducts, removed the aiapp skill references (mono references/products/aiapp.md + dingtalk-aiapp multi skill), and unpublished the aiapp server from the service-discovery envelope. Product count drops from 19 to 18.…rendering path alongside the raw JSON, plus deprecation shims for the old report shape.
This release merges the multi-contributor pre-mcp-discovery feature branch into main as a single squash (#391), bringing a large batch of new product surface — full DingTalk docs (doc), knowledge base (wiki), AI app (aiapp), AI-table forms + import/export, and reworked mail / todo / report command trees — while keeping service discovery pinned to production https://mcp.dingtalk.com (the branch's pre-mcp.dingtalk.com endpoint change was deliberately excluded; the four host constants in skill_command.go / auth/endpoints.go / cli/loader.go / market/registry.go stay on prod). It also folds in the portable auth bundle (dws auth export / import, #357) and PAT batch authorization (#389).
dws doc — full DingTalk document command family (#387, #362, #388, #390; internal/helpers/doc.go, internal/helpers/doc_jsonml.go, internal/helpers/docjsonml/) — search / list / info / read / create / update / upload / download / copy / move / rename, plus file, folder, block-level editing and comment (list / create / reply / create-inline). Authoring supports both DocxXML and a JSONML format with a v2 schema validator (docjsonml/jsonml-schema-v2.json + doc_jsonml_validate_v2.go). Document export and OA alignment land here.dws wiki — knowledge base management (internal/helpers/wiki.go, internal/helpers/wiki_proxy.go) — knowledge space create / get / list / search and member add / list / update, routed through a wiki proxy server.dws aiapp — AI application lifecycle (internal/helpers/aiapp.go) — create (with prompt / attachments / skills), query by task ID, modify by thread ID.dws aitable forms + import/export (internal/helpers/aitable_form.go, internal/helpers/aitable_export_import.go) — datasheet form management and full record import/export, the latter driven through the async-task helper for large datasets.chat / report / todo / contact / mail command trees aligned to the Wukong baseline (#355; internal/compat/mail_hooks.go, internal/compat/todo_hooks.go, internal/helpers/report_readable.go) — mail and todo gain dedicated compat hooks; report gains a human-readable rendering path alongside the raw JSON, plus deprecation shims for the old report shape.dws auth export / dws auth import (#357) — portable auth bundle for migrating Linux sandbox credentials. Exports the encrypted keychain (~/.local/share/dws-cli, including auth-token.enc and dek) plus required ~/.dws config so refresh tokens survive import; copying only app.json leaves access tokens expiring after ~2 hours. Supports -o / -i tar.gz paths and --base64 for copy/paste between sandboxes. dws auth status now shows refresh-token validity in table output.pkg/asynctask/, pkg/paging/) — shared helpers underpinning long-running operations (e.g. aitable import/export, doc export) and cursor/page traversal.envelope now registers cli.Aliases as cobra aliases (#391) — discovery-generated commands expose their declared aliases natively in the command tree, with accompanying command-structure and JSON-parsing cleanups.dws pat chmod prints a compact authorization summary by default, and gains batch authorization flows (#389; internal/pat/chmod.go) — scripts that parse the raw MCP JSON from stdout must now pass --format json or --verbose to keep the machine-readable payload; the default summary keeps grant status, agentCode, grantType, scope counts, and a next-action hint. New batch grant/plan flows (pat.batch_grant / pat.batch_plan) authorize multiple products in one session, fall back to the legacy single-grant path when the server reports PAT_BATCH_AUTH_UNSUPPORTED, use the server's default agentCode when none is given, and surface per-tool authorization metadata for grant planning.Nothing published for this version
Nothing published for this version
Two user-visible regressions resolved plus two AI-agent discoverability fixes. dws drive upload was returning HTTP 403 SignatureDoesNotMatch for any f
Two user-visible regressions resolved plus two AI-agent discoverability fixes. dws drive upload was returning HTTP 403 SignatureDoesNotMatch for any file whose MIME detects to a non-empty value — basically every real file — because the helper added a client-side Content-Type fallback whenever drive.get_upload_info returned an empty headers map. DingTalk drive's OSS presigned PUT URLs are signed against an empty Content-Type at signing time, so any client-supplied header makes the signature OSS recomputes diverge from the server-signed one, and the PUT is rejected (#347). On Apple Silicon, dws upgrade was aborting at the "解压并验证" step with signal: killed because GoReleaser cross-compiles darwin/arm64 binaries on ubuntu-latest with no codesign step, and macOS 11+ amfid SIGKILLs unsigned arm64 binaries on first exec (#339) — the release pipeline now ad-hoc signs every darwin tarball, and the upgrade client self-heals if it ever encounters an unsigned binary again. On the AI-agent discoverability side, dws aitable attachment upload-file (the one-shot prepare + PUT + commit composite) is no longer hidden from --help — agents that only browse the command tree were getting stuck at the prepare-only attachment upload step, which returns an upload URL + fileToken but doesn't actually upload. And dws --help itself now surfaces the missing-command upgrade hint that the custom renderRootHelp had been silently dropping from cobra's root.Long.
dws aitable attachment upload-file is now visible in dws aitable attachment --help (#347, internal/helpers/aitable.go) — the hardcoded one-shot composite (prepare + HTTP PUT + commit, returns fileToken directly) was previously marked Hidden:true and only reachable by agents that read skills/references/products/aitable.md. Agents that only discover commands via --help were getting stuck at the sibling envelope-generated attachment upload (prepare-only): they'd receive uploadUrl + fileToken, have no idea how to consume the URL, and either write the URL into the attachment field as if it were a token (wrong shape — the field expects [{"fileToken":"ft_xxx"}]) or fall back to "please use the UI" messages, which made dws look broken even though the capability was fully implemented. Unhiding mirrors the discoverability pattern lark-cli base +record-upload-attachment already follows. Short is tightened to explicitly mention the 3 steps it bundles; Long calls out the prepare-only sibling and recommends upload-file as the default for AI agents. The sibling attachment upload (prepare-only) keeps its envelope-generated registration but gets a new Long that states it is only step 1 of a 3-step flow, lists what an agent must do after (HTTP PUT to uploadUrl, then write [{"fileToken":"ft_xxx"}] into the attachment field), and points to upload-file as the recommended one-shot alternative. TestAITableUploadFileCommandIsDiscoverable in internal/helpers/aitable_upload_file_test.go guards against re-introducing Hidden:true.dws --help root output now surfaces the dws upgrade hint when no listed command fits (#347, internal/app/root.go + internal/app/root_help.go) — root.Long is set to "提示: 如果遇到能力缺失、命令报错、新功能未注册、或无法完成任务, 请先用 'dws upgrade' 升级到最新版本后再试. 钉钉 OpenAPI 和 dws CLI 持续迭代, 新能力和 bugfix 会先在新版本上线.". The custom renderRootHelp (which replaces cobra's default template to render the services / utilities sections) had been silently dropping root.Long; restoring it costs one Fprintln after the command list, separated by a blank line. The natural failure mode for both agents and users staring at dws --help is to give up or hack around when none of the listed commands fit — but in many cases the right action is simply dws upgrade, because new capabilities and bugfixes ship continuously and a missing command is usually a stale-binary issue. TestRenderRootHelpIncludesLong in internal/app/visibility_test.go uses a sentinel Long string and asserts the rendered output contains it verbatim, so any future rewrite of the help renderer that drops Long fails this test immediately.dws drive upload no longer fails with HTTP 403 SignatureDoesNotMatch on any non-empty MIME type (#347, internal/helpers/drive.go) — httpPutDriveFile was setting req.Header["Content-Type"] = fallbackMIME whenever the prepare_upload response returned an empty headers map. DingTalk drive's OSS presigned URLs sign StringToSign against an empty Content-Type at signing time, so any client-side header makes the signature OSS recomputes at PUT time differ from the server's presignature, and the upload is rejected with 403 SignatureDoesNotMatch. This broke every dws drive upload for any file whose MIME detects to a non-empty value (image/png, application/pdf, every common binary) — i.e. essentially every real upload. Fix: drop the hasContentType / fallbackMIME path entirely, trust the server's headers map as authoritative; empty map means "no client-side headers needed", do not infer. httpPutDriveFile's signature loses the fallbackMIME parameter. Manual verification: curl -X PUT -H "Content-Type:" --data-binary @file <same-presigned-url> returns HTTP 200, proving the only difference was the client-side Content-Type. TestHttpPutDriveFile_NoContentTypeWhenServerHeadersEmpty guards the empty-map path; TestHttpPutDriveFile_PassthroughServerHeaders guards that server-provided Content-Type / x-oss-* headers are forwarded verbatim. Important: internal/helpers/aitable.go's upload-file helper deliberately keeps its Set("Content-Type", mimeType) call — its OSS endpoint uses a different signing mode (server includes the client-declared MIME in the signature, verified across 12 file types — all succeed). The two helpers must not be unified without re-validating both endpoints.dws upgrade no longer dies with signal: killed on Apple Silicon after fetching the new binary (#339) — GoReleaser cross-compiles darwin/arm64 binaries on ubuntu-latest with no codesign step, and macOS 11+ on Apple Silicon requires at least an ad-hoc signature on every arm64 binary; amfid SIGKILLs unsigned arm64 binaries on first exec, which the upgrade client surfaces as signal: killed and aborts at the "解压并验证" step. Two layers of fix:
scripts/release/post-goreleaser.sh + .github/workflows/release.yml) — after GoReleaser produces the per-platform tarballs, post-goreleaser.sh unpacks each dws-darwin-*.tar.gz, applies an ad-hoc signature (codesign --force --sign - locally, rcodesign in CI), deterministically repacks the tarball, and rewrites the matching line in checksums.txt so the checksum stays consistent with the resigned tarball. release.yml installs rcodesign 0.27.0 before GoReleaser runs. Every 1.0.32+ tarball ships signed; the install regression is fixed at the source.validateNewBinary (internal/app/upgrade.go) — when running the freshly-extracted binary returns signal: killed on darwin, the validator retries once after running codesign --force --sign - on the binary and clearing the com.apple.quarantine xattr. This keeps dws upgrade working even if a future release ever skips the signing step again, and covers users upgrading from older unsigned binaries. internal/app/upgrade_test.go (+80 lines) covers the retry path end-to-end: a stripped binary exits 137 on first exec, validateNewBinary recovers via ad-hoc sign + xattr clear, the final binary shows Signature=adhoc and runs.Nothing published for this version
Closes the last drive-surface gap with the Wukong edition: dws drive upload lands as a single-shot composite (drive.get_upload_info → HTTP PUT to OSS
Closes the last drive-surface gap with the Wukong edition: dws drive upload lands as a single-shot composite (drive.get_upload_info → HTTP PUT to OSS → drive.commit_upload) so a local file reaches DingTalk drive in one CLI invocation, no manual three-step orchestration. Two more drive commands — dws drive list-spaces (list visible drive spaces) and dws drive delete (delete a drive file, routed via serverOverride to the doc MCP server) — ship via the portal envelope; dws cache refresh once to pick them up. Companion skill docs teach the agent to recognise dingpan URLs of the form alidocs.dingtalk.com/document/edit?dentryKey=… / …/document/preview?dentryKey=… and pass the whole URL through to --node instead of trying to extract dentryKey by hand (the server interprets dentryKey and a bare nodeId differently — manual extraction was failing).
dws drive upload --file <path> [--folder <dentryUuid>] [--space-id <id>] [--file-name <name>] [--mime-type <type>] (#335, see internal/helpers/drive.go) — composite leaf that runs the full three-step upload internally:
drive.get_upload_info — fetch the OSS-signed resourceUrl + uploadId + per-URL headers.PUT the file binary to OSS (10-minute timeout, attaches every header returned by step 1).drive.commit_upload — register the new file under the target space / folder.--dry-run prints the three step invocations as a single JSON payload without making any network calls. --file - is rejected on purpose: this is a local-path upload, not stdin streaming. --folder only accepts a dentryUuid; pure-numeric values are rejected up front (validateDriveParentID) so callers don't accidentally pass a chat-link dentryId (a different ID namespace) where the drive API expects a dentryUuid. Response normalisation handles all the wrapper shapes the upstream returns — content / result envelopes, resourceUrls[] arrays, and the flat resourceUrl / uploadUrl fallbacks — so the composite produces a stable JSON shape regardless of which path the upstream takes. The helper only registers upload; the existing six envelope-generated leaves (list / info / download / mkdir / upload-info / commit) keep flowing through dynamic discovery unchanged. pickCommands.MergeHardcodedLeaves guarantees dynamic leaves win on collision, so this helper only fills the upload gap.
dws drive list-spaces and dws drive delete (envelope rollout) (#335, ships via portal envelope) — list_spaces registers as a plain cliName alias on the existing drive MCP server; delete_document registers with serverOverride: doc so the call routes to the doc MCP server (which owns the delete API), surfacing under the drive command tree for ergonomics. Existing users must run dws cache refresh once to pick up these two new leaves; no binary upgrade is required for them, but they pair naturally with the v1.0.31 client that ships upload.
skills/references/url-patterns.md (#335) — single authority for dispatching alidocs.dingtalk.com URLs across doc / sheet / wiki. Five-way split: /i/p/<token> short links → expand via doc info; /i/nodes/<id> node URLs → probe with doc info and route by contentType / extension / nodeType; /spreadsheetv2/... → sheet; /document/edit|preview?dentryKey=<key> (dingpan format) → pass the whole URL to --node, do not strip dentryKey by hand; /i/share/... (read-only share) → use the read_url fallback. The "URL precheck" Step 0 in skills/SKILL.md now redirects every URL-bearing prompt through this dispatcher before the agent picks a product.
skills/references/products/doc.md — --node accepts dingpan URLs end-to-end (#335) — dws doc info / dws doc read examples gain two extra rows showing --node "https://alidocs.dingtalk.com/document/edit?dentryKey=<KEY>" and …/preview?dentryKey=<KEY> as first-class --node inputs. The "URL recognition & DOC_ID extraction" table adds the document/edit|preview?dentryKey=<key> row, and the extraction rules are split into three explicit clauses so the agent stops manually pulling dentryKey out of the URL and feeding it as a bare nodeId (which the server rejects). The "nodeId dual-format note" upgrades to "nodeId multi-format note" with four equivalent --node input shapes side by side.Aligns the open-source CLI with the IM envelope and schema-pipeline plumbing the Wukong edition has been running in pre-prod, plus three user-visible
Aligns the open-source CLI with the IM envelope and schema-pipeline plumbing the Wukong edition has been running in pre-prod, plus three user-visible quality-of-life fixes. The most visible one: chat-bot webhook payloads carrying literal Chinese mentions (@所有人 周报来了 / @张三 看一下) no longer fail with file not found — @ is only treated as the @<filename> file-injection prefix when followed by an ASCII path-shaped character. The chat command tree is refactored to lean on the service-discovery envelope: thin wrappers (chat search, chat group rename, chat group members list/add/remove/add-bot, chat bot search) move out of the hardcoded helper and become envelope-generated dynamic commands; the helper keeps only the chat commands with real business logic (intelligent routing, current-user resolution, response normalization, stdin/@file input). A new dws chat message reply joins the existing send / send-by-bot / recall-by-bot / send-by-webhook family. Underneath: transform: invert_bool lets envelopes flip boolean semantics between CLI surface and MCP body (e.g. --off ↔ mute=true); the pipeline executor fail-fast on upstream content.errorCode instead of polling forever; service-discovery dedup keeps two envelope entries that share an MCP endpoint but declare different cli.id as separate descriptors (so the bot-root / bot-message / bot-group trio fronting one MCP server stays as three distinct CLI command roots); and dws chat no longer nests as dws chat chat when two envelope servers both declare the same top-level command name.
transform: invert_bool for envelope flag overrides (#317, see internal/compat/transform.go) — flips a boolean at send time. Strings true/1/yes/on → false; false/0/no/off/"" → true. Used when the CLI surface and the MCP body have opposite semantics — e.g. envelope declares --off on the CLI but the MCP parameter is mute=true for "muted". The framework flips at send time so the envelope keeps the natural CLI verb without forcing every caller to remember the inverted mapping. Coverage in internal/compat/transform_test.go.dws chat message reply (#317, see internal/helpers/chat.go) — reply to a chat message. Sits alongside send / send-by-bot / recall-by-bot / send-by-webhook under dws chat message.chat command tree refactored to lean on the service-discovery envelope (#317, commit 6be1247) — internal/helpers/chat.go now only carries the chat commands that need real business logic on top of the raw MCP call: chat message send (current-user resolution + symmetric direct/group title validation), chat message send-by-bot / recall-by-bot / send-by-webhook (bot routing + stdin/@file input), and chat group create (response normalization). The thin wrappers — chat search, chat group rename, chat group members list/add/remove/add-bot, chat bot search — are now produced by the envelope as dynamic commands. Net diff in the helper: +358 / -71 overall (re-aligning to envelope-owned chat structure), and chat_test.go drops 71 lines of test-stubs the dynamic path covers natively. Every previously documented chat command keeps the same flag set and the same MCP tool routing — the surface is just sourced differently.content.errorCode (#317, see internal/compat/pipeline.go) — when an upstream tool returns a non-empty content.errorCode, executePipelineCall raises a validation error immediately with the upstream errorMessage instead of proceeding into the poll/download phase. Pre-execution cobra validation (MarkFlagRequired) only checks that a flag was set, not that its value was non-empty — so a --required-flag "" reaches the upstream tool and the upstream rejects with errorCode. Without the short-circuit the pipeline kept polling for a task ID that would never exist, either spinning to PollTimeout or burning through retries with no actionable error. Exit code 2 (validation), same as any other CLI-layer pre-flight rejection.cli.id (#317, see internal/market/registry.go) — NormalizeServers used to dedup envelope entries by endpoint alone (and by displayName in the second pass), which collapsed envelope entries that intentionally split one MCP endpoint into multiple CLI command trees. The bot-root / bot-message / bot-group trio all front the same .../server/4717... MCP endpoint and share the displayName 机器人消息, but each declares a distinct cli.id and a distinct CLI command root; the old dedup kept only the last-write and dropped two of them. The dedup key now appends #<cli.id> when present, falling back to endpoint / name when absent so historical envelopes without cli.id keep their existing behaviour. Coverage in internal/market/registry_test.go.@<text> injection no longer eats Chinese mentions like @所有人 / @张三 (#317, see internal/cli/stdin.go) — ReadFileArg and ResolveInputSource used to treat any value starting with @ as the @<filename> injection syntax. Chat-bot webhook payloads commonly contain literal mentions, so dws chat message send-by-bot --text "@所有人 周报" was failing with file not found: 所有人 周报 before the message reached the API. The new looksLikeFilePath heuristic accepts @ followed by an ASCII path-prefix character (A-Z / a-z / 0-9 / . / / / ~ / _ / -), or @- for stdin, and passes the value through unchanged otherwise. @A 但接下来都是中文@测试 does still attempt a file lookup because the rune right after @ is ASCII — this matches the documented @<path> prefix shape. The historical "bare @ is an error" behaviour is preserved. Coverage in internal/cli/stdin_test.go::TestReadFileArgChineseAtMention.dws chat no longer nests as dws chat chat when two envelope servers contribute the same top-level command (#317, see internal/compat/dynamic_commands.go) — BuildDynamicCommands used to overwrite topLevel[name] on the second contribution and rely on attachOrMerge later, which then attached the whole incoming command (named chat) under the existing root, producing dws chat chat <leaf>. The new mergeSubcommandsInto moves the second contribution's children under the first root and drops the duplicate wrapper, so e.g. group-chat + im envelopes that both declare cli.command: chat produce a single flat dws chat subtree.internal/app/runner.go + internal/app/direct_runtime.go) — when two envelope servers share the same cli.command, the per-product endpoint map endpoints[cmd] in registerDynamicServer is second-writer-wins, and catalog.FindProduct may return the wrong server's endpoint for a tool whose real owner is the other server. runtimeRunner.Run now cross-checks the canonical tool→endpoint map exposed by the new directRuntimeToolEndpoint: when the per-tool endpoint exists and differs from the per-product endpoint the catalog returned, the tool-owner endpoint wins. Pairs with the registry dedup change above so the routing matches the dedup result.Three discovery-envelope products land on the open-source surface — aiapp (AI applications), live (DingTalk live streaming), and aisearch (enterprise
Three discovery-envelope products land on the open-source surface — aiapp (AI applications), live (DingTalk live streaming), and aisearch (enterprise people search) — closing the gap with the Wukong edition's product list. The aisearch envelope ships rich model-tolerance affordances (short flags, flag aliases, subcommand aliases) so AI agents that hallucinate keyword synonyms (--query / --name / --q / --text / --find) or alias subcommands (search / find / query / user / people / ...) still route to the canonical person tool instead of erroring out. To support that final fragment of agent tolerance, internal/compat/registry.go relaxes the envelope-generated leaf command's Args validator from cobra.NoArgs to cobra.ArbitraryArgs — restoring cobra's own default (legacyArgs returns nil for leaves) so trailing positional words are silently ignored. Plus the previously-shipped credential-isolation fix.
dws aiapp / dws live / dws aisearch — three new products discovered via envelope (no public issue; pre-Diamond rollout) — open-source dws now exposes:
dws aiapp — AI application lifecycle: create --prompt <p> [--attachments <json>] [--skills <csv>] / query --task-id <id> / modify --prompt <p> --thread-id <id> [--skills <csv>]. Backed by upstream create_ai_app / query_ai_app / modify_ai_app MCP tools.dws live stream list — list my DingTalk live streams. Backed by upstream get_my_lives.dws aisearch person — enterprise people search by keyword + multi-dimension filter. Dimensions: all (default) / name / department / position / duty / supervisor / subordinate / phone / jobNumber — multiple comma-separated (--dimension name,department). Backed by upstream enterprise_person_search.aisearch envelope additionally registers -w / -d short flags (keyword / dimension); hidden flag aliases --query / --name / --q / --text / --find all routing to keyword; and cobra subcommand aliases search / find / query / user / people / search-person / search-user / user-search / lookup / ask / contact all routing to person. This closes the F-class model-tolerance regression cases in dws-wukong/auto-test/cli_to_mcp/testcases/aisearch/test_90_aisearch_param_regression.py (50/50 pass for aiapp + live + aisearch on the pre-mcp build).dws cache refresh once to pick up the new envelopes; no binary upgrade is required, but pairs naturally with the v1.0.29 client (see Fixed below for the envelope-leaf-Args change).NewDirectCommand in internal/compat/registry.go was hard-coding cobra.NoArgs for leaves without positional bindings (totalMax == 0). This is stricter than cobra's own legacyArgs (cobra args.go:30-32 returns nil for any command without subcommands), and surfaced as unknown command "<word>" for "<leaf>" whenever an AI agent passed trailing positional words after a leaf — e.g. dws aisearch person search --keyword "张" or dws aisearch person user search --keyword "张". Switching the totalMax == 0 branch (and the initial value) from cobra.NoArgs to cobra.ArbitraryArgs restores cobra's natural leaf behavior: trailing positional args are silently ignored. Existing positional-binding paths (MinimumNArgs / RangeArgs / MaximumNArgs) are unchanged. Verified against dws-wukong/auto-test/cli_to_mcp/testcases — aiapp (9/9) + live (3/3) + aisearch (38/38) = 50/50 pass, vs 48/50 before this patch.dws editions sharing the same config directory previously read and wrote the same app.json. A sibling edition that pinned its OAuth client ID could persist that ID through the shared post-login path, and the open-source build could later adopt it from the same file. Open-source/empty edition keeps the legacy app.json path for compatibility; sibling editions now use app-<edition>.json, matching the existing cache partitioning strategy. This prevents new cross-edition app credential writes and reads from colliding. After a sibling edition saves its new partitioned file, it also best-effort removes a legacy ~/.dws/app.json only when that file's clientId matches the sibling edition being saved; a different, unparsable, or otherwise unowned app.json is left untouched to avoid deleting open-source credentials. If you previously ran multiple editions in one shared ~/.dws, remove any confirmed-stale orphan manually with rm ~/.dws/app.json after verifying it is not the open-source credential file you still need.Nothing published for this version
A single symmetric follow-up to 1.0.26's #250: dws chat message send --group now refuses an empty --title at the CLI layer instead of letting the call
A single symmetric follow-up to 1.0.26's #250: dws chat message send --group <cid> now refuses an empty --title at the CLI layer instead of letting the call fall through to the API and surface a misleading 发群服务窗会话消息失败 error. No other behaviour changes.
dws chat message send rejects missing --title on group messages (#294, completes #250) — send_message_as_user's schema marks title as required (just like send_direct_message_as_user), but buildChatMessageSendInvocation only had the pre-validation on the direct-message branches. Group sends without a title were falling through to the API and returning the same misleading 发群服务窗会话消息失败 that #250 already fixed for direct messages. The check now covers both branches: missing --title on --group returns --title is required for group messages (--group) with exit code 2; missing on --user / --open-dingtalk-id keeps the original --title is required for direct messages (--user / --open-dingtalk-id). The Long help, --title flag description, the first Example, and skills/references/products/chat.md (including the drive→chat workflow example) are realigned to "title is required for both direct and group messages" — the docs previously contradicted themselves (the prose said 群聊可选 while the flag listing said 必填). internal/helpers/chat_test.go adds a group-without-title rejection case; the existing group / positional-text success cases now pass --title to stay aligned with the new validation. No API request shape change — the server has always required title; the CLI now matches.Two user-visible fixes plus the schema primitive they're built on. dws doc update now reads Markdown from a file or stdin, so long / multi-line / tabl
Two user-visible fixes plus the schema primitive they're built on. dws doc update now reads Markdown from a file or stdin, so long / multi-line / table-heavy content no longer gets mangled by shell escaping; dws sheet find --query stops returning unknown flag on the open-source build, restoring copy-paste from internal wukong docs. Underneath, schema/discovery envelopes get a generic file_read transform and a CLIFlagOverride.MapsTo field that lets two sibling CLI flags route into the same MCP parameter slot. Also suppresses a noisy WARN on normal stdio-plugin shutdown.
file_read transform + CLIFlagOverride.MapsTo field (#291, closes #277 #278 #282 #288) — discovery envelopes can now declare a path-typed CLI flag that performs the "file path → file contents string" conversion client-side before the value reaches the upstream MCP parameter.
transform: "file_read" (internal/compat/transform.go) — reads the file at the flag's value with UTF-8 validation; - means stdin. Any IO / encoding failure is surfaced as a validation error (exit 2), distinct from the generic transient-failure path (exit 1).CLIFlagOverride.MapsTo (internal/market/registry.go) — redirects the flag's final value (post-transform or literal) into a named MCP parameter slot instead of the default params[propertyName]. This lets a single MCP parameter (e.g. markdown) be fed by two sibling CLI flags — a literal --content and a file-reading --content-file — paired with the existing tool-level MutuallyExclusive / RequireOneOf to express "exclusive, at least one".internal/compat/dynamic_commands.go normalizer via a separate mapsToRoutes collection + routing pass; empty MapsTo preserves the legacy params[propertyName] = value semantics, so every pre-existing dynamic_commands test passes unchanged. Pre-prod end-to-end verified across 6 cases (see PR #291's Validation table).dws doc update --content-file <path> (envelope rollout) — fixes "long Markdown can't reach the doc". The old command only accepted --content "...", so long / multi-line / table-heavy Markdown got mangled by shell escaping and AI agents writing >2KB of content were stuck. The envelope now maps both --content (literal) and --content-file (file_read transform) to the markdown parameter, makes them mutually exclusive via cobra's MarkFlagsMutuallyExclusive, and requires at least one via RequireOneOf. --content-file - reads from stdin, so cat long.md | dws doc update --content-file - works directly. Existing users must run dws cache refresh once to pick up the new envelope.dws sheet find --query hidden alias (envelope rollout) — fixes "unknown flag when copy-pasting commands across editions". Users copying dws sheet find --query "..." from internal wukong docs onto open-source dws got unknown flag: --query, because the open-source primary flag is named --find. The envelope now registers --query as a hidden alias of --find via CLIFlagOverride.Aliases (the field shipped in 1.0.26) — it doesn't show up in --help, but accepts values and writes to the same MCP parameter. --find behaviour is unchanged. Also requires dws cache refresh once.failed to stop stdio client: exit status 1 WARN on normal stdio-plugin shutdown (#285) — when Stop() explicitly Kills the subprocess, the non-zero exit code returned by cmd.Wait() is expected behaviour, but it was being propagated as an error and logged to stderr on every CLI exit, polluting agent log parsing. Stop() now returns nil after Kill + Wait; the error path is reserved for "process exited on its own with non-zero" (e.g. stdin close without an explicit Kill). internal/transport/stdio.go + stdio_integration_test.go assert "Stop() returns nil after kill".Nothing published for this version
Platform-stability round: Windows PAT-auth browser opener no longer truncates URLs at &userCode=, macOS sandbox hosts get an opt-in keychain fallback,
Platform-stability round: Windows PAT-auth browser opener no longer truncates URLs at &userCode=, macOS sandbox hosts get an opt-in keychain fallback, and dws doc download rejects axls nodes before requesting drive:download consent. Two new global output formats -f ndjson and -f csv (matching larksuite/cli) land as first-class citizens with real-traffic-verified list detection. The dws doc comment * regression tracked in #240 is also resolved — fix is in the market metadata, users just need dws cache refresh once.
-f ndjson and -f csv global output formats (#259, closes #252) — ndjson emits one compact JSON record per line (works straight with jq -c / while read / log pipelines); csv goes through encoding/csv (RFC-4180 — quoting, embedded newlines, CJK all handled by stdlib) and reuses the existing -f table column resolver (normalizePayload / unwrapPrimaryObject / extractRowsFromMap / rowsFromSlice / formatValue) so table and csv stay visually aligned. After a 7-product real-traffic sweep (contact / chat / doc / mail / todo / minutes / schema), the preferredListKeys whitelist was extended to cover the actual DingTalk envelope shapes — contact user search (result), chat search (result.value), doc search (documents), mail mailbox list (emailAccounts), todo task list (result.todoCards) — so these commands now degrade into a proper row stream instead of collapsing to a single-line key,value blob. Lives in internal/output/ndjson.go + internal/output/csv.go; --format help in internal/app/flags.go now lists ndjson|csv alongside json|table|raw|pretty.StickyHandler 此前会把任何前缀命中已知 flag 的 --flagsuffix 一律切成 --flag suffix,于是 --starttime20260507 这类拼错被静默改写成 --start time20260507,把假值传到下游。新行为按 flag 的 pflag 类型 / JSON Schema format / enum 校验 suffix 是否像合法 value(共享逻辑见 pkg/cmdutil/sticky_suffix.go),不像就保留原 token 让 cobra 报 unknown flag。slice/array/object 类型的 flag 永不切分。首 rune 读取使用 utf8.DecodeRuneInString,对中文等多字节 value 安全。available_flags field on unknown-flag errors (#272) — dws -f json 的 unknown-flag 错误体里新增 available_flags(已排序、过滤掉 hidden 与内部 json / params),方便 agent 不解析 --help 就能恢复。Human-readable 输出会附 Flags: ... 行,截断在 200 字节内。dws chat message send 单聊缺 --title 时前置校验 (#250) — 单聊(--user / --open-dingtalk-id)的底层工具 send_direct_message_as_user 在 API 层强制要求 title,缺失时返回误导性的 发群服务窗会话消息失败。CLI 现在在 buildChatMessageSendInvocation 里前置校验,直接返回 --title is required for direct messages (--user / --open-dingtalk-id);同时把 Long help、--title flag 描述、Example 和 skills/references/products/chat.md 全部对齐为「单聊必填,群聊可选」。群聊行为不变。cmd /c start <url> on Windows interprets & as a command separator, so PAT URLs containing &userCode=... were silently chopped before the userCode segment, and the browser landed on a 0-permission DingTalk page. The retry opener now uses rundll32 url.dll,FileProtocolHandler, which passes the URL through verbatim. The PAT response also exposes a copy-safe data.authorizationUrl (in addition to the service-provided data.uri, which is preserved as-is), and human-readable PAT output prints PAT_AUTHORIZATION_URL=<full-url> on its own line so OpenClaw-style host wrappers that swallow or reformat stderr can still capture the full link. Legacy DingTalk hash-route shapes (https://open-dev.dingtalk.com/fe/old#%2FpersonalAuthorization%3FflowId=...%26userCode=...) are normalised back into the working /fe/old?hash=...#/personalAuthorization?...&userCode=... form. Regression tests cover the issue-shaped URLs (encoded hash, fragment, &userCode) plus the OpenClaw malformed-hash variant.dws doc download triggered drive:download PAT consent for unsupported axls nodes (#268, fixes #190) — added a get_document_info preflight before download_file, so online-sheet (axls) nodes are rejected locally with guidance to use sheet range tools instead. The preflight reads extension from deterministic response paths (no recursive payload scan) and routes its own PAT errors back through handlePatAuthCheck, preserving device-flow / host-owned PAT behaviour. Costs one extra MCP roundtrip per doc download — deliberate, so the unsupported path fails before consent. Lives in internal/app/doc_download_preflight.go; coverage in internal/app/runner_test.go.security / Keychain APIs, so every token operation failed. New opt-in DWS_DISABLE_KEYCHAIN=1 switches macOS to the same file-DEK path Linux uses (DEK at ~/Library/Application Support/dws-cli/dek, mode 0600), bypassing the system Keychain. Default behaviour is unchanged — fallback is strictly opt-in because file-DEK is a weaker trust model than Keychain-managed storage (DEK file sits next to ciphertext in the same directory). The Darwin / Linux file-DEK implementation is now shared in internal/keychain/file_dek.go (Linux path deduplicated by ~40 lines). Documented in docs/reference.md (中英) with the security tradeoff spelt out so users make the choice explicitly.dws doc comment {list,create,create-inline,reply} returned PARAM_ERROR - 未找到指定工具 (fixes #240, also #234) — the four comment tools used to live on an independent doc-comment MCP server. After the Portal merged comment functionality into the doc server descriptor, the runtime tools/list on the merged doc server didn't include them, so every dws doc comment * call returned the "tool not found" PARAM_ERROR. The market metadata for the doc server now declares serverOverride: "doc-comment" on all four comment toolOverrides, so the existing CLI routing path sends dws doc comment * to the still-running doc-comment MCP server (which has the tools). No CLI code change was required, but existing users must run dws cache refresh once to pick up the updated descriptor — without that, the stale local market cache keeps pointing the call at the merged doc server and the error persists. Verified post-refresh: dry-run resolves to https://mcp-gw.dingtalk.com/server/doc-comment with tool list_comments, real calls return normal business responses (e.g. legitimate cross-org authz errors) instead of 未找到指定工具.Nothing published for this version
Two generic envelope-schema enhancements that close gaps the cli_to_mcp test suite kept surfacing — both product-agnostic, no hardcoded helper command
Two generic envelope-schema enhancements that close gaps the cli_to_mcp test suite kept surfacing — both product-agnostic, no hardcoded helper commands. Plus missing skill references for the already-registered sheet and wiki products are now shipped.
sheet (在线电子表格) skill reference + product-overview entry — the sheet product registers 34 envelope tools covering worksheet CRUD (create / new / list / info / copy_sheet / update_sheet), range read/write (range read / range update / append), dimension ops (add-dimension / insert-dimension / delete-dimension / move-dimension / update-dimension), merge (merge-cells / unmerge-cells), find/replace (find / replace), filter views (filter-view {create, list, update, delete, update-criteria, delete-criteria}), sheet-level filters (create_filter / get_filter / update_filter / delete_filter / set_filter_criteria / clear_filter_criteria / sort_filter), image write (write-image), and async export (submit_export_job + query_export_job). These were live in the envelope but skills/references/products/sheet.md had not shipped and skills/SKILL.md 产品总览 didn't list sheet, so agents had no reference to consult and were skipping it during intent routing. This release adds the doc, registers sheet in 产品总览 + 意图判断决策树, extends description to include 在线电子表格, adds a Sheet row to README.md / README_zh.md "Key Services", and notes the v1.0.25 reality on naming (about a third of sheet tools still expose snake_case cli_names pending CLIAliases (#246) rollout) and on export (no consolidated dws sheet export exists in v1.0.25 — submit_export_job + query_export_job are the atomic primitives; Pipeline (#247) provides the future plumbing).wiki (知识库) skill reference + product-overview entry — the wiki product's 7 envelope tools (wiki.create_wikiSpace, wiki.get_wikiSpace, wiki.list_wikiSpaces, wiki.search_wikiSpaces, wiki.add_member, wiki.list_member, wiki.update_member, surfaced as dws wiki space create / get / list / search and dws wiki member add / list / update) have been registered for a while, but no skills/references/products/wiki.md shipped with them, so agents had no per-command reference to consult. This release adds the reference doc, registers wiki in skills/SKILL.md's 产品总览 table and 意图判断决策树, mentions 知识库 in the skill description frontmatter, adds a Wiki row to README.md / README_zh.md "Key Services", and removes wiki from the "Coming soon" callout (which was now stale).CLIToolOverride.CLIAliases envelope field (#246) — lets a single MCP tool register additional cobra command aliases via envelope JSON (e.g. range read also accepts range get, member list accepts member ls). Plumbed through the existing Route.Aliases → cobra.Command.Aliases path; sibling conflicts are silently dropped by cobra. Lives in internal/market/registry.go + internal/compat/dynamic_commands.go.json_parse_strict transform (#246) — strict-JSON variant of json_parse that does not fall back to YAML. Use when the upstream tool requires a structured array/object and silently coercing a malformed input to a scalar string would mask a real user error (observed: filter-view --criteria 'NOT_VALID_JSON' was being accepted and quietly creating an empty-criteria view). In internal/compat/transform.go.CLIToolOverride.Pipeline + pipeline executor (#247) — a single CLI command can now orchestrate an ordered sequence of MCP tool calls plus optional HTTP-download sinks, declared entirely in envelope JSON. Motivating use case: the "submit-job → poll-status → download-result" pattern (e.g. sheet export) that previously required per-product hardcoded helpers.
PipelineStep supports type:"call" (with optional PollUntilField / PollUntilValue / PollIntervalSec / PollTimeoutSec for polling loops) and type:"download" (resolves DownloadURLField, HTTP GETs the body, writes to the path from OutputFlag, infers filename for directory paths).$flag.<name> resolves a user CLI flag by alias; $step.<idx>.<dotPath> walks a prior step's response (works through wrapped MCP envelopes); literals pass through.CLIFlagOverride.PipelineLocal marks a flag as CLI-side only so CollectBindings skips it (value never reaches MCP params); the pipeline executor still reads it via extractFlagValuesByAlias.jobId: <id>\n, downloadUrl: <url>\n) alongside the standard JSON envelope, so shell pipelines and regex-based tests can extract key values without JSON parsing.Three small but user-visible safety/usability changes: the embedded distribution now refuses to self-upgrade, the dws auth login help text finally mat
Three small but user-visible safety/usability changes: the embedded distribution now refuses to self-upgrade, the dws auth login help text finally matches the actual default flow (loopback, not device), and the release workflow gains a manual fallback trigger.
dws upgrade is blocked in embedded distributions (#248) — when the CLI is shipped as an embedded asset (e.g. inside another product), dws upgrade would happily overwrite the host-managed binary. The upgrade entry point now detects the embedded build flag and exits early with a clear message; covered by internal/app/upgrade_embedded_guard_test.go.dws auth login help text reflects the real default (#238, fixes #226) — the long help previously claimed "OAuth 设备流 (默认)", but the actual default starts a 127.0.0.1 loopback listener and only switches to device flow when --device is passed. SSH-into-headless-Linux users following the old text hit a dead end (remote-side 127.0.0.1 is unreachable from the local browser). Help and two flagErrorWithSuggestions messages in root.go are realigned: each method is named after its real flag (OAuth Loopback 流 (默认) / OAuth 设备流 (--device) / 直接提供 Token (--token)), with an explicit --device example for SSH/headless. No behaviour change.workflow_dispatch trigger added to release workflow as a fallback (#261) — GitHub occasionally drops tag-push events; the release job can now be re-run manually against any tag ref without having to delete and re-push the tag.Nothing published for this version
A single fix for HTTP proxy support across the CLI's custom HTTP transports. No behaviour changes elsewhere.
A single fix for HTTP proxy support across the CLI's custom HTTP transports. No behaviour changes elsewhere.
HTTP_PROXY / HTTPS_PROXY environment variables silently ignored by all custom transports (#237, fixes #236) — the three custom http.Transport instances built by the CLI (internal/transport/client.go MCP transport, internal/apiclient/client.go DingTalk OpenAPI client, internal/app/legacy.go IPv4-forcing registry client) all set DialContext / TLSClientConfig / timeouts but omitted the Proxy field. Per Go's net/http contract, a non-nil Transport without an explicit Proxy means "no proxy" — env vars are silently ignored, breaking sandboxed or air-gapped deployments that route outbound through HTTP_PROXY / HTTPS_PROXY. All three transports now set Proxy: http.ProxyFromEnvironment.Proxy func against http.ProxyFromEnvironment, avoiding flakiness from Go's envProxyOnce memoisation when running alongside tests that read proxy env early. (#237)Nothing published for this version
Two release-blocking bug fixes: dws attendance summary now exposes the server-required --stats-type flag (without it, every call returned C0002), and
Two release-blocking bug fixes: dws attendance summary now exposes the server-required --stats-type flag (without it, every call returned C0002), and the install scripts finally populate ~/.hermes/skills/dws/ for users who already have Hermes.
dws attendance summary returned C0002 (统计类型错误) on every call (#228, fixes #227) — the DingTalk MCP tool get_attendance_summary requires statsType at the business layer even though the schema marks it optional. The CLI did not expose any way to set it, so the command was 100% unusable. A new --stats-type flag (week / month) is now plumbed through to QueryUserAttendVO.statsType; the flag is documented as required in the long help, flag description, and skills/references/products/attendance.md..hermes/skills/ when populating skill directories (#221, fixes #188) — the AGENT_DIRS lists across build/npm/install.js, scripts/install.sh, scripts/install.ps1, scripts/install-skills.sh and the four upgrade-path mirrors (8 sources total once review feedback was addressed) did not include .hermes/skills, so users with Hermes installed were not getting ~/.hermes/skills/dws/ populated automatically. The existing parent-directory gate keeps this zero-side-effect for users without Hermes.--stats-type regression coverage in test/cli_compat/attendance_test.go — verifies statsType is written to QueryUserAttendVO when set to month or week, and is omitted when not provided. (#228)A single critical routing fix for dws drive commands. No new commands or behaviour changes elsewhere.
A single critical routing fix for dws drive commands. No new commands or behaviour changes elsewhere.
dws drive mkdir / dws drive download silently routed to the doc MCP server (#220, fixes #219) — when two MCP servers register tools with the same name (e.g. both drive and doc expose create_folder), the tool-level endpoint map used last-writer-wins, so drive-side calls landed on the doc endpoint and returned mock-shaped responses (success: true with a fake folderId) without actually creating anything. directRuntimeEndpoint now resolves product-level first when the caller already knows the productID, and only falls back to the tool-level lookup when productID is empty. The wrong-server collision and the resulting "succeeded but didn't" behaviour are gone.Documentation polish and a login regression fix. No behaviour changes outside the login MCP refresh path.
Documentation polish and a login regression fix. No behaviour changes outside the login MCP refresh path.
clientId from an old MCP cache (#213) — dws login now unconditionally re-fetches the MCP descriptor, so a previously cached client id can't keep producing auth errors after the server rotates it.dws chat message list pagination (#218, fixes #195) — clarifies that nextCursor is opaque and must be passed back as --cursor exactly; warns against parsing or reusing it as an offset.dws contact search examples (#209) — switched from the removed --keyword flag to the current --query.dws todo help text (#205) — expanded field semantics so MCP wrappers generate accurate schemas.dws chat message send-by-bot and dws report create help (#217, #106, #107) — --robot-code / --title / --text now carry the (必填) marker; report create --contents documents the key=field_name requirement and rewrites examples as a template detail → create two-step pipeline.Discovery hardening for edition overlays: edition.SupplementServers / FallbackServers hooks now consistently surface through the runtime catalog loade
Discovery hardening for edition overlays: edition.SupplementServers / FallbackServers hooks now consistently surface through the runtime catalog loader, not just the static command tree, so overlay products that live outside the Portal envelope (e.g. Wukong gray-release conference) resolve an endpoint on both the cold-cache and tool-not-in-catalog paths. Ships with per-edition cache partitioning to stop cross-edition disk-cache leakage, plus a small todo fix.
pkg/config.EditionPartition(name) (#197) — returns the cache partition key for a given edition. Open-source core ("" / "open") keeps using DefaultPartition (default/default); every other edition gets its own namespace (<edition>/default), preventing cross-edition data leakage in the shared ~/.dws disk cache. Lives in pkg/config as a leaf helper so internal/cli, internal/app, and internal/cache can all call it without risking import cycles.internal/editionmerge shared package (#197) — single source of truth for converting edition.ServerInfo into market.ServerDescriptor (ToDescriptor) and for merging SupplementServers / FallbackServers into a descriptor list. Both internal/cli (command tree) and internal/app (runtime catalog) now apply the edition hooks against the same discovery pipeline.EnvironmentLoader.loadFromCache honors SupplementServers even on empty registry (#197) — when the Portal registry cache is missing or empty, the catalog loader still materialises the edition's SupplementServers as endpoint-only discovery.RuntimeServer entries (source: edition_supplement), so hardcoded overlay commands for supplement-only products can still resolve an endpoint via the catalog path. Previously loadFromCache short-circuited to an empty catalog whenever the registry snapshot was empty, silently dropping gray-release products.DefaultPartition to EditionPartition(edition.Get().Name) (#197) — the runtime catalog, registry snapshot, and tools snapshot are now partitioned per edition instead of all editions sharing default/default.loadFromCache appends supplement servers alongside fresh-cache servers (#197) — supplement entries whose CLI.ID / Key are already present in the cached registry are skipped, so the hook never shadows Portal-published servers; only new products are added.runtimeRunner.Run falls through to directRuntimeEndpoint for supplement products (#197) — when the catalog contains the product (e.g. supplied by SupplementServers) but the specific tool is not declared, the runner now trusts directRuntimeEndpoint to resolve a working endpoint for the tool before returning the explicit catalog-miss error. Supplement entries intentionally carry no tool list, so this is the path that makes overlay-only tools executable.mergeSupplementServers / fallbackToDescriptors moved out of internal/app/legacy.go (#197) — relocated into internal/editionmerge and reused by the catalog loader, eliminating the duplicate edition.ServerInfo → market.ServerDescriptor logic that previously only ran on the static command-tree path.dws todo task get returns empty (#202) — the helper was calling query_todo_detail, which is not a valid MCP tool and returns empty. Switched to get_todo_detail as declared in discovery.json, restoring correct task-detail behaviour.edition.SupplementServers (not yet in the Portal envelope) now resolve an endpoint through the catalog path in both cold-start and tool-not-declared scenarios.internal/editionmerge/merge_test.go — descriptor conversion + supplement/fallback merge semantics.internal/cli/loader_partition_test.go + loader_supplement_test.go — edition-partitioned cache reads and supplement hook surfacing from loadFromCache (including empty-registry cold path and existing-ID deduplication).internal/app/legacy_wukong_partition_e2e_test.go — end-to-end cache partition isolation for the Wukong edition.internal/app/runner_supplement_fallback_test.go — runner falls through to directRuntimeEndpoint when the tool isn't declared by a supplement-sourced catalog entry.pkg/config/constants_test.go — EditionPartition name handling ("", "open", custom edition).4, Discovery/cache/protocol 6), dws pat chmod / pat browser-policy entry points, stderr-JSON classifier updates, and host-control metadata injection.Nothing published for this version
Raw DingTalk OpenAPI access lands as a new dws api surface for both api.dingtalk.com and oapi.dingtalk.com, backed by app-level token caching and guar
Raw DingTalk OpenAPI access lands as a new dws api surface for both api.dingtalk.com and oapi.dingtalk.com, backed by app-level token caching and guarded host allowlists. PAT enters the host-owned A-core loop: agent hosts can own authorization UI through DINGTALK_DWS_AGENTCODE, parse single-line stderr JSON, call dws pat chmod, and replay the original command. Chat helper regressions are fixed, skill references are brought back in line with shipped commands, and the v1.0.17 Mail release notes are backfilled into README / CHANGELOG.
4; Discovery, cache, and protocol negotiation failures now use exit code 6. Downstream scripts that previously treated 4 as Discovery must update their handling.dws api raw DingTalk OpenAPI command (#184) — direct DingTalk OpenAPI calls without writing an MCP wrapper first. Supports GET / POST / PUT / PATCH / DELETE, JSON --params / --data, stdin input, dry-run previews, --jq, field selection, --page-all, --page-limit, --page-delay, and --base-url.api.dingtalk.com requests use the x-acs-dingtalk-access-token header; oapi.dingtalk.com requests use the legacy access_token query parameter. The raw API client validates the target host before attaching credentials.DINGTALK_DWS_AGENTCODE is set, PAT hits return exit=4 plus single-line stderr JSON; the host renders authorization UI, calls dws pat chmod <scope>..., and replays the original command.dws pat chmod authorization entry point (#142) — grants scopes with --agentCode, --grant-type, and session fallback support; DINGTALK_DWS_AGENTCODE can supply the agent code when the flag is omitted.dws pat browser-policy --enabled <true|false> [--agentCode <id>] controls whether the CLI may open a browser, independently from --format output mode.code, errorCode, and error_code, including PAT_NO_PERMISSION, risk-tier PAT errors, PAT_SCOPE_AUTH_REQUIRED, and AGENT_CODE_NOT_EXISTS.data.hostControl and data.openBrowser, keeping host-facing JSON shapes aligned.claw-type: openClaw; DINGTALK_AGENT, DWS_CHANNEL, and host-owned PAT detection are kept as independent signals.flowId device-code fallback remain supported, with guarded debug output and envelope priority.dws chat message send --group ... again accepts and forwards --at-users, --at-all, and --at-mobiles; those flags are rejected outside group-chat mode so single-chat sends cannot silently drop @-mention intent.dws chat group members list --id <openConversationId> is reachable after the helper/dynamic merge path changed. cmdutil.MergeHardcodedLeaves now honors higher-priority helper groups when a dynamic envelope contributes a leaf at the same path.simple.md now uses shipped OA command names (list-pending, list-initiated), removes a non-existent devdoc search-error command, and marks workbench.md as Draft because workbench commands are not available in the runtime.dws pat chmod now returns an explicit error instead of treating {"Content": null} as success.DWS_SESSION_ID / REWIND_SESSION_ID values are no longer logged when the two env vars disagree.members list, helper-vs-envelope shape mismatch, and merge-priority behavior. (#180)Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →