Files
42d8178a5a feat(dingtalk-tag): 数字员工管理、身份登录与本地 Agent 接入 (#1391)
* 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>
2026-09-22 10:46:27 +08:00
..
2026-03-27 17:55:57 +08:00
2026-07-21 09:41:18 +08:00