* feat(dev): add DEAP agent commands
* feat(dev): add DEAP profile flags
* feat(deap-agent): 观测命令对齐 HSF 接口收敛
- 删 run-executions、resolve-run-id 两个命令(HSF 接口已移除对应方法)
- run-status 改双定位:--run-id 或 --source-id+--source-type 二选一,全场域通用;
移除旧的 assistantId+taskId 单聊口径
- 新增 send-message:以数字员工身份发消息返回 runId,写操作需 --yes 确认
- source-type 加白名单 enum(im_message/trigger_rule/scenario_instance)
- 同步契约测试与叶子清单
* refactor(deap-agent): source-type 白名单收敛为 im_message + trigger_rule
chat-2 删除 scenario_instance 来源类型后同步;并注明 im_message 的 sourceId 是
钉钉开放态 openMessageId,单聊/群@/群感知三条链路含义一致。
* fix(deap-agent): 按规范解析内建 MCP 端点
* fix: align deap agent cli with mcp tools
* refactor(deap-agent): 删除 send-message;run-status/trace 只按来源定位
- 删除 send-message 命令(HSF 侧 sendMessage 已移除)
- run-status / trace 去掉 --run-id 与 --trace-id,统一 --source-id + --source-type
且与 --assistant-id 一并必填:调用方拿不到 runId,留个填不了的 flag 只诱导瞎传
- 保留辰驷 c158ea17 对生命周期命令的 MCP 文案对齐
* refactor(deap): 提到顶级 dws deap 前缀,分管理态/观测态两子组
DEAP 是独立平台,与开放平台应用(agentId/clientId/机器人配置)无包含关系,
此前挂在 dws dev deap-agent 下会让两个产品的路由与文案互相干扰。
命令树:
- dws deap manage create/detail/list/save-draft/publish/delete(含不可逆写)
- dws deap observe run-status/trace(全部只读)
契约:ProductID dev→deap,canonicalPath 与 CLIPath 全量更新;MCP tool 名不变,
MCP 侧配置无需调整。
Skill 文档:新增 references/deap/(index + manage + observe),并在 misc 路由表、
说明与 frontmatter 注册。observe.md 写清 openTaskId→openMessageId 两跳取法——
dws chat message send 只返回 openTaskId,直接拿它查必然 NOT_FOUND,而报错
看不出是 ID 类型错了。
* refactor(deap): dev.go / devapp_test.go 回退至主干,不在其领域留痕
dev 是开放平台应用的命令树,属另一领域。DEAP 迁到顶级 dws deap 后,
dev 侧无需任何改动——之前加的挂载已删,剩下的注释与文案提示也一并回退,
两个文件现与 origin/main 逐字一致。
DEAP 不再挂 dev 的事实由 deap_agent_test.go 的 TestDeapIsNotMountedUnderDev
单独钉住(断言写在本领域测试文件里,不侵入 dev 的测试)。
* docs(deap): 去掉与 dev 的对比性提示
那些"两者是不同产品/别搞混/曾挂在 dev 下"的措辞源于早先把 DEAP 挂在 dev 下的
误解。dev 与 DEAP 本无关联,写对比反而凭空建立联想并暗示一段已不存在的历史。
清理:deap-index.md 的混淆警示、misc SKILL.md 的路由提示、deapHandler 与
deapProductID 的注释、TestDeapIsNotMountedUnderDev(该测试同时耦合 devHandler)。
保留 AvoidWhen 里的产品边界声明:create 在两侧同名,属真实可能的误选,
与"别搞混"的元叙述不同。
* feat(deap): select detail configuration type
* ci(actions): 简化构建并发布 Release
* feat: add Skill and MCP commands to deap agent
* fix(deap): compose Skill upload and creation
* fix(deap): route MCP query to published tool
* fix(deap): route skill query to registered tool
* feat(deap): support combined response modes
* feat(deap): use scoped credential for skill upload
* fix(deap): call published skill upload credential tool
* fix(deap): follow renamed upload credential tool
* docs(deap): explain temporary dws token workflow
* docs(deap): remove deprecated dws token workflow
* feat(dingtalk-tag): rename deap command and add auth token
* docs(dingtalk-tag): clarify dws auth credential response
* refactor(dingtalk-tag): group capabilities and unify agent IDs
* feat(dingtalk-tag): support main program type
* docs(dingtalk-tag): document local agent program type
* feat(skill): align dingtalk-tag guidance
* feat(dingtalk-tag): connect digital employees to local DSH
* fix(dingtalk-tag): constrain main program types
* feat(dingtalk-tag): filter employee list by program type
* fix(dingtalk-tag): resolve managed token identity
* fix(dingtalk-tag): bind managed identities to published profile
* fix(dingtalk-tag): resolve async reply receipts
* docs(dingtalk-tag): clarify default program type guidance
* feat(dingtalk-tag): add profile-only connect mode [skip ci]
* fix(dingtalk-tag): resolve operator in employee token scope
* feat(dingtalk-tag): add isolated local Agent adapters
* fix(dingtalk-tag): close registration races and verify worker lifecycle
* fix(dingtalk-tag): validate saved employee self identity
* feat(dingtalk-tag): unify adapter lifecycle and DSH release control
* fix(dingtalk-tag): fence lost hosts and recover DSH registration
* feat(dingtalk-tag): rename managed login command
* feat(dingtalk-tag): complete managed employee login
* fix(auth): complete external code login and address review findings
Based on build.17.1. Expose auth exchange with client-id selection, verified identity persistence, transactional direct credentials, scoped configuration and accurate provenance. Redact code arguments in performance reports. Include regression tests and bounded real-service verification notes.
* fix(auth): isolate exchange invocations and preserve credential pairing
* fix(auth): close early-exit and config-snapshot isolation gaps
* fix(dingtalk-tag): tolerate temporarily unavailable send receipts
* feat(dingtalk-tag): integrate server device binding lifecycle
* fix(dingtalk-tag): fence uncertain binding writes and preserve retry receipts
* test(dingtalk-tag): update binding command and interface contracts
* fix: refresh local agent binding token and classify rejections
* chore: repair digital employee release fragment metadata
* fix: align local agent MCP tool names
* fix(release): upload only distributable archives and checksums
* docs(release): require change notes and include installation guidance
* fix: align local agent MCP binding contract (#13)
Validated against the pre-production bind -> rebind -> unbind lifecycle on PR head 49e20e79cdbf9b07efbc581aefe90cb839957dff.
* fix(dingtalk-tag): mark digital employee event source
* feat(dingtalk-tag): remove connect profile-only mode
* feat(dingtalk-tag): 移除 connect bind 和 rebind 子命令
* fix(dingtalk-tag): flatten MCP config and require employee scope
* fix(connect): 修正未知命令纠错和发布详情错误提示
* docs(dingtalk-tag): align MCP draft auto-mount guidance
* docs(dingtalk-tag): correct MCP auto-mount release note
* feat(dingtalk-tag): add digital employee management and local agent integration
* fix(dingtalk-tag): align dry-run contracts and cross-platform CI
* fix(event): 修正数字员工传输就绪与系统帧处理
* docs: 补充数字员工真实收发与恢复验收证据
* test(dingtalk-tag): cover platform identity and failure boundaries
* test(dingtalk-tag): exercise worker startup and upload failure paths
* fix(dingtalk-tag): handle Windows shutdown and corrupt state paths
* test(dingtalk-tag): cover login delivery and unbind recovery boundaries
* docs: 明确原消息故障未解决并补充来源对照
* fix: adapt upstream named registration and test employee failure boundaries
* fix: complete employee lifecycle cancellation and failure coverage
* test: wait for consumer startup before cancellation
* docs: align shared skill descriptions with upstream routing
* fix: allow atomic employee state replacement during Windows reads
* fix: retry bounded Windows snapshot rename contention
* test: exercise employee daemon lifecycle on Windows
* feat(dingtalk-tag): align user-facing manage contract
* fix(dingtalk-tag): unify supervisor user id field
* fix: 数字员工事件消费沿用默认来源
* fix(dingtalk-tag): unify avatar URL handling
* fix(dingtalk-tag): map agent type to MCP contract
* fix(dingtalk-tag): validate response mode by agent type
* feat(dingtalk-tag): add manage set-visibility command and align merged tests
- Add `dws dingtalk-tag manage set-visibility` leaf binding the set_visibility
MCP tool (agent-uuid/visibility required; staff-ids/dept-ids as full-replace
string slices), with full contract (Identity/Interface/Safety/Selection).
- Align merge-carried PR-only tests with the feature branch's authoritative
contract:
- drop deapAgentProfileJSON assertions (profile-json flag removed by feature)
- remove retired employee-no size-validation case
- update published-detail missing-identity error assertion to the generalized
message that no longer leaks field names
- provide required --response-mode in RemovesRetiredFlags create case (open_code
default now mandates it) and reflect it in expected MCP args
* fix: address PR #1391 CI lint and auto-CR P1/P2 findings
- CI Lint (make format-check): gofmt-realign the create-args map literal in
deap_agent_test.go so the format gate passes and the full test matrix runs.
- [P1] external_exchange.go: reuse the ClientSecret of the finally selected
clientID by scanning all candidates. The reuse loop was gated on
clientID == configured.id, but configured is only the first valid candidate,
so explicitly selecting a later app/env candidate via --client-id dropped its
unmanaged secret and fell through to an empty-secret managed exchange,
breaking that app's normal OAuth authorization-code login. Managed-ness is now
decided per candidate. Adds env/app later-candidate regression tests.
- [P2] apiclient: reject CR/LF in the streaming UploadMultipart field keys,
file field name and file name before creating the pipe/goroutine, matching the
existing non-streaming newMultipartBody guard, so user-controlled names cannot
inject extra MIME headers/fields. Adds streaming-entry injection tests.
* test(app): align deap-agent schema contract with simplified dingtalk-tag contract
The feature branch simplified the digital-employee create/save-draft/list
contract in source but internal/app/schema_deap_agent_contract_test.go still
asserted the pre-simplification shape, so the final-schema gate failed once CI
Lint stopped fail-fast-skipping the matrix. Verified the same failures exist on
the pristine feature branch (pre-existing test debt, not a merge artifact).
- create_digital_employee: 11 -> 7 params (drop dept-name/icon/profile-json/
employee-no/position-name/supervisor-uid; add avatar-url/supervisor-user-id;
main-program-type property digitalTagEmployeeProfile.mainProgramType -> type).
- update_digital_employee_draft: 15 -> 11 params (same removals/additions).
- list_digital_employees: main-program-type property mainProgramType -> type.
- MCP auto-mount help/schema: expected phrases 自动追加/selectedSkills ->
自动挂载/回读确认 to match the current create_mcp guidance.
- Add set_visibility to the final-schema contract (new manage leaf: write/high/
user_required; agent-uuid/visibility required, staff-ids/dept-ids arrays).
* fix(dingtalk-tag): make create example pass agent dry-run gate
The create_digital_employee example was rewritten to showcase --avatar-url but
dropped --response-mode and pointed avatar-url at a local ./avatar.png. Under
DWS_AGENT_EXAMPLES_DRY_RUN the example must survive the command's own
validation: open_code (default) now requires a --response-mode, and a local
avatar path must be a readable image file, which the dry-run harness does not
materialize. Restore --response-mode mention_only and use a public HTTPS avatar
URL so the example dry-runs through the standard request preview without a
local file read.
* test(helpers): reach 100% changed-code coverage for deap avatar/skill-upload; preserve credential error cause
The changed-code coverage gate (target 100%) failed at 97.74% because the
platform harness only runs TestCrossPlatformCoverage*/TestAllShortcuts tests,
and the digital-employee avatar-upload feature shipped without such tests.
- Rename the four TestDevDeapAgent* avatar/local-agent tests to the
TestCrossPlatformCoverage* convention so the platform coverage harness runs
them (avatar uncovered statements 59 -> 32).
- Add deap_agent_avatar_coverage_test.go covering the remaining branches:
avatar URL validation (http-no-host/unsafe-path/bad-ext/unreadable/dir/
oversize/valid), stage-error formatting + Unwrap, created-UUID envelope
traversal, profile preparation, invalid main-program-type, save-draft
response-mode/avatar validation, create/save avatar call branches (dry-run,
transport error, parse error, upload error), and the OpenAPI avatar uploader
delegate.
- Improve the skill-upload credential error: stop collapsing every cause into a
bare "OpenAPI 认证失败". Preserve the underlying stage cause and server
code/trace for the MCP credential call (no secret exists yet on failure),
while still redacting the injected resolver error so credential material can
never leak. Covered by a new temporaryCredential MCP-branch test.
Local platform coverage gate now reports 100.0000% (2971 changed statements).
* test(helpers): make avatar unsafe-path case cross-platform for Windows coverage
The avatar URL-input branch test used "/abs/path.png" to trigger SafeInputPath's
absolute-path rejection, but filepath.IsAbs("/abs/path.png") is false on Windows,
so it fell through to the unreadable-file branch and failed Coverage (Windows)
with '本地文件不可读' instead of '路径不安全'. Use a NUL control character
("invalid\x00.png"), which rejectControlChars rejects on every platform (same
convention as the existing upload-boundaries test), keeping the unsafe-path
branch covered cross-platform.
* fix(dingtalk-tag): gate employee enqueue on capacity before writing ledger
Previously enqueue wrote the audit entry and dedup task file before checking
the 128-conversation and 32-per-queue capacity limits. A transient capacity
rejection therefore left an 'accepted' task file behind, so Event Bus
redelivery of the same event was short-circuited as already handled and the
message was permanently swallowed (recovery only rewrites accepted->needs_review,
never reprocessing).
Move both capacity checks ahead of audit/writeEmployeeJSON. enqueue holds r.mu
and is the sole producer, so a queue with room can never fill before the send,
letting the record persist only once acceptance is guaranteed; the send is now
unconditional and non-blocking.
Add regression coverage asserting that queue_capacity and conversation_capacity
rejections leave no dedup record and that redelivering the same event after the
backpressure clears is accepted and persisted.
* docs(dingtalk-tag): describe set-visibility scope as ALL/PARTIAL
Align the set-visibility command help, flag usage, MCP description, example and
parameter docs with the server contract that now accepts ALL and PARTIAL
(SELECTED kept only as a legacy alias). PARTIAL is stated explicitly to mean
'visible to the specified members/departments', and the example uses the full
--visibility PARTIAL value instead of the truncated PART.
* feat(dingtalk-tag): unify capability lifecycle
* 补齐数字员工能力跨平台覆盖测试
* docs: add reproducible test evidence for PR #1391
* fix(dingtalk-tag): upload the validated skill file handle
* docs: refresh PR 1391 test evidence and CR resolutions
* ci: give Windows native coverage enough time to finish
* fix(dingtalk-tag): align profile contracts and require explicit type
* fix(test): satisfy required employee type fixture
---------
Co-authored-by: chensi.wx <chensi.wx@alibaba-inc.com>
Co-authored-by: siyideng <dsy274007@alibaba-inc.com>
Co-authored-by: chichuan <haofeng.hf@alibaba-inc.com>
Co-authored-by: 赤川 <30925823+haofeng0705@users.noreply.github.com>
Co-authored-by: 丞天 <yuanchenyu.ycy@alibaba-inc.com>
Co-authored-by: dingpan <dingpan.dp@alibaba-inc.com>
Host hrbrain, markdown, pat, and profile under dingtalk-misc, retire
their standalone packages, and rename dws-shared to dingtalk-shared
across live paths, coverage, installers, and policy.
Co-authored-by: Cursor <cursoragent@cursor.com>
Schema Catalog now assembles from Contract/ParamDecl/Interface and Cobra only.
Keep fetch-mcp-metadata as an optional diagnostic dump and ban the retired pin path.
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the compiled-literal feasibility probe and its generator; runtime
Catalog delivery is already single-track ResolveSchemaBuild only.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move the last hint parameter overlays into in-code ParamDecls (helpers +
shortcuts), regenerate catalog, and harden the migrate script for factory
and Use/RPC matching so all 74 overlay tools are declaration-backed.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move 121/210 parameter-level Schema field overlays from
schema_hints/metadata JSON into in-code ParamDecl declarations on
DeclareLeafMetadata commands. The declared values are emitted as
dws.schema.* annotations at assembly time via ApplyParamDecls,
outranking tool_schema_hint (rank 620 > 500) so the hint overlay
becomes redundant once the declaration is in place.
Key mechanism changes:
- Add SchemaDecl.Parameters []ParamDecl with property/required/
interface_type/description/required_when/enum fields
- Add cli.ParamDecl type carried inside ContractFinalPayload
- ApplyParamDecls emits dws.schema.* annotations from the payload
at assembly time (no sync.Map, no tree-rebuild key issue)
- Add cli.AnnotateRuntimeFlagInterfaceType (41 overlays needed it)
- Add cli.AnnotateRuntimeFlagRequiredValue for explicit true/false
- Compatibility alias check tolerates dws.schema.* annotation
differences between primary and alias commands
- Remove runtime source_hash recomputation (87ms/997k allocs saved;
enforced by check-generated-drift.sh at build time instead)
- Add shortcut.Flag.RequiredWhen and wire through FromShortcut
- Add boolFlag OR semantics to fix confirmationBypass disagreement
- Add bindKey default kebab-to-camel for forgotten Bind
- Add schema consumption benchmarks (catalog decode 1.5s/817MB,
shortcut load 1.9ms/3MB — three orders of magnitude apart)
- Add catalog codegen feasibility probe (34 tools: 0.06s compile,
31ns access, 87KB linked — extrapolates to 2.1MB for 845 tools)
Migrated products (29 tools, 121 fields):
aisearch(1), chat(12), contact(4), doc(3), drive(1),
hrbrain(10), mail(2), report(1), sheet(3), todo(6)
Remaining 89 fields across 11 products blocked by:
- RPCName not found as string literal (19 tools, shortcut/variable)
- No matching DeclareLeafMetadata near callMCPTool (11 tools)
- No single RPCName for multi-step commands (drive.upload etc.)
All tests green: corecmd, cli, helpers. Generation and drift clean.
Accept corpId:userId and friendly organization/account selectors across global --profile, profile switch/use, event child processes, and multi-profile command execution.
List every local account in storage order with live identity-token status, preserve exact current and previous identities, and require explicit selection when an organization has no deterministic current account.
Extend auth logout to remove one exact account, every account in one organization, or all local accounts while revoking each token with its persisted credentials.
Use in-memory login tokens for identity enrichment before persistence, refresh generated Schema artifacts, and cover the complete CLI flow with isolated beta.3 end-to-end tests.
* feat(auth): support multi-profile login
* fix(auth): complete multi-org profile acceptance
* feat(auth): 完成多组织 profile 验收
* docs(auth): 补充多组织 Ralph 验收材料
* feat(auth): 支持 auth switch TUI 切换 profile
* feat(auth): logout 默认清理所有组织
* feat(auth): login 默认新增组织授权
* feat(profile): 使用 profile switch 切换组织
* docs(ralph): 更新 profile switch 验收材料
* fix(profile): 展示全部可切换组织
* feat(profile): support multi-org switch tui
* chore(install): add branch source installer
* fix(profile): keep global profile out of tool params
* feat(profile): support csv multi-profile runtime
* ci: add multi-profile e2e workflow
* ci: run multi-profile e2e on all branches
* docs: document multi-profile e2e ci gate
* docs: remove multi-profile test cases from pr
* ci: harden multi-profile e2e gates
* fix(auth): serialize profiles.json RMW and harden multi-profile persistence
Wrap all profiles.json read-modify-write paths (profile switch/use/remove,
status marking, token save, logout) in the existing dual-layer lock via a new
withProfilesLock helper. Split each writer into a public (locking) entry point
plus a lock-free *Locked variant so the non-reentrant lock is never re-acquired;
the refresh path (oauth_helpers) and the load-path legacy migration now call the
lock-free saver to avoid self-deadlock.
Also: write profiles.json and the token marker via per-write random temp names
(uuid) to stop concurrent writers from corrupting a fixed .tmp file; quarantine
an unparseable profiles.json and rebuild an empty config so the CLI can
self-heal instead of locking out auth reset/logout; make DeleteAllTokenData
proceed even if profiles.json cannot be read; and stop SyncLegacyTokenMirror
from deleting the legacy mirror on a transient keychain read error.
* fix(auth): do not fall back to a different org's legacy token slot
When no explicit --profile is given, LoadTokenDataForProfile resolves the
current/primary profile and reads its per-corp keychain slot. If that slot
read failed, the code silently fell through to the legacy single token slot,
which after any drift between the legacy mirror and the current profile could
belong to a different organization. The command would then run as the wrong
org with no indication to the user.
Reproduction (conceptual):
- profiles.json currentProfile = corpA
- corpA's keychain slot is unreadable, legacy single slot still holds corpB
- any read command (no --profile) silently used corpB's token
Fix: when a profile is resolved but its slot read fails and no --profile was
given, only fall back to the legacy single slot when its CorpID matches the
resolved profile (same org); otherwise return the original error instead of
acting as a different organization. The no-profile legacy path (pre-migration
installs with no resolved profile) is unchanged.
Tests:
- Covered by the existing internal/auth suite under go test -race; the
same-org fallback preserves the legacy-mirror case while the cross-org
case now surfaces the read error.
* feat(skill): document multi-org profile usage and always ship dws-shared
The skills had no guidance on the multi-profile capability, so an agent would
treat the CLI as single-org: when a lookup missed in the current org it would
give up or ask the user instead of searching other logged-in orgs. The multi
skill set also referenced a `dws-shared` prerequisite that was never actually
installed, and the only multi-org hints lived inline in three product skills.
This adds, in source only:
- A "multi-org / profile" section in the mono SKILL.md (concept, commands,
cross-org rule, aggregation, safety guardrails) plus a decision-tree entry,
trigger conditions, and a corrected logout danger-table row (logout removes
all orgs by default; removing the primary silently re-elects a new primary,
confirm before removing the primary).
- A standalone skills/multi/dingtalk-profile skill mirroring the same content.
- A new skills/multi/dws-shared skill that carries auth, global flags and the
multi-org rule, so every product skill's PREREQUISITE resolves and all
read/search skills inherit the cross-org behavior without per-skill edits.
- Cross-org fallback notes on dingtalk-aisearch / chat / contact.
To guarantee the prerequisite actually ships, multi-mode install now force-
includes dws-shared even when --skill / --exclude narrows the set (no-op when
the source has no dws-shared, preserving older layouts).
Tests:
- internal/app: TestP1SharedAlwaysIncludedWithSkillFilter installs with
`-s aitable` and asserts dws-shared still lands in the destination;
TestP1SharedNoopWhenAbsent guards the older-layout no-op.
- go test -race ./internal/auth/... ./internal/app/... passes.
---------
Co-authored-by: shangguanxuan.sgx <shangguanxuan.sgx@alibaba-inc.com>
Co-authored-by: qinze <audanye@gmail.com>
Override leaves whose backing MCP tool isn't actually deployed rendered in
`dws <svc> --help` but failed at invocation with "tool not found" (43 phantom
commands across 391 overrides; attendance declared 38, only 4 deployed).
BuildDynamicCommands now takes an existingTools oracle (CLI slug -> live tool
set from the tools/ cache). A leaf whose tool is missing from its resolved
server's set is marked Hidden; groups left childless collapse. Safety rails:
acts only when a server's tool set is KNOWN and non-empty (cold cache / overlay
/ plugin paths pass nil and no-op, never blanking the tree); serverOverride
leaves resolve against the target server; pipeline leaves are never hidden.
Adds scripts/dev/check-phantom-overrides.py as a publish-time gate, and
phantom_guard_test.go covering hide / cold-cache-keep / serverOverride / pipeline
/ empty-group-collapse.