@xdxer/dingtalk-agent 0.1.4-beta.7 → 0.1.4-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +1 -2
  2. package/dist/bin/dingtalk-agent.js +17 -334
  3. package/dist/bin/dingtalk-agent.js.map +1 -1
  4. package/dist/src/init.js +1 -1
  5. package/dist/src/init.js.map +1 -1
  6. package/package.json +10 -12
  7. package/dist/src/boot.js +0 -70
  8. package/dist/src/boot.js.map +0 -1
  9. package/dist/src/duty.js +0 -74
  10. package/dist/src/duty.js.map +0 -1
  11. package/dist/src/kb.js +0 -240
  12. package/dist/src/kb.js.map +0 -1
  13. package/dist/src/runs.js +0 -79
  14. package/dist/src/runs.js.map +0 -1
  15. package/docs/ARCHITECTURE.md +0 -219
  16. package/docs/MINIMAL-WORKSPACE-V1.md +0 -172
  17. package/docs/OPEN-SOURCE-REFERENCES.md +0 -107
  18. package/docs/SELF-TEST.md +0 -252
  19. package/docs//345/206/205/347/275/221/345/256/236/347/233/270.md +0 -77
  20. package/evals/baselines/2026-07-14/behavior-summary.json +0 -28
  21. package/evals/baselines/2026-07-14/contract-summary.json +0 -18
  22. package/evals/baselines/2026-07-14/live-canary-summary.json +0 -25
  23. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/SKILL.md +0 -72
  24. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/action-contract.md +0 -31
  25. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/event-to-behavior.md +0 -22
  26. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/memory-and-evolution.md +0 -25
  27. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/runtime-modes.md +0 -34
  28. package/evals/baselines/2026-07-15/task-lifecycle-summary.json +0 -50
  29. package/evals/evals.json +0 -339
  30. package/evals/fixtures/dm-ambiguous-send.json +0 -4
  31. package/evals/fixtures/dm-blocked.json +0 -4
  32. package/evals/fixtures/dm-clear.json +0 -4
  33. package/evals/fixtures/dm-discussion.json +0 -4
  34. package/evals/fixtures/dm-doc-write-no-tool.json +0 -4
  35. package/evals/fixtures/dm-long-task-ack.json +0 -4
  36. package/evals/fixtures/dm-nonblocking-gap.json +0 -4
  37. package/evals/fixtures/dm-structured-task.json +0 -4
  38. package/evals/fixtures/group.json +0 -10
  39. package/evals/fixtures/mentioned.json +0 -3
  40. package/evals/run-contract-evals.mjs +0 -1169
  41. package/evals/run-shadow-evals.mjs +0 -267
  42. package/evals/runners/README.md +0 -66
  43. package/evals/runners/claude-shadow.mjs +0 -533
  44. package/evals/schemas/action-request.schema.json +0 -77
  45. package/evals/shadow-evals.json +0 -133
  46. package/skills/AGENTS.md +0 -104
  47. package/skills//344/273/273/345/212/241.md +0 -48
  48. package/skills//345/237/272/347/241/200/350/241/214/344/270/272.md +0 -44
  49. package/skills//345/277/203/350/267/263.md +0 -79
  50. package/skills//346/266/210/346/201/257.md +0 -50
  51. package/skills//347/237/245/350/257/206.md +0 -55
  52. package/skills//350/257/204/346/265/213.md +0 -62
  53. package/skills//351/222/211/351/222/211.md +0 -64
  54. package/templates/ontology/index.md +0 -24
  55. package/templates/ontology/self/access.md +0 -38
  56. package/templates/ontology/self/role-spec.md +0 -34
  57. package/templates/ontology/self/workspace.md +0 -41
package/README.md CHANGED
@@ -253,7 +253,6 @@ dingtalk-agent init
253
253
  dingtalk-agent prepare --event-file event.json --json
254
254
  dingtalk-agent help runtime
255
255
  dingtalk-agent help adapters
256
- dingtalk-agent eval contract
257
256
  ```
258
257
 
259
258
  `listen` 是可选的开发联调 Adapter,不是 Agent 主进程。云端 Driver、Claude Code 插件或本地 DWS 都可以提供事件,并从标准化事件之后复用同一 Session / Run / Action 内核。
@@ -266,7 +265,7 @@ npm run typecheck
266
265
  npm run eval:contract
267
266
  ```
268
267
 
269
- 当前确定性合同包含 **30 个场景**,覆盖:
268
+ 当前确定性合同包含 **31 个场景**,覆盖:
270
269
 
271
270
  - 通过 `npx skills` 委托全局 Skill 安装、发现、重装和卸载;
272
271
  - 首次 setup、用户级 PATH、DWS 版本/认证和三端客户端发现;
@@ -13,10 +13,6 @@ import { join, resolve } from 'node:path';
13
13
  import { readFileSync } from 'node:fs';
14
14
  import { spawn, spawnSync } from 'node:child_process';
15
15
  import * as cfgmod from '../src/config.js';
16
- import * as kbmod from '../src/kb.js';
17
- import * as runsmod from '../src/runs.js';
18
- import * as dutymod from '../src/duty.js';
19
- import { boot } from '../src/boot.js';
20
16
  import { init } from '../src/init.js';
21
17
  import { ackDispatch, listPendingDispatches, prepareSession } from '../src/sessions.js';
22
18
  import { readEventFile, consumeNdjson, heartbeatEvent } from '../src/driver.js';
@@ -35,47 +31,23 @@ const ROOT = process.env.DTA_ROOT
35
31
  ? resolve(process.env.DTA_ROOT)
36
32
  : (cfgmod.findRoot(process.cwd()) || process.cwd());
37
33
  const HELP = `
38
- dta(dingtalk-agent)—— Skill-first 的钉钉数字员工行为容器
34
+ dta —— 给 Agent 安装钉钉员工的基础行为和工作环境
39
35
 
40
- 用法: dta <command> [options]
41
- 完整名称 dingtalk-agent 与 dta 完全等价。
36
+ 装好后,Agent 可以:
37
+ · 判断 @、单聊和群消息是否应该回应
38
+ · 通过 DWS 回复消息、协作并使用钉钉标准产品
39
+ · 从本地目录或钉钉文档加载身份、记忆和知识
42
40
 
43
- 首次安装或升级:
44
- npm install --global --prefix "$HOME/.local" --registry=https://registry.npmjs.org @xdxer/dingtalk-agent@${PACKAGE_JSON.version}
45
- "$HOME/.local/bin/dta" setup --skip-cli-install
46
- setup 用户级安装 CLI、修复 PATH、安装 Skill、检查 DWS
47
- upgrade 自升级 CLI,并重新执行 setup 与完整 doctor
48
- [--channel beta|latest] [--version X] [--dry-run] [--force] [--json]
49
- doctor 检查 PATH / Node / DWS / Skill / Agent 客户端
41
+ 开始使用:
42
+ dta setup 首次配置 CLI、DWS 和基础行为 Skill
43
+ dta bootstrap --json 为当前 Agent 会话加载工作区
50
44
 
51
- Skill(不要求 Workspace)
52
- skill install 安装全局 Basic Behavior(Claude Code + Codex + OpenCode)
53
- skill status 检查版本、漂移和客户端可发现性
54
- skill upgrade 通过 npx skills 重装当前包内 Skill
55
- skill uninstall 通过 npx skills 卸载全局 Skill
56
- 通用: [--dry-run] [--json];安装生命周期委托给 npx skills
45
+ 维护环境:
46
+ dta doctor 检查哪里没装好,并给出修复提示
47
+ dta upgrade 自升级,再重新检查完整环境
48
+ dta skill status 查看 Claude Code、Codex、OpenCode 能否发现 Skill
57
49
 
58
- 每个 Agent Session(不要求 init)
59
- bootstrap 发现当前本地 Workspace;不修改项目文件
60
- [--storage <uri>] local-dir:<path> | dingtalk-doc:<node-or-url>
61
- [--state-dir <path>] 远端文档只读快照目录
62
- [--profile <dws-profile>] [--json]
63
-
64
- 可信事件模式(可选;需要一次 init)
65
- init [--context-id X] 仅为 Prepared Run 建最小 Workspace
66
- prepare --event-file <file|-> 事件 → Session → Run
67
- act ack 对原消息贴「收到」;不代表接单
68
- act reply --text "…" 引用回复原消息
69
- act ask --text "…" 只问一个真正阻塞的问题
70
- act silence [--reason "…"] 不外发,但留下行为回执
71
- 通用: [--run <path>] [--dry-run] [--json]
72
-
73
- smoke 极简本地验收(不写钉钉)
74
- eval contract / behavior 完整合同或模型行为评测
75
-
76
- help runtime 事件宿主与 controller 命令
77
- help adapters 可选 DWS listen 适配器
78
- help legacy 查看兼容的本体、KB、值班与进化命令
50
+ 接入钉钉事件和可靠外发:dta help runtime
79
51
  `;
80
52
  const RUNTIME_HELP = `
81
53
  dingtalk-agent 可信事件运行时(可选宿主能力)
@@ -98,17 +70,6 @@ dingtalk-agent 可选 Driver / 开发联调适配器
98
70
 
99
71
  listen 不是 Agent 主入口。Claude Code、DWS 云端或其他宿主都可以直接提供事件。
100
72
  `;
101
- const LEGACY_HELP = `
102
- dingtalk-agent 兼容命令(不属于 P0 四对象主链)
103
-
104
- boot
105
- kb mount|list|sync|search
106
- duty --check|--run|--list
107
- todo
108
- log / feedback / runs / evolve
109
-
110
- 这些能力继续可用,但新增 Agent 应先安装全局 Skill;只有可信事件宿主才需要 runtime。
111
- `;
112
73
  function fail(msg, code = 1, hint = '') {
113
74
  console.error(`错误: ${msg}`);
114
75
  if (hint)
@@ -127,10 +88,6 @@ catch (e) {
127
88
  fail(message, Number(error?.exitCode) || 2, errorHint(message));
128
89
  }
129
90
  async function main() {
130
- if (cmd === 'help' && sub === 'legacy') {
131
- console.log(LEGACY_HELP);
132
- return;
133
- }
134
91
  if (cmd === 'help' && sub === 'runtime') {
135
92
  console.log(RUNTIME_HELP);
136
93
  return;
@@ -139,6 +96,9 @@ async function main() {
139
96
  console.log(ADAPTER_HELP);
140
97
  return;
141
98
  }
99
+ if (cmd === 'help' && sub) {
100
+ fail(`不认识的帮助主题: ${sub}`, 1, '运行 dta --help 回到产品入口;事件宿主可用 dta help runtime。');
101
+ }
142
102
  if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') {
143
103
  console.log(HELP);
144
104
  return;
@@ -450,285 +410,8 @@ async function main() {
450
410
  process.exitCode = 2;
451
411
  return;
452
412
  }
453
- // ── eval:宿主评测入口;shadow 永不调用 act / DWS。──
454
- if (cmd === 'eval') {
455
- const script = sub === 'contract'
456
- ? join(PACKAGE_ROOT, 'evals', 'run-contract-evals.mjs')
457
- : sub === 'shadow'
458
- ? join(PACKAGE_ROOT, 'evals', 'runners', 'claude-shadow.mjs')
459
- : sub === 'behavior'
460
- ? join(PACKAGE_ROOT, 'evals', 'run-shadow-evals.mjs')
461
- : '';
462
- if (!script)
463
- fail('eval 的子命令: contract / shadow / behavior');
464
- const childEnv = { ...process.env };
465
- // 合同评测会创建隔离工作区;继承宿主 DTA_ROOT 会让 Action Gate 错读外层 control store。
466
- delete childEnv.DTA_ROOT;
467
- const child = spawnSync(process.execPath, [script, ...argv.slice(2)], {
468
- cwd: ROOT,
469
- env: childEnv,
470
- stdio: 'inherit',
471
- });
472
- if (child.error)
473
- throw child.error;
474
- if (child.signal)
475
- throw new Error(`评测进程被 ${child.signal} 终止`);
476
- process.exitCode = child.status ?? 2;
477
- return;
478
- }
479
- if (cmd === 'smoke') {
480
- const script = join(PACKAGE_ROOT, 'evals', 'run-contract-evals.mjs');
481
- const childEnv = { ...process.env };
482
- delete childEnv.DTA_ROOT;
483
- const child = spawnSync(process.execPath, [script, '--smoke'], {
484
- cwd: ROOT, env: childEnv, stdio: 'inherit',
485
- });
486
- if (child.error)
487
- throw child.error;
488
- if (child.signal)
489
- throw new Error(`Smoke 被 ${child.signal} 终止`);
490
- process.exitCode = child.status ?? 2;
491
- return;
492
- }
493
- // ── kb ──
494
- if (cmd === 'kb') {
495
- const cfg = cfgmod.must(ROOT);
496
- const { values: o } = parseArgs({
497
- args: argv.slice(2), allowPositionals: true,
498
- options: {
499
- name: { type: 'string' }, from: { type: 'string' }, own: { type: 'string' },
500
- sync: { type: 'string' }, local: { type: 'string' }, force: { type: 'boolean' },
501
- },
502
- });
503
- if (sub === 'mount') {
504
- if (!o.name || !o.from)
505
- fail('要 --name 和 --from。源的写法见 --help');
506
- kbmod.parseSource(o.from); // 写错了当场炸
507
- const own = o.own || kbmod.EXTERNAL;
508
- const sync = o.sync || (own === kbmod.OWNED ? kbmod.PUSH : kbmod.PULL);
509
- if (own === kbmod.OWNED && sync === kbmod.PULL) {
510
- fail('owned 的真值在 Git,不该 pull —— 那会把远端的东西覆盖到本地。\n' +
511
- ' **所有权决定真值方向。** owned → push;external → pull。');
512
- }
513
- const local = o.local || join('.cache/kb', o.name);
514
- cfg.kb = (cfg.kb || []).filter((k) => k.name !== o.name);
515
- cfg.kb.push({ name: o.name, from: o.from, own, sync, local });
516
- cfgmod.save(ROOT, cfg);
517
- console.log(`✅ 挂上「${o.name}」 ${o.from}`);
518
- console.log(` 所有权=${own} · 同步=${sync} · 落在 ${local}`);
519
- console.log(own === kbmod.OWNED
520
- ? ' (真值在 Git,钉钉是暴露层。人在钉钉里改了 → sync 时会【停下来问】,不盲覆盖。)'
521
- : ' (真值在钉钉,本地只是【可丢弃的缓存】。过期了 kb search 会警告。)');
522
- return;
523
- }
524
- if (sub === 'list') {
525
- if (!cfg.kb?.length)
526
- return console.log('一个都没挂。 kb mount --help');
527
- for (const kb of cfg.kb) {
528
- const s = kbmod.stale(kb);
529
- console.log(` ${s ? '⚠️' : '·'} ${kb.name.padEnd(14)} ${kb.own.padEnd(9)} ${kb.sync.padEnd(5)} ${kb.from}`);
530
- console.log(` 落在 ${kb.local} 上次同步 ${kb._syncedAt || '从未'}${s ? ' ← **过期了,不许拿来回答**' : ''}`);
531
- }
532
- return;
533
- }
534
- if (sub === 'sync') {
535
- const targets = (cfg.kb || []).filter((k) => !o.name || k.name === o.name);
536
- if (!targets.length)
537
- fail('没有这个知识库。 kb list');
538
- for (const kb of targets) {
539
- if (kb.sync === kbmod.NONE) {
540
- console.log(` skip ${kb.name}(sync=none)`);
541
- continue;
542
- }
543
- if (kb.sync === kbmod.PULL)
544
- kbmod.pull(kb, ROOT, o.force);
545
- else
546
- kbmod.push(kb, ROOT, o.force);
547
- }
548
- cfgmod.save(ROOT, cfg);
549
- return;
550
- }
551
- if (sub === 'search') {
552
- const kw = argv[2];
553
- if (!kw)
554
- fail('要给关键词。 kb search "<关键词>"');
555
- kbmod.search(cfg, ROOT, kw);
556
- return;
557
- }
558
- fail('kb 的子命令: mount / list / sync / search');
559
- }
560
- // ── boot ──
561
- if (cmd === 'boot') {
562
- boot(cfgmod.must(ROOT), ROOT);
563
- return;
564
- }
565
- // ── duty ──
566
- if (cmd === 'duty') {
567
- const cfg = cfgmod.must(ROOT);
568
- const { values: o } = parseArgs({
569
- args: argv.slice(1), allowPositionals: true,
570
- options: { check: { type: 'boolean' }, run: { type: 'string' },
571
- list: { type: 'boolean' }, force: { type: 'boolean' } },
572
- });
573
- const duties = dutymod.loadDuties(cfg, ROOT);
574
- if (o.list) {
575
- const ks = Object.keys(duties);
576
- console.log(ks.length ? ks.join('\n')
577
- : '(值班表是空的 —— 母体出厂如此,这是【正确】的。在 .dingtalk-agent/config.json 的 duties 里加。)');
578
- return;
579
- }
580
- if (o.run) {
581
- const fn = duties[o.run];
582
- if (!fn)
583
- fail(`没有这个值班: ${o.run}(有: ${Object.keys(duties).join('、') || '无'})`);
584
- const s = fn();
585
- if (s.kind === '交付' && !dutymod.inDeliver() && !o.force) {
586
- return console.log(`⏸ 「${o.run}」是外发动作,静默时段顺延,**不打扰第三方**。`);
587
- }
588
- if (s.done)
589
- return console.log(`✅ 「${o.run}」已完成,本拍无需执行。${s.detail}`);
590
- console.log(`【${o.run}】该做了。${s.gap}`);
591
- console.log(` 剧本: ${s.playbook || '(没写剧本 —— 这个值班不合格)'}`);
592
- console.log(` ⚠️ 送达【回读确认】之后才准记 pass。没送达就写 pass = 自己骗自己。`);
593
- return;
594
- }
595
- // --check(默认)
596
- console.log('值班表(**完成状态在数据里,不在记忆里**;幂等,漏拍下一拍自愈)');
597
- console.log(` 现在 ${runsmod.now().slice(11, 16)} · ${dutymod.inNight() ? '夜间窗口(重活可跑)' : '白天(重活挂起)'} · 外发窗口${dutymod.inDeliver() ? '开' : '关'}`);
598
- const ids = Object.keys(duties);
599
- if (!ids.length) {
600
- console.log('\n (值班表是空的 —— 母体出厂如此。)');
601
- return;
602
- }
603
- const todo = [];
604
- for (const id of ids) {
605
- const s = duties[id]();
606
- const mark = s.done ? '✅ 已完成' : (s.due ? '⬜ 待补齐' : '⏸ 不在窗口');
607
- console.log(` ${s.id.padEnd(8)} [${s.kind}] ${mark} ${s.detail}${s.gap ? ' → ' + s.gap : ''}`);
608
- if (!s.done && s.due)
609
- todo.push(s.id);
610
- }
611
- if (todo.length) {
612
- console.log(`\n本拍要做: ${todo.map((t) => 'duty --run ' + t).join('、')}`);
613
- process.exit(1);
614
- }
615
- console.log('\n全部补齐,静默即可(**不要为此打扰主人**)');
616
- return;
617
- }
618
- // ── 留痕与进化 ──
619
- if (cmd === 'log' || cmd === 'logq') {
620
- const { values: o } = parseArgs({
621
- args: argv.slice(1),
622
- options: { did: { type: 'string' }, asked: { type: 'string' }, kind: { type: 'string' },
623
- secs: { type: 'string' }, r: { type: 'string' }, ev: { type: 'string' },
624
- ch: { type: 'string' }, issue: { type: 'string' },
625
- conv: { type: 'string' }, msg: { type: 'string' } },
626
- });
627
- if (!o.did)
628
- fail('要 --did(我怎么答的)');
629
- const t = runsmod.logq(ROOT, {
630
- ...o, did: o.did, secs: +(o.secs || 0), r: o.r || 'pass',
631
- });
632
- console.log(`运行记录已落: runs/${runsmod.today()}.md @${t}`);
633
- if (!o.conv)
634
- console.log(' ⚠️ 没带 --conv —— 事后【拉不出对话流】,这一条基本没有训练价值。');
635
- return;
636
- }
637
- if (cmd === 'feedback') {
638
- const { values: o } = parseArgs({
639
- args: argv.slice(1),
640
- options: { kind: { type: 'string' }, text: { type: 'string' },
641
- conv: { type: 'string' }, at: { type: 'string' } },
642
- });
643
- if (!o.kind || ![runsmod.FB_OK, runsmod.FB_MORE, runsmod.FB_FIX].includes(o.kind)) {
644
- fail('--kind 只能是 认可 / 追问 / 纠正');
645
- }
646
- const day = runsmod.today();
647
- const rows = runsmod.read(ROOT, day);
648
- let cand = rows.filter((r) => r.asked);
649
- if (o.conv)
650
- cand = cand.filter((r) => r.conv === o.conv);
651
- if (!cand.length)
652
- fail(`今天没有可回填的问答行 —— **是不是答完忘了 logq?**`);
653
- const tgt = o.at ? cand.find((r) => r.t === o.at) : cand[cand.length - 1];
654
- if (!tgt)
655
- fail(`找不到 t=${o.at} 的行`);
656
- tgt.fb = o.kind;
657
- if (o.text)
658
- tgt.fbt = o.text.slice(0, 600);
659
- runsmod.write(ROOT, day, rows);
660
- console.log(`已回填: ${day} @${tgt.t} → 「${o.kind}」(原问: ${(tgt.asked || '').slice(0, 40)})`);
661
- return;
662
- }
663
- if (cmd === 'runs') {
664
- const { values: o } = parseArgs({
665
- args: argv.slice(1),
666
- options: { days: { type: 'string' }, conv: { type: 'string' },
667
- issue: { type: 'string' }, top: { type: 'string' } },
668
- });
669
- let rows = runsmod.scan(ROOT, +(o.days || 1));
670
- if (o.conv) {
671
- rows = rows.filter((r) => r.conv === o.conv);
672
- console.log(`【对话流】会话 ${o.conv} · ${rows.length} 条 —— **这就是训练素材**`);
673
- for (const r of rows) {
674
- console.log(` ── ${r._day} ${r.t} issue=${r.issue || '-'} msg=${r.msg || '-'}`);
675
- if (r.asked)
676
- console.log(` 主人: ${r.asked}`);
677
- console.log(` 我 : ${r.did} [${r.r}]`);
678
- if (r.fb)
679
- console.log(` 反馈: ${r.fb}${r.fbt ? ` 「${r.fbt}」` : ''}`);
680
- }
681
- if (!rows.length)
682
- console.log(' (空 —— 这个会话没留痕。**没留痕 = 这些交互等于没发生过。**)');
683
- return;
684
- }
685
- if (o.issue) {
686
- const issue = o.issue;
687
- rows = rows.filter((r) => (r.issue || '').startsWith(issue));
688
- }
689
- console.log(`运行记录 runs/ · ${rows.length} 条`);
690
- for (const r of rows.slice(-(+(o.top || 30)))) {
691
- console.log(` ${r._day} ${r.t} [${(r.r || '').padEnd(11)}] ${(r.ch || '').padEnd(6)} ${(r.did || '').slice(0, 30)} ${r.fb ? '← ' + r.fb : ''}`);
692
- }
693
- return;
694
- }
695
- if (cmd === 'evolve') {
696
- const { values: o } = parseArgs({ args: argv.slice(1), options: { days: { type: 'string' } } });
697
- const rows = runsmod.scan(ROOT, +(o.days || 7));
698
- const ans = rows.filter((r) => r.asked);
699
- const bad = ans.filter((r) => [runsmod.FB_FIX, runsmod.FB_MORE].includes(r.fb || ''));
700
- const unk = ans.filter((r) => !r.fb);
701
- const failed = rows.filter((r) => ['fail', 'unsupported', 'degraded'].includes(r.r));
702
- console.log(`【进化清单】近 ${o.days || 7} 天`);
703
- console.log(` 面向主人的回答 ${ans.length} 条 · 被【打回】${bad.length} 条 · 引擎层没答出来 ${failed.length} 条`);
704
- if (unk.length)
705
- console.log(` ⚠️ ${unk.length} 条【没收到反馈就结案了】—— 答完没回填,这次交互等于白跑。`);
706
- if (bad.length) {
707
- console.log('\n── 主人打回的(**直接就是这周该改什么**)──');
708
- for (const r of bad) {
709
- console.log(` ${r._day} ${r.t} [${r.fb}] 会话=${r.conv || '-'}`);
710
- console.log(` 主人问: ${(r.asked || '').slice(0, 60)}`);
711
- console.log(` 我答 : ${(r.did || '').slice(0, 60)}`);
712
- console.log(` 他说 : ${r.fbt || '(没记下来)'}`);
713
- console.log(` 复盘 : dingtalk-agent runs --conv ${r.conv || '<会话id>'}`);
714
- }
715
- }
716
- if (!bad.length && !failed.length) {
717
- console.log('\n (没有打回,也没有失败 —— 要么真的都对,要么【没人回填反馈】。看上面那行。)');
718
- }
719
- return;
720
- }
721
- // ── todo:把待办拉全(心跳收件用)──
722
- if (cmd === 'todo') {
723
- const dws = await import('../src/dws.js');
724
- const all = dws.todosAll('false');
725
- console.log(`未完成待办 ${all.length} 条(**已翻页拉全** —— 只看第一页会漏掉循环件)`);
726
- for (const t of all)
727
- console.log(` ${t.taskId} ${(t.subject || '').slice(0, 60)}`);
728
- return;
729
- }
730
413
  fail(`不认识的命令: ${cmd}`, 1, `运行 dta --help 查看全部命令${suggest(cmd, [
731
- 'setup', 'upgrade', 'doctor', 'skill', 'bootstrap', 'init', 'prepare', 'act', 'smoke', 'eval',
414
+ 'setup', 'upgrade', 'doctor', 'skill', 'bootstrap', 'init', 'prepare', 'run', 'dispatch', 'act',
732
415
  ])}`);
733
416
  }
734
417
  function errorHint(message) {