@xdxer/dingtalk-agent 0.1.1 → 0.1.4-beta.0

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 (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -76
  3. package/dist/bin/dingtalk-agent.js +763 -0
  4. package/dist/bin/dingtalk-agent.js.map +1 -0
  5. package/dist/src/actions.js +562 -0
  6. package/dist/src/actions.js.map +1 -0
  7. package/dist/src/boot.js +70 -0
  8. package/dist/src/boot.js.map +1 -0
  9. package/dist/src/bootstrap.js +144 -0
  10. package/dist/src/bootstrap.js.map +1 -0
  11. package/dist/src/config.js +86 -0
  12. package/dist/src/config.js.map +1 -0
  13. package/dist/src/doctor.js +166 -0
  14. package/dist/src/doctor.js.map +1 -0
  15. package/dist/src/driver.js +45 -0
  16. package/dist/src/driver.js.map +1 -0
  17. package/{src → dist/src}/duty.js +42 -44
  18. package/dist/src/duty.js.map +1 -0
  19. package/dist/src/dws.js +270 -0
  20. package/dist/src/dws.js.map +1 -0
  21. package/dist/src/events.js +233 -0
  22. package/dist/src/events.js.map +1 -0
  23. package/dist/src/fields.js +132 -0
  24. package/dist/src/fields.js.map +1 -0
  25. package/dist/src/init.js +41 -0
  26. package/dist/src/init.js.map +1 -0
  27. package/dist/src/kb.js +240 -0
  28. package/dist/src/kb.js.map +1 -0
  29. package/dist/src/package-root.js +17 -0
  30. package/dist/src/package-root.js.map +1 -0
  31. package/dist/src/runs.js +79 -0
  32. package/dist/src/runs.js.map +1 -0
  33. package/dist/src/sessions.js +668 -0
  34. package/dist/src/sessions.js.map +1 -0
  35. package/dist/src/setup.js +101 -0
  36. package/dist/src/setup.js.map +1 -0
  37. package/dist/src/skill-manager.js +288 -0
  38. package/dist/src/skill-manager.js.map +1 -0
  39. package/dist/src/skills.js +200 -0
  40. package/dist/src/skills.js.map +1 -0
  41. package/dist/src/types.js +2 -0
  42. package/dist/src/types.js.map +1 -0
  43. package/dist/src/waits.js +945 -0
  44. package/dist/src/waits.js.map +1 -0
  45. package/dist/src/workspace.js +173 -0
  46. package/dist/src/workspace.js.map +1 -0
  47. package/docs/ARCHITECTURE.md +217 -0
  48. package/docs/INSTALLATION.md +135 -0
  49. package/docs/MINIMAL-WORKSPACE-V1.md +172 -0
  50. package/docs/OPEN-SOURCE-REFERENCES.md +107 -0
  51. package/docs/SELF-TEST.md +252 -0
  52. package/docs/architecture/dingtalk-agent-blueprint.png +0 -0
  53. package/docs/architecture/dingtalk-agent-blueprint.svg +144 -0
  54. package/docs/architecture/durable-async-agent-runtime.png +0 -0
  55. package/docs/architecture/durable-async-agent-runtime.svg +234 -0
  56. package/docs//345/206/205/347/275/221/345/256/236/347/233/270.md +77 -0
  57. package/evals/baselines/2026-07-14/behavior-summary.json +28 -0
  58. package/evals/baselines/2026-07-14/contract-summary.json +18 -0
  59. package/evals/baselines/2026-07-14/live-canary-summary.json +25 -0
  60. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/SKILL.md +72 -0
  61. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/action-contract.md +31 -0
  62. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/event-to-behavior.md +22 -0
  63. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/memory-and-evolution.md +25 -0
  64. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/runtime-modes.md +34 -0
  65. package/evals/baselines/2026-07-15/task-lifecycle-summary.json +50 -0
  66. package/evals/evals.json +316 -0
  67. package/evals/fixtures/dm-ambiguous-send.json +4 -0
  68. package/evals/fixtures/dm-blocked.json +4 -0
  69. package/evals/fixtures/dm-clear.json +4 -0
  70. package/evals/fixtures/dm-discussion.json +4 -0
  71. package/evals/fixtures/dm-doc-write-no-tool.json +4 -0
  72. package/evals/fixtures/dm-long-task-ack.json +4 -0
  73. package/evals/fixtures/dm-nonblocking-gap.json +4 -0
  74. package/evals/fixtures/dm-structured-task.json +4 -0
  75. package/evals/fixtures/group.json +10 -0
  76. package/evals/fixtures/mentioned.json +3 -0
  77. package/evals/run-contract-evals.mjs +1106 -0
  78. package/evals/run-shadow-evals.mjs +267 -0
  79. package/evals/runners/README.md +66 -0
  80. package/evals/runners/claude-shadow.mjs +533 -0
  81. package/evals/schemas/action-request.schema.json +77 -0
  82. package/evals/shadow-evals.json +133 -0
  83. package/package.json +28 -6
  84. package/skills/AGENTS.md +21 -3
  85. package/skills/dingtalk-basic-behavior/SKILL.md +86 -0
  86. package/skills/dingtalk-basic-behavior/assets/task-checkpoint.md +37 -0
  87. package/skills/dingtalk-basic-behavior/references/action-contract.md +31 -0
  88. package/skills/dingtalk-basic-behavior/references/event-to-behavior.md +24 -0
  89. package/skills/dingtalk-basic-behavior/references/memory-and-evolution.md +27 -0
  90. package/skills/dingtalk-basic-behavior/references/runtime-modes.md +34 -0
  91. package/skills/dingtalk-basic-behavior/references/task-lifecycle.md +108 -0
  92. package/skills//345/237/272/347/241/200/350/241/214/344/270/272.md +44 -0
  93. package/skills//345/277/203/350/267/263.md +11 -0
  94. package/skills//346/266/210/346/201/257.md +14 -14
  95. package/skills//350/257/204/346/265/213.md +14 -1
  96. package/skills//351/222/211/351/222/211.md +3 -2
  97. package/templates/behaviors/basic.json +68 -0
  98. package/templates/fields/default/field.json +25 -0
  99. package/bin/dingtalk-agent.js +0 -289
  100. package/src/boot.js +0 -65
  101. package/src/config.js +0 -42
  102. package/src/dws.js +0 -192
  103. package/src/init.js +0 -84
  104. package/src/kb.js +0 -221
  105. package/src/runs.js +0 -77
@@ -0,0 +1,166 @@
1
+ import { accessSync, constants, existsSync, readFileSync } from 'node:fs';
2
+ import { delimiter, dirname, join, resolve } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { skillStatus } from './skill-manager.js';
6
+ export function doctor(packageRoot, options = {}) {
7
+ const home = options.home || homedir();
8
+ const env = options.env || process.env;
9
+ const checks = [];
10
+ const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'));
11
+ const version = String(pkg.version || '');
12
+ const nodeVersion = process.versions.node;
13
+ checks.push(versionAtLeast(nodeVersion, '18.3.0')
14
+ ? { id: 'node', level: 'pass', summary: `Node.js ${nodeVersion}` }
15
+ : {
16
+ id: 'node', level: 'fail', summary: `Node.js ${nodeVersion} 过旧`,
17
+ fix: '升级到 Node.js 18.3 或更高版本',
18
+ });
19
+ const cli = findExecutable('dingtalk-agent', env);
20
+ const dta = findExecutable('dta', env);
21
+ const visible = cli || dta;
22
+ if (visible) {
23
+ const installed = runText(visible, ['--version'], env);
24
+ const installedVersion = installed.ok ? firstLine(installed.stdout) : '';
25
+ checks.push(installedVersion === version
26
+ ? { id: 'cli', level: 'pass', summary: `CLI ${installedVersion} 已在 PATH 中`, detail: visible }
27
+ : {
28
+ id: 'cli', level: 'warn', summary: 'PATH 中的 CLI 版本与当前包不一致',
29
+ detail: `${visible} (${installedVersion || '无法识别版本'})`,
30
+ fix: `运行 npx --yes @xdxer/dingtalk-agent@${version} setup`,
31
+ });
32
+ }
33
+ else {
34
+ checks.push({
35
+ id: 'cli', level: 'fail', summary: 'PATH 找不到 dingtalk-agent',
36
+ detail: `当前 PATH=${env.PATH || ''}`,
37
+ fix: `运行 npx --yes @xdxer/dingtalk-agent@${version} setup`,
38
+ });
39
+ }
40
+ const dws = findExecutable('dws', env);
41
+ if (!dws) {
42
+ checks.push({
43
+ id: 'dws', level: 'fail', summary: 'PATH 找不到 dws',
44
+ fix: '安装 DWS,并确认 `dws version --format json` 可以执行',
45
+ });
46
+ checks.push({
47
+ id: 'dws-auth', level: 'fail', summary: '无法检查 DWS 认证',
48
+ fix: 'DWS 可用后运行 `dws auth login`',
49
+ });
50
+ }
51
+ else {
52
+ const versionRun = runJson(dws, ['version', '--format', 'json'], env);
53
+ const dwsVersion = versionRun.json?.version || firstLine(versionRun.stdout);
54
+ const supported = versionRun.ok && versionAtLeast(String(dwsVersion || '').replace(/^v/, ''), '1.0.15');
55
+ checks.push(supported
56
+ ? { id: 'dws', level: 'pass', summary: `DWS ${dwsVersion || '可执行'}`, detail: dws }
57
+ : {
58
+ id: 'dws', level: 'fail',
59
+ summary: versionRun.ok ? `DWS ${dwsVersion || ''} 低于最低版本 1.0.15` : 'DWS 存在但无法运行',
60
+ detail: versionRun.ok ? dws : versionRun.error,
61
+ fix: versionRun.ok ? '运行 `dws upgrade`' : '运行 `dws doctor` 检查安装',
62
+ });
63
+ const authRun = runJson(dws, ['auth', 'status', '--format', 'json'], env);
64
+ const authenticated = authRun.ok && authRun.json?.authenticated === true &&
65
+ authRun.json?.token_valid !== false;
66
+ checks.push(authenticated
67
+ ? {
68
+ id: 'dws-auth', level: 'pass', summary: 'DWS 已认证',
69
+ detail: [authRun.json?.corp_name, authRun.json?.user_name].filter(Boolean).join(' · '),
70
+ }
71
+ : {
72
+ id: 'dws-auth', level: 'fail', summary: 'DWS 尚未认证或凭据已失效',
73
+ detail: authRun.error || 'authenticated=false', fix: '运行 `dws auth login`',
74
+ });
75
+ }
76
+ const skill = skillStatus(packageRoot, { home });
77
+ checks.push(skill.canonical.current
78
+ ? {
79
+ id: 'skill', level: 'pass',
80
+ summary: `Basic Behavior Skill ${skill.bundled.skillVersion} 已安装`,
81
+ detail: skill.canonical.path,
82
+ }
83
+ : {
84
+ id: 'skill', level: skill.canonical.exists ? 'warn' : 'fail',
85
+ summary: skill.canonical.exists ? 'Basic Behavior Skill 需要升级或存在本地修改' : 'Basic Behavior Skill 未安装',
86
+ detail: skill.canonical.path,
87
+ fix: skill.canonical.exists
88
+ ? '运行 `dingtalk-agent skill status`,确认后执行 `dingtalk-agent skill upgrade`'
89
+ : '运行 `dingtalk-agent skill install`',
90
+ });
91
+ for (const client of skill.clients) {
92
+ checks.push({
93
+ id: client.name,
94
+ level: client.state === 'ok' ? 'pass' : client.state === 'conflict' ? 'fail' : 'warn',
95
+ summary: `${client.label}: ${client.state === 'ok' ? '可发现 Skill' : client.state === 'conflict' ? 'Skill 路径冲突' : '尚不可发现 Skill'}`,
96
+ detail: `${client.discovery} · ${client.path}`,
97
+ fix: client.state === 'ok' ? undefined : '运行 `dingtalk-agent skill install`',
98
+ });
99
+ }
100
+ const nextSteps = checks
101
+ .filter((check) => check.level !== 'pass' && check.fix)
102
+ .map((check) => check.fix)
103
+ .filter((step, index, all) => all.indexOf(step) === index);
104
+ return {
105
+ $schema: 'dingtalk-agent/doctor@1',
106
+ ready: !checks.some((check) => check.level === 'fail'),
107
+ checks,
108
+ nextSteps,
109
+ skill,
110
+ };
111
+ }
112
+ export function findExecutable(command, env = process.env) {
113
+ const path = env.PATH || '';
114
+ const extensions = process.platform === 'win32'
115
+ ? (env.PATHEXT || '.EXE;.CMD;.BAT').split(';')
116
+ : [''];
117
+ for (const directory of path.split(delimiter).filter(Boolean)) {
118
+ for (const extension of extensions) {
119
+ const candidate = resolve(directory, command + extension.toLowerCase());
120
+ try {
121
+ accessSync(candidate, process.platform === 'win32' ? constants.F_OK : constants.X_OK);
122
+ return candidate;
123
+ }
124
+ catch { /* continue */ }
125
+ }
126
+ }
127
+ return '';
128
+ }
129
+ function runJson(command, args, env) {
130
+ const run = spawnSync(command, args, { encoding: 'utf8', env, timeout: 8_000 });
131
+ const stdout = String(run.stdout || '').trim();
132
+ const stderr = String(run.stderr || '').trim();
133
+ let json = null;
134
+ try {
135
+ json = stdout ? JSON.parse(stdout) : null;
136
+ }
137
+ catch { /* human output is allowed */ }
138
+ return {
139
+ ok: run.status === 0 && !run.error,
140
+ json,
141
+ stdout,
142
+ error: run.error?.message || stderr || (run.signal ? `被 ${run.signal} 终止` : `退出码 ${run.status}`),
143
+ };
144
+ }
145
+ function runText(command, args, env) {
146
+ const run = spawnSync(command, args, { encoding: 'utf8', env, timeout: 8_000 });
147
+ return {
148
+ ok: run.status === 0 && !run.error,
149
+ stdout: String(run.stdout || '').trim(),
150
+ };
151
+ }
152
+ function firstLine(text) {
153
+ return text.split(/\r?\n/, 1)[0]?.trim() || '';
154
+ }
155
+ function versionAtLeast(actual, minimum) {
156
+ const a = actual.split('.').map((part) => Number.parseInt(part, 10) || 0);
157
+ const b = minimum.split('.').map((part) => Number.parseInt(part, 10) || 0);
158
+ for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
159
+ if ((a[i] || 0) > (b[i] || 0))
160
+ return true;
161
+ if ((a[i] || 0) < (b[i] || 0))
162
+ return false;
163
+ }
164
+ return true;
165
+ }
166
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/doctor.ts"],"names":[],"mappings":"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","sourcesContent":["import { accessSync, constants, existsSync, readFileSync } from 'node:fs'\nimport { delimiter, dirname, join, resolve } from 'node:path'\nimport { homedir } from 'node:os'\nimport { spawnSync } from 'node:child_process'\n\nimport { skillStatus, type GlobalSkillStatus } from './skill-manager.js'\n\nexport type CheckLevel = 'pass' | 'warn' | 'fail'\n\nexport interface DoctorCheck {\n id: 'node' | 'cli' | 'dws' | 'dws-auth' | 'skill' | 'claude-code' | 'codex' | 'opencode'\n level: CheckLevel\n summary: string\n detail?: string\n fix?: string\n}\n\nexport interface DoctorReport {\n $schema: 'dingtalk-agent/doctor@1'\n ready: boolean\n checks: DoctorCheck[]\n nextSteps: string[]\n skill: GlobalSkillStatus\n}\n\nexport interface DoctorOptions {\n home?: string\n env?: NodeJS.ProcessEnv\n}\n\nexport function doctor(packageRoot: string, options: DoctorOptions = {}): DoctorReport {\n const home = options.home || homedir()\n const env = options.env || process.env\n const checks: DoctorCheck[] = []\n const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))\n const version = String(pkg.version || '')\n\n const nodeVersion = process.versions.node\n checks.push(versionAtLeast(nodeVersion, '18.3.0')\n ? { id: 'node', level: 'pass', summary: `Node.js ${nodeVersion}` }\n : {\n id: 'node', level: 'fail', summary: `Node.js ${nodeVersion} 过旧`,\n fix: '升级到 Node.js 18.3 或更高版本',\n })\n\n const cli = findExecutable('dingtalk-agent', env)\n const dta = findExecutable('dta', env)\n const visible = cli || dta\n if (visible) {\n const installed = runText(visible, ['--version'], env)\n const installedVersion = installed.ok ? firstLine(installed.stdout) : ''\n checks.push(installedVersion === version\n ? { id: 'cli', level: 'pass', summary: `CLI ${installedVersion} 已在 PATH 中`, detail: visible }\n : {\n id: 'cli', level: 'warn', summary: 'PATH 中的 CLI 版本与当前包不一致',\n detail: `${visible} (${installedVersion || '无法识别版本'})`,\n fix: `运行 npx --yes @xdxer/dingtalk-agent@${version} setup`,\n })\n } else {\n checks.push({\n id: 'cli', level: 'fail', summary: 'PATH 找不到 dingtalk-agent',\n detail: `当前 PATH=${env.PATH || ''}`,\n fix: `运行 npx --yes @xdxer/dingtalk-agent@${version} setup`,\n })\n }\n\n const dws = findExecutable('dws', env)\n if (!dws) {\n checks.push({\n id: 'dws', level: 'fail', summary: 'PATH 找不到 dws',\n fix: '安装 DWS,并确认 `dws version --format json` 可以执行',\n })\n checks.push({\n id: 'dws-auth', level: 'fail', summary: '无法检查 DWS 认证',\n fix: 'DWS 可用后运行 `dws auth login`',\n })\n } else {\n const versionRun = runJson(dws, ['version', '--format', 'json'], env)\n const dwsVersion = versionRun.json?.version || firstLine(versionRun.stdout)\n const supported = versionRun.ok && versionAtLeast(String(dwsVersion || '').replace(/^v/, ''), '1.0.15')\n checks.push(supported\n ? { id: 'dws', level: 'pass', summary: `DWS ${dwsVersion || '可执行'}`, detail: dws }\n : {\n id: 'dws', level: 'fail',\n summary: versionRun.ok ? `DWS ${dwsVersion || ''} 低于最低版本 1.0.15` : 'DWS 存在但无法运行',\n detail: versionRun.ok ? dws : versionRun.error,\n fix: versionRun.ok ? '运行 `dws upgrade`' : '运行 `dws doctor` 检查安装',\n })\n\n const authRun = runJson(dws, ['auth', 'status', '--format', 'json'], env)\n const authenticated = authRun.ok && authRun.json?.authenticated === true &&\n authRun.json?.token_valid !== false\n checks.push(authenticated\n ? {\n id: 'dws-auth', level: 'pass', summary: 'DWS 已认证',\n detail: [authRun.json?.corp_name, authRun.json?.user_name].filter(Boolean).join(' · '),\n }\n : {\n id: 'dws-auth', level: 'fail', summary: 'DWS 尚未认证或凭据已失效',\n detail: authRun.error || 'authenticated=false', fix: '运行 `dws auth login`',\n })\n }\n\n const skill = skillStatus(packageRoot, { home })\n checks.push(skill.canonical.current\n ? {\n id: 'skill', level: 'pass',\n summary: `Basic Behavior Skill ${skill.bundled.skillVersion} 已安装`,\n detail: skill.canonical.path,\n }\n : {\n id: 'skill', level: skill.canonical.exists ? 'warn' : 'fail',\n summary: skill.canonical.exists ? 'Basic Behavior Skill 需要升级或存在本地修改' : 'Basic Behavior Skill 未安装',\n detail: skill.canonical.path,\n fix: skill.canonical.exists\n ? '运行 `dingtalk-agent skill status`,确认后执行 `dingtalk-agent skill upgrade`'\n : '运行 `dingtalk-agent skill install`',\n })\n\n for (const client of skill.clients) {\n checks.push({\n id: client.name,\n level: client.state === 'ok' ? 'pass' : client.state === 'conflict' ? 'fail' : 'warn',\n summary: `${client.label}: ${client.state === 'ok' ? '可发现 Skill' : client.state === 'conflict' ? 'Skill 路径冲突' : '尚不可发现 Skill'}`,\n detail: `${client.discovery} · ${client.path}`,\n fix: client.state === 'ok' ? undefined : '运行 `dingtalk-agent skill install`',\n })\n }\n\n const nextSteps = checks\n .filter((check) => check.level !== 'pass' && check.fix)\n .map((check) => check.fix as string)\n .filter((step, index, all) => all.indexOf(step) === index)\n return {\n $schema: 'dingtalk-agent/doctor@1',\n ready: !checks.some((check) => check.level === 'fail'),\n checks,\n nextSteps,\n skill,\n }\n}\n\nexport function findExecutable(command: string, env: NodeJS.ProcessEnv = process.env): string {\n const path = env.PATH || ''\n const extensions = process.platform === 'win32'\n ? (env.PATHEXT || '.EXE;.CMD;.BAT').split(';')\n : ['']\n for (const directory of path.split(delimiter).filter(Boolean)) {\n for (const extension of extensions) {\n const candidate = resolve(directory, command + extension.toLowerCase())\n try {\n accessSync(candidate, process.platform === 'win32' ? constants.F_OK : constants.X_OK)\n return candidate\n } catch { /* continue */ }\n }\n }\n return ''\n}\n\nfunction runJson(command: string, args: string[], env: NodeJS.ProcessEnv) {\n const run = spawnSync(command, args, { encoding: 'utf8', env, timeout: 8_000 })\n const stdout = String(run.stdout || '').trim()\n const stderr = String(run.stderr || '').trim()\n let json: any = null\n try { json = stdout ? JSON.parse(stdout) : null } catch { /* human output is allowed */ }\n return {\n ok: run.status === 0 && !run.error,\n json,\n stdout,\n error: run.error?.message || stderr || (run.signal ? `被 ${run.signal} 终止` : `退出码 ${run.status}`),\n }\n}\n\nfunction runText(command: string, args: string[], env: NodeJS.ProcessEnv) {\n const run = spawnSync(command, args, { encoding: 'utf8', env, timeout: 8_000 })\n return {\n ok: run.status === 0 && !run.error,\n stdout: String(run.stdout || '').trim(),\n }\n}\n\nfunction firstLine(text: string): string {\n return text.split(/\\r?\\n/, 1)[0]?.trim() || ''\n}\n\nfunction versionAtLeast(actual: string, minimum: string): boolean {\n const a = actual.split('.').map((part) => Number.parseInt(part, 10) || 0)\n const b = minimum.split('.').map((part) => Number.parseInt(part, 10) || 0)\n for (let i = 0; i < Math.max(a.length, b.length); i += 1) {\n if ((a[i] || 0) > (b[i] || 0)) return true\n if ((a[i] || 0) < (b[i] || 0)) return false\n }\n return true\n}\n"]}
@@ -0,0 +1,45 @@
1
+ // 静态 CLI 的事件驱动入口(TypeScript source)。它不内置模型,只把每个事件变成可交给沙箱/Agent 的 dispatch。
2
+ import { readFileSync } from 'node:fs';
3
+ import { createInterface } from 'node:readline';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { prepareSession } from './sessions.js';
6
+ import { parseEventInput } from './events.js';
7
+ export function readEventFile(path) {
8
+ const text = path === '-' ? readFileSync(0, 'utf8') : readFileSync(path, 'utf8');
9
+ return parseEventInput(text);
10
+ }
11
+ export async function consumeNdjson(root, cfg, input = process.stdin, options = {}) {
12
+ const rl = createInterface({ input, crlfDelay: Infinity });
13
+ let accepted = 0, rejected = 0;
14
+ for await (const line of rl) {
15
+ if (!line.trim())
16
+ continue;
17
+ try {
18
+ const event = parseEventInput(line);
19
+ const out = prepareSession(root, cfg, event, options);
20
+ process.stdout.write(JSON.stringify(out) + '\n');
21
+ accepted++;
22
+ }
23
+ catch (e) {
24
+ process.stdout.write(JSON.stringify({
25
+ type: 'dingtalk-agent.run.rejected',
26
+ error: String(e.message || e),
27
+ }) + '\n');
28
+ rejected++;
29
+ }
30
+ }
31
+ return { accepted, rejected };
32
+ }
33
+ export function heartbeatEvent(key = '', eventId = '') {
34
+ const now = new Date();
35
+ const correlationKey = key || 'heartbeat:default';
36
+ return {
37
+ type: 'agent.heartbeat',
38
+ // correlation 稳定,表示同一项 duty;event_id 每拍唯一,表示一次新的信号。
39
+ // 外部调度器重试同一拍时应复用 --heartbeat-event-id,才能端到端幂等。
40
+ event_id: eventId || `heartbeat:${now.toISOString()}:${randomUUID()}`,
41
+ timestamp: now.getTime(),
42
+ correlation_id: correlationKey,
43
+ };
44
+ }
45
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/driver.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,MAAM,UAAU,aAAa,CAAC,IAAI;IAChC,MAAM,IAAI,GAAG,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAChF,OAAO,eAAe,CAAC,IAAI,CAAC,CAAA;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAI,EACJ,GAAG,EACH,QAA+B,OAAO,CAAC,KAAK,EAC5C,UAA+B,EAAE;IAEjC,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC1D,IAAI,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAA;IAC9B,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,EAAE,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAQ;QAC1B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;YACnC,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;YACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;YAChD,QAAQ,EAAE,CAAA;QACZ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;gBAClC,IAAI,EAAE,6BAA6B;gBACnC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;aAC9B,CAAC,GAAG,IAAI,CAAC,CAAA;YACV,QAAQ,EAAE,CAAA;QACZ,CAAC;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE;IACnD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,cAAc,GAAG,GAAG,IAAI,mBAAmB,CAAA;IACjD,OAAO;QACL,IAAI,EAAE,iBAAiB;QACvB,oDAAoD;QACpD,+CAA+C;QAC/C,QAAQ,EAAE,OAAO,IAAI,aAAa,GAAG,CAAC,WAAW,EAAE,IAAI,UAAU,EAAE,EAAE;QACrE,SAAS,EAAE,GAAG,CAAC,OAAO,EAAE;QACxB,cAAc,EAAE,cAAc;KAC/B,CAAA;AACH,CAAC","sourcesContent":["// 静态 CLI 的事件驱动入口(TypeScript source)。它不内置模型,只把每个事件变成可交给沙箱/Agent 的 dispatch。\n\nimport { readFileSync } from 'node:fs'\nimport { createInterface } from 'node:readline'\nimport { randomUUID } from 'node:crypto'\nimport { prepareSession } from './sessions.js'\nimport { parseEventInput } from './events.js'\n\nexport function readEventFile(path) {\n const text = path === '-' ? readFileSync(0, 'utf8') : readFileSync(path, 'utf8')\n return parseEventInput(text)\n}\n\nexport async function consumeNdjson(\n root,\n cfg,\n input: NodeJS.ReadableStream = process.stdin,\n options: Record<string, any> = {},\n) {\n const rl = createInterface({ input, crlfDelay: Infinity })\n let accepted = 0, rejected = 0\n for await (const line of rl) {\n if (!line.trim()) continue\n try {\n const event = parseEventInput(line)\n const out = prepareSession(root, cfg, event, options)\n process.stdout.write(JSON.stringify(out) + '\\n')\n accepted++\n } catch (e) {\n process.stdout.write(JSON.stringify({\n type: 'dingtalk-agent.run.rejected',\n error: String(e.message || e),\n }) + '\\n')\n rejected++\n }\n }\n return { accepted, rejected }\n}\n\nexport function heartbeatEvent(key = '', eventId = '') {\n const now = new Date()\n const correlationKey = key || 'heartbeat:default'\n return {\n type: 'agent.heartbeat',\n // correlation 稳定,表示同一项 duty;event_id 每拍唯一,表示一次新的信号。\n // 外部调度器重试同一拍时应复用 --heartbeat-event-id,才能端到端幂等。\n event_id: eventId || `heartbeat:${now.toISOString()}:${randomUUID()}`,\n timestamp: now.getTime(),\n correlation_id: correlationKey,\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- // 内化心跳值班 —— **待办是给人的,心跳是 Agent 的生理。**
1
+ // 内化心跳值班 —— **待办是给人的,心跳是 Agent 的生理。**(TypeScript source)
2
2
  //
3
3
  // 把「永远做不完的义务」塞进「有终态的待办」,是错配:
4
4
  // 接力链一断 → 列表里没有下一期 → 心跳无事可做、静默收工、**且不自知**。
@@ -9,24 +9,19 @@
9
9
  // 交付 08:00–22:00(外发,**静默时段顺延,不打扰第三方**)
10
10
  //
11
11
  // **完成判据必须【数据派生】** —— 不记状态、不建标记文件、不看会话记忆。
12
- import { scan, ranTonight, now, today } from './runs.js'
13
-
14
- export const NIGHT = [0, 6]
15
- export const DELIVER = [8, 22]
16
-
17
- const hour = () => new Date(Date.now() + 8 * 3600e3).getUTCHours()
18
- const minute = () => new Date(Date.now() + 8 * 3600e3).getUTCMinutes()
19
- const weekday = () => new Date(Date.now() + 8 * 3600e3).getUTCDay() // 0=周日
20
-
21
- export const inNight = () => hour() >= NIGHT[0] && hour() < NIGHT[1]
22
- export const inDeliver = () => hour() >= DELIVER[0] && hour() < DELIVER[1]
23
-
12
+ import { scan, ranTonight, now, today } from './runs.js';
13
+ export const NIGHT = [0, 6];
14
+ export const DELIVER = [8, 22];
15
+ const hour = () => new Date(Date.now() + 8 * 3600e3).getUTCHours();
16
+ const minute = () => new Date(Date.now() + 8 * 3600e3).getUTCMinutes();
17
+ const weekday = () => new Date(Date.now() + 8 * 3600e3).getUTCDay(); // 0=周日
18
+ export const inNight = () => hour() >= NIGHT[0] && hour() < NIGHT[1];
19
+ export const inDeliver = () => hour() >= DELIVER[0] && hour() < DELIVER[1];
24
20
  /** 近 days 天的运行记录里,有没有一条 what 且 pass 的。 */
25
21
  export function ranOk(root, what, days) {
26
- const hit = scan(root, days).find((r) => r.did === what && r.r === 'pass')
27
- return hit ? `${hit._day} ${hit.t}` : ''
22
+ const hit = scan(root, days).find((r) => r.did === what && r.r === 'pass');
23
+ return hit ? `${hit._day} ${hit.t}` : '';
28
24
  }
29
-
30
25
  /**
31
26
  * 交付类值班的判据。**外发没有派生数据可查 —— 唯一的证据就是留痕。**
32
27
  *
@@ -42,35 +37,38 @@ export function ranOk(root, what, days) {
42
37
  * **在 1 或 2 落地之前,只有单实例是安全的。**
43
38
  */
44
39
  export function deliverDuty(root, { id, hh, mm = 0, playbook, weekly = false }) {
45
- const what = '值班·' + id
46
- let days, span, due
47
- if (weekly) {
48
- days = weekday() === 0 ? 7 : weekday()
49
- span = '本周'
50
- // ⚠️ 不能写成「只有周一 due」—— 周一漏一拍 → 这一期【永不补、也永不报警】。
51
- // 正确语义:**本期内还没送达,就一直该做。**
52
- due = weekday() >= 1 && (weekday() > 1 || hour() >= hh)
53
- } else {
54
- days = 1; span = '今天'
55
- due = hour() > hh || (hour() === hh && minute() >= mm)
56
- }
57
- const at = ranOk(root, what, days)
58
- return {
59
- id, kind: '交付', playbook,
60
- due: due && inDeliver(),
61
- done: Boolean(at),
62
- detail: at ? `${span}已送达 ${at}`
63
- : `${span}还没送达 · 该做的时刻 ${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`,
64
- gap: at ? '' : '按剧本执行 → **回读确认送达** → runlog 记 pass',
65
- }
40
+ const what = '值班·' + id;
41
+ let days, span, due;
42
+ if (weekly) {
43
+ days = weekday() === 0 ? 7 : weekday();
44
+ span = '本周';
45
+ // ⚠️ 不能写成「只有周一 due」—— 周一漏一拍 → 这一期【永不补、也永不报警】。
46
+ // 正确语义:**本期内还没送达,就一直该做。**
47
+ due = weekday() >= 1 && (weekday() > 1 || hour() >= hh);
48
+ }
49
+ else {
50
+ days = 1;
51
+ span = '今天';
52
+ due = hour() > hh || (hour() === hh && minute() >= mm);
53
+ }
54
+ const at = ranOk(root, what, days);
55
+ return {
56
+ id, kind: '交付', playbook,
57
+ due: due && inDeliver(),
58
+ done: Boolean(at),
59
+ detail: at ? `${span}已送达 ${at}`
60
+ : `${span}还没送达 · 该做的时刻 ${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`,
61
+ gap: at ? '' : '按剧本执行 → **回读确认送达** → runlog 记 pass',
62
+ };
66
63
  }
67
-
68
64
  /** 值班表 —— **母体出厂是空的,这是【正确】的。** 具体岗位在 config.duties 里加。 */
69
65
  export function loadDuties(cfg, root) {
70
- const out = {}
71
- for (const d of cfg.duties || []) {
72
- if (d.kind === '交付') out[d.id] = () => deliverDuty(root, d)
73
- // 轻/重 类的 done 判据是【数据派生】的,随岗位而异 → fork 后在这里注册自己的探针。
74
- }
75
- return out
66
+ const out = {};
67
+ for (const d of cfg.duties || []) {
68
+ if (d.kind === '交付')
69
+ out[d.id] = () => deliverDuty(root, d);
70
+ // 轻/重 类的 done 判据是【数据派生】的,随岗位而异 → fork 后在这里注册自己的探针。
71
+ }
72
+ return out;
76
73
  }
74
+ //# sourceMappingURL=duty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duty.js","sourceRoot":"","sources":["../../src/duty.ts"],"names":[],"mappings":"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","sourcesContent":["// 内化心跳值班 —— **待办是给人的,心跳是 Agent 的生理。**(TypeScript source)\n//\n// 把「永远做不完的义务」塞进「有终态的待办」,是错配:\n// 接力链一断 → 列表里没有下一期 → 心跳无事可做、静默收工、**且不自知**。\n//\n// 三类 + 窗口闸门:\n// 轻 白天任意一拍(秒级)\n// 重 00:00–06:00(白天每 15 分钟全量重算一遍是纯浪费)\n// 交付 08:00–22:00(外发,**静默时段顺延,不打扰第三方**)\n//\n// **完成判据必须【数据派生】** —— 不记状态、不建标记文件、不看会话记忆。\nimport { scan, ranTonight, now, today } from './runs.js'\n\nexport const NIGHT = [0, 6]\nexport const DELIVER = [8, 22]\n\nconst hour = () => new Date(Date.now() + 8 * 3600e3).getUTCHours()\nconst minute = () => new Date(Date.now() + 8 * 3600e3).getUTCMinutes()\nconst weekday = () => new Date(Date.now() + 8 * 3600e3).getUTCDay() // 0=周日\n\nexport const inNight = () => hour() >= NIGHT[0] && hour() < NIGHT[1]\nexport const inDeliver = () => hour() >= DELIVER[0] && hour() < DELIVER[1]\n\n/** 近 days 天的运行记录里,有没有一条 what 且 pass 的。 */\nexport function ranOk(root, what, days) {\n const hit = scan(root, days).find((r) => r.did === what && r.r === 'pass')\n return hit ? `${hit._day} ${hit.t}` : ''\n}\n\n/**\n * 交付类值班的判据。**外发没有派生数据可查 —— 唯一的证据就是留痕。**\n *\n * ⚠️ 这条 pass 只能在【回读确认送达】之后写。\n * 没送达就写 pass = 自己骗自己,**比链断掉还糟**(断链至少看得出来\"今天没跑\")。\n *\n * ⚠️⚠️ **已知缺口**:判据靠本地 runs/,而 runs/ 是 gitignored 的。\n * 换实例 / 换容器 / 清了本地档 → **今天的群消息会再发一遍。群里刷屏不可撤销。**\n * 对策(按可靠性排序):\n * 1. 发之前**先回读目标本身**(群里今天已经有这条了吗)—— 证据在【外面的世界】\n * 2. runs/ 落共享存储,让多实例看到同一份\n * 3. 至少:发之前先 duty --check,人眼确认\n * **在 1 或 2 落地之前,只有单实例是安全的。**\n */\nexport function deliverDuty(root, { id, hh, mm = 0, playbook, weekly = false }) {\n const what = '值班·' + id\n let days, span, due\n if (weekly) {\n days = weekday() === 0 ? 7 : weekday()\n span = '本周'\n // ⚠️ 不能写成「只有周一 due」—— 周一漏一拍 → 这一期【永不补、也永不报警】。\n // 正确语义:**本期内还没送达,就一直该做。**\n due = weekday() >= 1 && (weekday() > 1 || hour() >= hh)\n } else {\n days = 1; span = '今天'\n due = hour() > hh || (hour() === hh && minute() >= mm)\n }\n const at = ranOk(root, what, days)\n return {\n id, kind: '交付', playbook,\n due: due && inDeliver(),\n done: Boolean(at),\n detail: at ? `${span}已送达 ${at}`\n : `${span}还没送达 · 该做的时刻 ${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`,\n gap: at ? '' : '按剧本执行 → **回读确认送达** → runlog 记 pass',\n }\n}\n\n/** 值班表 —— **母体出厂是空的,这是【正确】的。** 具体岗位在 config.duties 里加。 */\nexport function loadDuties(cfg, root) {\n const out: Record<string, () => ReturnType<typeof deliverDuty>> = {}\n for (const d of cfg.duties || []) {\n if (d.kind === '交付') out[d.id] = () => deliverDuty(root, d)\n // 轻/重 类的 done 判据是【数据派生】的,随岗位而异 → fork 后在这里注册自己的探针。\n }\n return out\n}\n"]}