@xdxer/dingtalk-agent 0.1.4-beta.7 → 0.1.4-beta.9
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.
- package/CHANGELOG.md +35 -0
- package/README.en.md +395 -0
- package/README.md +466 -36
- package/dist/bin/dingtalk-agent.js +1071 -340
- package/dist/bin/dingtalk-agent.js.map +1 -1
- package/dist/src/actions.js +98 -14
- package/dist/src/actions.js.map +1 -1
- package/dist/src/agent-audit.js +460 -0
- package/dist/src/agent-audit.js.map +1 -0
- package/dist/src/agent-bindings.js +132 -0
- package/dist/src/agent-bindings.js.map +1 -0
- package/dist/src/agent-definition.js +182 -0
- package/dist/src/agent-definition.js.map +1 -0
- package/dist/src/agent-enhance.js +678 -0
- package/dist/src/agent-enhance.js.map +1 -0
- package/dist/src/bootstrap.js +125 -17
- package/dist/src/bootstrap.js.map +1 -1
- package/dist/src/development-workspace.js +729 -0
- package/dist/src/development-workspace.js.map +1 -0
- package/dist/src/dws.js +145 -0
- package/dist/src/dws.js.map +1 -1
- package/dist/src/eval-evidence.js +193 -0
- package/dist/src/eval-evidence.js.map +1 -0
- package/dist/src/init.js +1 -1
- package/dist/src/init.js.map +1 -1
- package/dist/src/invocation.js +36 -0
- package/dist/src/invocation.js.map +1 -0
- package/dist/src/lab.js +679 -0
- package/dist/src/lab.js.map +1 -0
- package/dist/src/lease.js +100 -0
- package/dist/src/lease.js.map +1 -0
- package/dist/src/memory/candidates.js +451 -0
- package/dist/src/memory/candidates.js.map +1 -0
- package/dist/src/memory/completion-evidence.js +536 -0
- package/dist/src/memory/completion-evidence.js.map +1 -0
- package/dist/src/memory/operational.js +263 -0
- package/dist/src/memory/operational.js.map +1 -0
- package/dist/src/memory/remote-state.js +478 -0
- package/dist/src/memory/remote-state.js.map +1 -0
- package/dist/src/memory/task-checkpoints.js +204 -0
- package/dist/src/memory/task-checkpoints.js.map +1 -0
- package/dist/src/multica-deploy.js +1480 -0
- package/dist/src/multica-deploy.js.map +1 -0
- package/dist/src/multica-provider.js +685 -0
- package/dist/src/multica-provider.js.map +1 -0
- package/dist/src/opencode-evals.js +1062 -0
- package/dist/src/opencode-evals.js.map +1 -0
- package/dist/src/opencode-provider.js +531 -0
- package/dist/src/opencode-provider.js.map +1 -0
- package/dist/src/opencode-workspace.js +197 -0
- package/dist/src/opencode-workspace.js.map +1 -0
- package/dist/src/perception.js +225 -0
- package/dist/src/perception.js.map +1 -0
- package/dist/src/personal-event-evals.js +595 -0
- package/dist/src/personal-event-evals.js.map +1 -0
- package/dist/src/promotion.js +786 -0
- package/dist/src/promotion.js.map +1 -0
- package/dist/src/remote-semantic-state-live-evals.js +888 -0
- package/dist/src/remote-semantic-state-live-evals.js.map +1 -0
- package/dist/src/remote-semantic-state-worker.js +38 -0
- package/dist/src/remote-semantic-state-worker.js.map +1 -0
- package/dist/src/remote-state-evals.js +501 -0
- package/dist/src/remote-state-evals.js.map +1 -0
- package/dist/src/response-gate.js +51 -0
- package/dist/src/response-gate.js.map +1 -0
- package/dist/src/robot-evals.js +770 -0
- package/dist/src/robot-evals.js.map +1 -0
- package/dist/src/sessions.js +66 -105
- package/dist/src/sessions.js.map +1 -1
- package/dist/src/skill-manager.js +25 -16
- package/dist/src/skill-manager.js.map +1 -1
- package/dist/src/skills.js.map +1 -1
- package/dist/src/storage-evals.js +26 -0
- package/dist/src/storage-evals.js.map +1 -0
- package/dist/src/types.js.map +1 -1
- package/dist/src/waits.js +5 -1
- package/dist/src/waits.js.map +1 -1
- package/dist/src/workspace.js +28 -3
- package/dist/src/workspace.js.map +1 -1
- package/docs/INSTALLATION.md +47 -0
- package/docs/SECOND-AGENT-ACCEPTANCE.md +62 -0
- package/docs/architecture/agent-memory-topology.png +0 -0
- package/docs/architecture/agent-memory-topology.svg +132 -0
- package/docs/architecture/general-agent-kernel-topology.png +0 -0
- package/docs/architecture/general-agent-kernel-topology.svg +149 -0
- package/docs/architecture/provider-bound-development-workspace.png +0 -0
- package/docs/architecture/provider-bound-development-workspace.svg +141 -0
- package/docs/architecture/task-completion-gate.png +0 -0
- package/docs/architecture/task-completion-gate.svg +191 -0
- package/docs/schemas/agent-audit-load-evidence.schema.json +14 -0
- package/docs/schemas/agent-audit.schema.json +92 -0
- package/docs/schemas/agent-bindings.schema.json +54 -0
- package/docs/schemas/agent-definition.schema.json +78 -0
- package/docs/schemas/agent-enhancement-plan.schema.json +88 -0
- package/docs/schemas/agent-enhancement-receipt.schema.json +37 -0
- package/docs/schemas/enriched-invocation.schema.json +46 -0
- package/docs/schemas/eval-candidate-plan.schema.json +28 -0
- package/docs/schemas/eval-candidate-result.schema.json +20 -0
- package/docs/schemas/eval-candidate.schema.json +30 -0
- package/docs/schemas/invocation.schema.json +19 -0
- package/docs/schemas/memory-candidate-proposal.schema.json +18 -0
- package/docs/schemas/memory-candidate.schema.json +76 -0
- package/docs/schemas/memory-publish-target.schema.json +25 -0
- package/docs/schemas/multica-deployment-list.schema.json +29 -0
- package/docs/schemas/multica-deployment-operation.schema.json +51 -0
- package/docs/schemas/multica-deployment-plan.schema.json +70 -0
- package/docs/schemas/multica-deployment-receipt.schema.json +87 -0
- package/docs/schemas/multica-deployment-status.schema.json +23 -0
- package/docs/schemas/multica-workspace-inspection.schema.json +77 -0
- package/docs/schemas/multica-workspace-plan.schema.json +68 -0
- package/docs/schemas/multica-workspace-resource-list.schema.json +27 -0
- package/docs/schemas/multica-workspace-status.schema.json +34 -0
- package/docs/schemas/observation.schema.json +21 -0
- package/docs/schemas/operational-memory-provider.schema.json +36 -0
- package/docs/schemas/operational-memory-record.schema.json +24 -0
- package/docs/schemas/perception-input.schema.json +56 -0
- package/docs/schemas/project.schema.json +112 -0
- package/docs/schemas/promotion-list.schema.json +36 -0
- package/docs/schemas/promotion-plan.schema.json +60 -0
- package/docs/schemas/promotion-policy.schema.json +29 -0
- package/docs/schemas/promotion-receipt.schema.json +43 -0
- package/docs/schemas/promotion-status.schema.json +23 -0
- package/docs/schemas/release-readiness.schema.json +65 -0
- package/docs/schemas/remote-semantic-state-live-eval.schema.json +60 -0
- package/docs/schemas/remote-semantic-state-manifest.schema.json +79 -0
- package/docs/schemas/remote-semantic-state-provider.schema.json +98 -0
- package/docs/schemas/response-gate.schema.json +20 -0
- package/docs/schemas/task-checkpoint.schema.json +71 -0
- package/docs/schemas/task-completion-evidence.schema.json +154 -0
- package/docs/schemas/workspace-doctor.schema.json +56 -0
- package/docs/schemas/workspace-state.schema.json +39 -0
- package/evals/baselines/2026-07-16/opencode-basic-010-completion-summary.json +123 -0
- package/evals/baselines/2026-07-16/opencode-basic-skill-required-summary.json +69 -0
- package/evals/baselines/2026-07-16/opencode-multi-surface-summary.json +63 -0
- package/evals/baselines/2026-07-16/remote-state-live-summary.json +70 -0
- package/evals/baselines/2026-07-17/agent-enhance-opencode-dogfood-summary.json +98 -0
- package/evals/baselines/2026-07-17/personal-event-live-readiness-summary.json +68 -0
- package/examples/agents/fde-coach/AGENTS.md +26 -0
- package/examples/agents/fde-coach/MEMORY.md +3 -0
- package/examples/agents/fde-coach/fields/default/field.json +24 -0
- package/examples/agents/fde-coach/knowledge/INDEX.md +4 -0
- package/examples/agents/fde-coach/skills/fde-coach/SKILL.md +13 -0
- package/examples/agents/release-manager/AGENTS.md +26 -0
- package/examples/agents/release-manager/MEMORY.md +3 -0
- package/examples/agents/release-manager/fields/default/field.json +24 -0
- package/examples/agents/release-manager/knowledge/INDEX.md +4 -0
- package/examples/agents/release-manager/skills/release-manager/SKILL.md +13 -0
- package/lab/README.md +109 -0
- package/lab/agent-eval/catalog.json +91 -0
- package/lab/agent-eval/classic-failures.json +177 -0
- package/lab/agent-eval/completion-gate-regression.json +99 -0
- package/lab/agent-eval/personal-event-live.example.json +94 -0
- package/lab/agent-eval/remote-semantic-state-live.example.json +70 -0
- package/lab/agent-eval/remote-semantic-state-provider.fixture.json +47 -0
- package/lab/agent-eval/remote-state-workspace/AGENTS.md +8 -0
- package/lab/agent-eval/remote-state-workspace/opencode.json +7 -0
- package/lab/agent-eval/remote-state-workspace/skills/remote-state-operator/SKILL.md +13 -0
- package/lab/agent-eval/remote-state.example.json +31 -0
- package/lab/agent-eval/workspace/AGENTS.md +7 -0
- package/lab/agent-eval/workspace/MEMORY.md +4 -0
- package/lab/agent-eval/workspace/artifacts/pending-review.md +3 -0
- package/lab/agent-eval/workspace/knowledge/INDEX.md +4 -0
- package/lab/agent-eval/workspace/opencode.json +20 -0
- package/lab/manifest.example.json +27 -0
- package/lab/manifest.personal-event.example.json +27 -0
- package/lab/project-workspace/README.md +11 -0
- package/lab/project-workspace/fake-multica-provider.mjs +266 -0
- package/lab/project-workspace/multica-deploy.fixture.json +29 -0
- package/lab/project-workspace/multica-readonly.fixture.json +69 -0
- package/lab/project-workspace/observation.fixture.json +14 -0
- package/lab/project-workspace/opencode-provider-suite.json +65 -0
- package/lab/project-workspace/project.fixture.json +44 -0
- package/lab/project-workspace/promotion-policy.fixture.json +15 -0
- package/lab/robot-eval/pool.example.json +30 -0
- package/lab/robot-eval/suite.json +123 -0
- package/lab/robot-eval/workspace/AGENTS.md +21 -0
- package/lab/robot-eval/workspace/MEMORY.md +3 -0
- package/lab/robot-eval/workspace/knowledge/INDEX.md +5 -0
- package/lab/robot-eval/workspace/opencode.json +22 -0
- package/lab/schemas/agent-eval-catalog.schema.json +47 -0
- package/lab/schemas/lab-manifest.schema.json +70 -0
- package/lab/schemas/personal-event-eval.schema.json +91 -0
- package/lab/schemas/remote-state-eval.schema.json +66 -0
- package/lab/schemas/robot-eval-suite.schema.json +184 -0
- package/lab/schemas/robot-pool.schema.json +56 -0
- package/package.json +30 -14
- package/skills/dingtalk-agent-boot-multica/SKILL.md +40 -0
- package/skills/dingtalk-agent-compose/SKILL.md +110 -0
- package/skills/dingtalk-agent-compose/assets/AGENTS.template.md +26 -0
- package/skills/dingtalk-agent-compose/assets/agent.bindings.dingtalk-doc.template.json +13 -0
- package/skills/dingtalk-agent-compose/assets/agent.bindings.local.template.json +13 -0
- package/skills/dingtalk-agent-compose/assets/opencode.template.json +12 -0
- package/skills/dingtalk-agent-compose/assets/role-skill.template.md +24 -0
- package/skills/dingtalk-agent-compose/evals/evals.json +94 -0
- package/skills/dingtalk-agent-compose/references/agent-definition-contract.md +55 -0
- package/skills/dingtalk-agent-compose/references/opencode-host-contract.md +65 -0
- package/skills/dingtalk-agent-compose/references/storage-routing.md +20 -0
- package/skills/dingtalk-agent-deploy/SKILL.md +60 -0
- package/skills/dingtalk-agent-deploy/references/multica-deployment-contract.md +49 -0
- package/skills/dingtalk-agent-deploy/references/promotion-observation-contract.md +49 -0
- package/skills/dingtalk-agent-eval/SKILL.md +116 -0
- package/skills/dingtalk-agent-eval/assets/eval-catalog.template.json +18 -0
- package/skills/dingtalk-agent-eval/evals/evals.json +61 -0
- package/skills/dingtalk-agent-eval/references/eval-topology.md +34 -0
- package/skills/dingtalk-agent-eval/references/evidence-contract.md +31 -0
- package/skills/dingtalk-agent-eval/references/scenario-taxonomy.md +25 -0
- package/skills/dingtalk-agent-eval/references/storage-modes.md +75 -0
- package/skills/dingtalk-basic-behavior/SKILL.md +63 -3
- package/skills/dingtalk-basic-behavior/assets/memory-candidate-proposal.json +10 -0
- package/skills/dingtalk-basic-behavior/assets/{task-checkpoint.md → task-checkpoint.json} +2 -21
- package/skills/dingtalk-basic-behavior/references/action-contract.md +2 -0
- package/skills/dingtalk-basic-behavior/references/memory-and-evolution.md +15 -1
- package/skills/dingtalk-basic-behavior/references/perception-and-gates.md +28 -0
- package/skills/dingtalk-basic-behavior/references/task-lifecycle.md +32 -7
- package/dist/src/boot.js +0 -70
- package/dist/src/boot.js.map +0 -1
- package/dist/src/duty.js +0 -74
- package/dist/src/duty.js.map +0 -1
- package/dist/src/kb.js +0 -240
- package/dist/src/kb.js.map +0 -1
- package/dist/src/runs.js +0 -79
- package/dist/src/runs.js.map +0 -1
- package/docs/ARCHITECTURE.md +0 -219
- package/docs/MINIMAL-WORKSPACE-V1.md +0 -172
- package/docs/OPEN-SOURCE-REFERENCES.md +0 -107
- package/docs/SELF-TEST.md +0 -252
- package/docs//345/206/205/347/275/221/345/256/236/347/233/270.md +0 -77
- package/evals/baselines/2026-07-14/behavior-summary.json +0 -28
- package/evals/baselines/2026-07-14/contract-summary.json +0 -18
- package/evals/baselines/2026-07-14/live-canary-summary.json +0 -25
- package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/SKILL.md +0 -72
- package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/action-contract.md +0 -31
- package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/event-to-behavior.md +0 -22
- package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/memory-and-evolution.md +0 -25
- package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/runtime-modes.md +0 -34
- package/evals/baselines/2026-07-15/task-lifecycle-summary.json +0 -50
- package/evals/evals.json +0 -339
- package/evals/fixtures/dm-ambiguous-send.json +0 -4
- package/evals/fixtures/dm-blocked.json +0 -4
- package/evals/fixtures/dm-clear.json +0 -4
- package/evals/fixtures/dm-discussion.json +0 -4
- package/evals/fixtures/dm-doc-write-no-tool.json +0 -4
- package/evals/fixtures/dm-long-task-ack.json +0 -4
- package/evals/fixtures/dm-nonblocking-gap.json +0 -4
- package/evals/fixtures/dm-structured-task.json +0 -4
- package/evals/fixtures/group.json +0 -10
- package/evals/fixtures/mentioned.json +0 -3
- package/evals/run-contract-evals.mjs +0 -1169
- package/evals/run-shadow-evals.mjs +0 -267
- package/evals/runners/README.md +0 -66
- package/evals/runners/claude-shadow.mjs +0 -533
- package/evals/schemas/action-request.schema.json +0 -77
- package/evals/shadow-evals.json +0 -133
- package/skills/AGENTS.md +0 -104
- package/skills//344/273/273/345/212/241.md +0 -48
- package/skills//345/237/272/347/241/200/350/241/214/344/270/272.md +0 -44
- package/skills//345/277/203/350/267/263.md +0 -79
- package/skills//346/266/210/346/201/257.md +0 -50
- package/skills//347/237/245/350/257/206.md +0 -55
- package/skills//350/257/204/346/265/213.md +0 -62
- package/skills//351/222/211/351/222/211.md +0 -64
- package/templates/ontology/index.md +0 -24
- package/templates/ontology/self/access.md +0 -38
- package/templates/ontology/self/role-spec.md +0 -34
- package/templates/ontology/self/workspace.md +0 -41
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { join, relative, resolve } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
export const REQUIRED_BASIC_SKILL = 'dingtalk-basic-behavior';
|
|
7
|
+
export const REQUIRED_BASIC_SKILL_INSTRUCTION = '.agents/skills/dingtalk-basic-behavior/SKILL.md';
|
|
8
|
+
/**
|
|
9
|
+
* OpenCode only discovers project Skills by default; it does not load their body.
|
|
10
|
+
* This binding makes the Basic Skill an unconditional instruction while retaining
|
|
11
|
+
* the native project Skill for discovery and explicit inspection.
|
|
12
|
+
*/
|
|
13
|
+
export function bindRequiredBasicSkill(workspace) {
|
|
14
|
+
const workspaceReal = realpathSync(resolve(workspace));
|
|
15
|
+
const skillPath = join(workspaceReal, REQUIRED_BASIC_SKILL_INSTRUCTION);
|
|
16
|
+
if (!existsSync(skillPath)) {
|
|
17
|
+
throw new Error(`OpenCode 必需 Basic Skill 不存在: ${skillPath}`);
|
|
18
|
+
}
|
|
19
|
+
const skillBody = readFileSync(skillPath, 'utf8');
|
|
20
|
+
const name = frontmatterValue(skillBody, 'name');
|
|
21
|
+
const version = metadataVersion(skillBody);
|
|
22
|
+
if (name !== REQUIRED_BASIC_SKILL || !version) {
|
|
23
|
+
throw new Error(`OpenCode Basic Skill 元数据非法: name=${name || 'missing'} version=${version || 'missing'}`);
|
|
24
|
+
}
|
|
25
|
+
const configPath = join(workspaceReal, 'opencode.json');
|
|
26
|
+
const config = existsSync(configPath)
|
|
27
|
+
? parseObject(readFileSync(configPath, 'utf8'), 'opencode.json')
|
|
28
|
+
: {};
|
|
29
|
+
const instructions = config.instructions === undefined
|
|
30
|
+
? []
|
|
31
|
+
: stringArray(config.instructions, 'opencode.json#instructions');
|
|
32
|
+
config.$schema ||= 'https://opencode.ai/config.json';
|
|
33
|
+
config.instructions = [
|
|
34
|
+
REQUIRED_BASIC_SKILL_INSTRUCTION,
|
|
35
|
+
...instructions.filter((item) => item !== REQUIRED_BASIC_SKILL_INSTRUCTION),
|
|
36
|
+
];
|
|
37
|
+
const permission = config.permission === undefined
|
|
38
|
+
? {}
|
|
39
|
+
: objectValue(config.permission, 'opencode.json#permission');
|
|
40
|
+
const skillPermission = permission.skill === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: objectValue(permission.skill, 'opencode.json#permission.skill');
|
|
43
|
+
skillPermission[REQUIRED_BASIC_SKILL] = 'allow';
|
|
44
|
+
permission.skill = skillPermission;
|
|
45
|
+
config.permission = permission;
|
|
46
|
+
atomicJson(configPath, config);
|
|
47
|
+
return {
|
|
48
|
+
$schema: 'dingtalk-agent/opencode-skill-binding@1',
|
|
49
|
+
host: 'opencode',
|
|
50
|
+
required: true,
|
|
51
|
+
strategy: 'instructions',
|
|
52
|
+
workspace: workspaceReal,
|
|
53
|
+
configPath,
|
|
54
|
+
instruction: REQUIRED_BASIC_SKILL_INSTRUCTION,
|
|
55
|
+
skill: {
|
|
56
|
+
name,
|
|
57
|
+
version,
|
|
58
|
+
path: realpathSync(skillPath),
|
|
59
|
+
hash: sha256(skillBody),
|
|
60
|
+
},
|
|
61
|
+
configHash: sha256(readFileSync(configPath, 'utf8')),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function verifyRequiredBasicSkill(binding, command = process.env.DTA_OPENCODE_BIN || 'opencode') {
|
|
65
|
+
const current = bindRequiredBasicSkill(binding.workspace);
|
|
66
|
+
if (current.skill.hash !== binding.skill.hash || current.configHash !== binding.configHash) {
|
|
67
|
+
throw new Error('OpenCode Basic Skill 或配置在 preflight 前发生漂移');
|
|
68
|
+
}
|
|
69
|
+
const versionRun = runOpenCode(command, ['--version'], binding.workspace);
|
|
70
|
+
const configRun = runOpenCode(command, ['debug', 'config'], binding.workspace);
|
|
71
|
+
const resolved = parseObject(configRun, 'opencode debug config');
|
|
72
|
+
const resolvedInstructions = stringArray(resolved.instructions || [], 'opencode resolved instructions');
|
|
73
|
+
if (!resolvedInstructions.includes(REQUIRED_BASIC_SKILL_INSTRUCTION)) {
|
|
74
|
+
throw new Error('OpenCode resolved config 未强制加载 dingtalk-basic-behavior');
|
|
75
|
+
}
|
|
76
|
+
const isolatedHome = mkdtempSync(join(tmpdir(), 'dta-opencode-preflight-'));
|
|
77
|
+
let skillsRun = '';
|
|
78
|
+
try {
|
|
79
|
+
skillsRun = runOpenCode(command, ['debug', 'skill'], binding.workspace, 4 * 1024 * 1024, {
|
|
80
|
+
HOME: isolatedHome,
|
|
81
|
+
XDG_CONFIG_HOME: join(isolatedHome, 'config'),
|
|
82
|
+
XDG_DATA_HOME: join(isolatedHome, 'data'),
|
|
83
|
+
XDG_CACHE_HOME: join(isolatedHome, 'cache'),
|
|
84
|
+
XDG_STATE_HOME: join(isolatedHome, 'state'),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
rmSync(isolatedHome, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
const discoveredLocation = skillsRun.match(new RegExp(`"name":\\s*"${REQUIRED_BASIC_SKILL}"[\\s\\S]{0,4000}?"location":\\s*"([^"]+)"`))?.[1];
|
|
91
|
+
let discovered = false;
|
|
92
|
+
if (discoveredLocation) {
|
|
93
|
+
try {
|
|
94
|
+
const decoded = JSON.parse(`"${discoveredLocation}"`);
|
|
95
|
+
discovered = existsSync(decoded) && realpathSync(decoded) === binding.skill.path;
|
|
96
|
+
}
|
|
97
|
+
catch { /* malformed or truncated debug output */ }
|
|
98
|
+
}
|
|
99
|
+
if (!discovered) {
|
|
100
|
+
const marker = skillsRun.indexOf(REQUIRED_BASIC_SKILL);
|
|
101
|
+
const nearby = marker >= 0
|
|
102
|
+
? skillsRun.slice(Math.max(0, marker - 160), marker + 600).replace(/\s+/g, ' ')
|
|
103
|
+
: `debug skill bytes=${Buffer.byteLength(skillsRun)}`;
|
|
104
|
+
throw new Error(`OpenCode 未从当前项目发现 Basic Skill: ${binding.skill.path}; ${nearby}`);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
...binding,
|
|
108
|
+
verified: true,
|
|
109
|
+
openCodeVersion: versionRun.trim(),
|
|
110
|
+
resolvedInstruction: true,
|
|
111
|
+
discoveredProjectSkill: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function removeRequiredBasicSkillInstruction(workspace) {
|
|
115
|
+
const workspaceReal = realpathSync(resolve(workspace));
|
|
116
|
+
const configPath = join(workspaceReal, 'opencode.json');
|
|
117
|
+
const config = existsSync(configPath)
|
|
118
|
+
? parseObject(readFileSync(configPath, 'utf8'), 'opencode.json')
|
|
119
|
+
: {};
|
|
120
|
+
const instructions = config.instructions === undefined
|
|
121
|
+
? []
|
|
122
|
+
: stringArray(config.instructions, 'opencode.json#instructions');
|
|
123
|
+
config.instructions = instructions.filter((item) => item !== REQUIRED_BASIC_SKILL_INSTRUCTION);
|
|
124
|
+
const permission = config.permission === undefined
|
|
125
|
+
? {}
|
|
126
|
+
: objectValue(config.permission, 'opencode.json#permission');
|
|
127
|
+
const skillPermission = permission.skill === undefined
|
|
128
|
+
? {}
|
|
129
|
+
: objectValue(permission.skill, 'opencode.json#permission.skill');
|
|
130
|
+
skillPermission[REQUIRED_BASIC_SKILL] = 'deny';
|
|
131
|
+
permission.skill = skillPermission;
|
|
132
|
+
config.permission = permission;
|
|
133
|
+
atomicJson(configPath, config);
|
|
134
|
+
}
|
|
135
|
+
function runOpenCode(command, args, cwd, maxBuffer = 4 * 1024 * 1024, environment = {}) {
|
|
136
|
+
const child = spawnSync(command, args, {
|
|
137
|
+
cwd,
|
|
138
|
+
encoding: 'utf8',
|
|
139
|
+
timeout: 30_000,
|
|
140
|
+
maxBuffer,
|
|
141
|
+
env: { ...process.env, ...environment },
|
|
142
|
+
});
|
|
143
|
+
if (child.error || child.status !== 0) {
|
|
144
|
+
throw new Error(`OpenCode preflight 失败 (${args.join(' ')}): ${child.error?.message || String(child.stderr || child.stdout || `exit=${child.status}`).trim()}`);
|
|
145
|
+
}
|
|
146
|
+
return String(child.stdout || '');
|
|
147
|
+
}
|
|
148
|
+
function frontmatterValue(body, key) {
|
|
149
|
+
const frontmatter = body.match(/^---\s*\n([\s\S]*?)\n---/)?.[1] || '';
|
|
150
|
+
return frontmatter.match(new RegExp(`^${key}:\\s*["']?([^"'\\n]+)["']?\\s*$`, 'm'))?.[1]?.trim() || '';
|
|
151
|
+
}
|
|
152
|
+
function metadataVersion(body) {
|
|
153
|
+
const frontmatter = body.match(/^---\s*\n([\s\S]*?)\n---/)?.[1] || '';
|
|
154
|
+
return frontmatter.match(/^metadata:\s*\n(?:^[ \t]+.*\n)*?^[ \t]+version:\s*["']?([^"'\n]+)["']?\s*$/m)?.[1]?.trim() || '';
|
|
155
|
+
}
|
|
156
|
+
function parseObject(text, label) {
|
|
157
|
+
let value;
|
|
158
|
+
try {
|
|
159
|
+
value = JSON.parse(text);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
throw new Error(`${label} 不是合法 JSON: ${error.message}`);
|
|
163
|
+
}
|
|
164
|
+
return objectValue(value, label);
|
|
165
|
+
}
|
|
166
|
+
function objectValue(value, label) {
|
|
167
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
168
|
+
throw new Error(`${label} 必须是 JSON object`);
|
|
169
|
+
}
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
function stringArray(value, label) {
|
|
173
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
|
|
174
|
+
throw new Error(`${label} 必须是 string[]`);
|
|
175
|
+
}
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
function sha256(value) {
|
|
179
|
+
return createHash('sha256').update(value).digest('hex');
|
|
180
|
+
}
|
|
181
|
+
function atomicJson(path, value) {
|
|
182
|
+
const body = JSON.stringify(value, null, 2) + '\n';
|
|
183
|
+
const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
184
|
+
writeFileSync(temporary, body, { flag: 'wx' });
|
|
185
|
+
renameSync(temporary, path);
|
|
186
|
+
}
|
|
187
|
+
export function relativeBinding(binding) {
|
|
188
|
+
return {
|
|
189
|
+
...binding,
|
|
190
|
+
configPath: relative(binding.workspace, binding.configPath),
|
|
191
|
+
skill: {
|
|
192
|
+
...binding.skill,
|
|
193
|
+
path: relative(binding.workspace, binding.skill.path),
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=opencode-workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-workspace.js","sourceRoot":"","sources":["../../src/opencode-workspace.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto'\nimport {\n existsSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync,\n} from 'node:fs'\nimport { spawnSync } from 'node:child_process'\nimport { join, relative, resolve } from 'node:path'\nimport { tmpdir } from 'node:os'\n\nexport const REQUIRED_BASIC_SKILL = 'dingtalk-basic-behavior'\nexport const REQUIRED_BASIC_SKILL_INSTRUCTION =\n '.agents/skills/dingtalk-basic-behavior/SKILL.md'\n\nexport interface OpenCodeSkillBinding {\n $schema: 'dingtalk-agent/opencode-skill-binding@1'\n host: 'opencode'\n required: true\n strategy: 'instructions'\n workspace: string\n configPath: string\n instruction: string\n skill: {\n name: string\n version: string\n path: string\n hash: string\n }\n configHash: string\n}\n\nexport interface OpenCodeSkillPreflight extends OpenCodeSkillBinding {\n verified: true\n openCodeVersion: string\n resolvedInstruction: true\n discoveredProjectSkill: true\n}\n\n/**\n * OpenCode only discovers project Skills by default; it does not load their body.\n * This binding makes the Basic Skill an unconditional instruction while retaining\n * the native project Skill for discovery and explicit inspection.\n */\nexport function bindRequiredBasicSkill(workspace: string): OpenCodeSkillBinding {\n const workspaceReal = realpathSync(resolve(workspace))\n const skillPath = join(workspaceReal, REQUIRED_BASIC_SKILL_INSTRUCTION)\n if (!existsSync(skillPath)) {\n throw new Error(`OpenCode 必需 Basic Skill 不存在: ${skillPath}`)\n }\n const skillBody = readFileSync(skillPath, 'utf8')\n const name = frontmatterValue(skillBody, 'name')\n const version = metadataVersion(skillBody)\n if (name !== REQUIRED_BASIC_SKILL || !version) {\n throw new Error(`OpenCode Basic Skill 元数据非法: name=${name || 'missing'} version=${version || 'missing'}`)\n }\n\n const configPath = join(workspaceReal, 'opencode.json')\n const config = existsSync(configPath)\n ? parseObject(readFileSync(configPath, 'utf8'), 'opencode.json')\n : {}\n const instructions = config.instructions === undefined\n ? []\n : stringArray(config.instructions, 'opencode.json#instructions')\n config.$schema ||= 'https://opencode.ai/config.json'\n config.instructions = [\n REQUIRED_BASIC_SKILL_INSTRUCTION,\n ...instructions.filter((item) => item !== REQUIRED_BASIC_SKILL_INSTRUCTION),\n ]\n\n const permission = config.permission === undefined\n ? {}\n : objectValue(config.permission, 'opencode.json#permission')\n const skillPermission = permission.skill === undefined\n ? {}\n : objectValue(permission.skill, 'opencode.json#permission.skill')\n skillPermission[REQUIRED_BASIC_SKILL] = 'allow'\n permission.skill = skillPermission\n config.permission = permission\n atomicJson(configPath, config)\n\n return {\n $schema: 'dingtalk-agent/opencode-skill-binding@1',\n host: 'opencode',\n required: true,\n strategy: 'instructions',\n workspace: workspaceReal,\n configPath,\n instruction: REQUIRED_BASIC_SKILL_INSTRUCTION,\n skill: {\n name,\n version,\n path: realpathSync(skillPath),\n hash: sha256(skillBody),\n },\n configHash: sha256(readFileSync(configPath, 'utf8')),\n }\n}\n\nexport function verifyRequiredBasicSkill(\n binding: OpenCodeSkillBinding,\n command = process.env.DTA_OPENCODE_BIN || 'opencode',\n): OpenCodeSkillPreflight {\n const current = bindRequiredBasicSkill(binding.workspace)\n if (current.skill.hash !== binding.skill.hash || current.configHash !== binding.configHash) {\n throw new Error('OpenCode Basic Skill 或配置在 preflight 前发生漂移')\n }\n\n const versionRun = runOpenCode(command, ['--version'], binding.workspace)\n const configRun = runOpenCode(command, ['debug', 'config'], binding.workspace)\n const resolved = parseObject(configRun, 'opencode debug config')\n const resolvedInstructions = stringArray(\n resolved.instructions || [], 'opencode resolved instructions')\n if (!resolvedInstructions.includes(REQUIRED_BASIC_SKILL_INSTRUCTION)) {\n throw new Error('OpenCode resolved config 未强制加载 dingtalk-basic-behavior')\n }\n\n const isolatedHome = mkdtempSync(join(tmpdir(), 'dta-opencode-preflight-'))\n let skillsRun = ''\n try {\n skillsRun = runOpenCode(command, ['debug', 'skill'], binding.workspace, 4 * 1024 * 1024, {\n HOME: isolatedHome,\n XDG_CONFIG_HOME: join(isolatedHome, 'config'),\n XDG_DATA_HOME: join(isolatedHome, 'data'),\n XDG_CACHE_HOME: join(isolatedHome, 'cache'),\n XDG_STATE_HOME: join(isolatedHome, 'state'),\n })\n } finally {\n rmSync(isolatedHome, { recursive: true, force: true })\n }\n const discoveredLocation = skillsRun.match(\n new RegExp(`\"name\":\\\\s*\"${REQUIRED_BASIC_SKILL}\"[\\\\s\\\\S]{0,4000}?\"location\":\\\\s*\"([^\"]+)\"`),\n )?.[1]\n let discovered = false\n if (discoveredLocation) {\n try {\n const decoded = JSON.parse(`\"${discoveredLocation}\"`)\n discovered = existsSync(decoded) && realpathSync(decoded) === binding.skill.path\n } catch { /* malformed or truncated debug output */ }\n }\n if (!discovered) {\n const marker = skillsRun.indexOf(REQUIRED_BASIC_SKILL)\n const nearby = marker >= 0\n ? skillsRun.slice(Math.max(0, marker - 160), marker + 600).replace(/\\s+/g, ' ')\n : `debug skill bytes=${Buffer.byteLength(skillsRun)}`\n throw new Error(`OpenCode 未从当前项目发现 Basic Skill: ${binding.skill.path}; ${nearby}`)\n }\n\n return {\n ...binding,\n verified: true,\n openCodeVersion: versionRun.trim(),\n resolvedInstruction: true,\n discoveredProjectSkill: true,\n }\n}\n\nexport function removeRequiredBasicSkillInstruction(workspace: string): void {\n const workspaceReal = realpathSync(resolve(workspace))\n const configPath = join(workspaceReal, 'opencode.json')\n const config = existsSync(configPath)\n ? parseObject(readFileSync(configPath, 'utf8'), 'opencode.json')\n : {}\n const instructions = config.instructions === undefined\n ? []\n : stringArray(config.instructions, 'opencode.json#instructions')\n config.instructions = instructions.filter((item) => item !== REQUIRED_BASIC_SKILL_INSTRUCTION)\n const permission = config.permission === undefined\n ? {}\n : objectValue(config.permission, 'opencode.json#permission')\n const skillPermission = permission.skill === undefined\n ? {}\n : objectValue(permission.skill, 'opencode.json#permission.skill')\n skillPermission[REQUIRED_BASIC_SKILL] = 'deny'\n permission.skill = skillPermission\n config.permission = permission\n atomicJson(configPath, config)\n}\n\nfunction runOpenCode(\n command: string,\n args: string[],\n cwd: string,\n maxBuffer = 4 * 1024 * 1024,\n environment: Record<string, string> = {},\n): string {\n const child = spawnSync(command, args, {\n cwd,\n encoding: 'utf8',\n timeout: 30_000,\n maxBuffer,\n env: { ...process.env, ...environment },\n })\n if (child.error || child.status !== 0) {\n throw new Error(`OpenCode preflight 失败 (${args.join(' ')}): ${\n child.error?.message || String(child.stderr || child.stdout || `exit=${child.status}`).trim()}`)\n }\n return String(child.stdout || '')\n}\n\nfunction frontmatterValue(body: string, key: string): string {\n const frontmatter = body.match(/^---\\s*\\n([\\s\\S]*?)\\n---/)?.[1] || ''\n return frontmatter.match(new RegExp(`^${key}:\\\\s*[\"']?([^\"'\\\\n]+)[\"']?\\\\s*$`, 'm'))?.[1]?.trim() || ''\n}\n\nfunction metadataVersion(body: string): string {\n const frontmatter = body.match(/^---\\s*\\n([\\s\\S]*?)\\n---/)?.[1] || ''\n return frontmatter.match(/^metadata:\\s*\\n(?:^[ \\t]+.*\\n)*?^[ \\t]+version:\\s*[\"']?([^\"'\\n]+)[\"']?\\s*$/m)?.[1]?.trim() || ''\n}\n\nfunction parseObject(text: string, label: string): Record<string, any> {\n let value: unknown\n try { value = JSON.parse(text) }\n catch (error) { throw new Error(`${label} 不是合法 JSON: ${(error as Error).message}`) }\n return objectValue(value, label)\n}\n\nfunction objectValue(value: unknown, label: string): Record<string, any> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`${label} 必须是 JSON object`)\n }\n return value as Record<string, any>\n}\n\nfunction stringArray(value: unknown, label: string): string[] {\n if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {\n throw new Error(`${label} 必须是 string[]`)\n }\n return value as string[]\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value).digest('hex')\n}\n\nfunction atomicJson(path: string, value: unknown): void {\n const body = JSON.stringify(value, null, 2) + '\\n'\n const temporary = `${path}.${process.pid}.${Date.now()}.tmp`\n writeFileSync(temporary, body, { flag: 'wx' })\n renameSync(temporary, path)\n}\n\nexport function relativeBinding(binding: OpenCodeSkillBinding): Record<string, unknown> {\n return {\n ...binding,\n configPath: relative(binding.workspace, binding.configPath),\n skill: {\n ...binding.skill,\n path: relative(binding.workspace, binding.skill.path),\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { digest, stableStringify } from './events.js';
|
|
2
|
+
const MAX_TEXT = 8_000;
|
|
3
|
+
const MAX_RECENT = 50;
|
|
4
|
+
const MAX_BURST_MESSAGES = 5;
|
|
5
|
+
const BURST_WINDOW_MS = 120_000;
|
|
6
|
+
const BUILT_INS = [
|
|
7
|
+
{ id: 'quote', enrich: enrichQuote },
|
|
8
|
+
{ id: 'burst', enrich: enrichBurst },
|
|
9
|
+
{ id: 'identity', enrich: enrichIdentity },
|
|
10
|
+
];
|
|
11
|
+
export function sanitizePerceptionInput(value) {
|
|
12
|
+
if (value === undefined || value === null || value === '')
|
|
13
|
+
return {};
|
|
14
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
15
|
+
throw new Error('perception input 必须是 JSON object');
|
|
16
|
+
}
|
|
17
|
+
const input = value;
|
|
18
|
+
const allowed = new Set(['$schema', 'fetchedAt', 'quote', 'recentMessages', 'identity']);
|
|
19
|
+
const unknown = Object.keys(input).filter((key) => !allowed.has(key));
|
|
20
|
+
if (unknown.length)
|
|
21
|
+
throw new Error(`perception input 含未知字段: ${unknown.join(', ')}`);
|
|
22
|
+
if (input.$schema && input.$schema !== 'dingtalk-agent/perception-input@1') {
|
|
23
|
+
throw new Error('perception input schema 非法');
|
|
24
|
+
}
|
|
25
|
+
if (input.fetchedAt !== undefined && !validTime(input.fetchedAt)) {
|
|
26
|
+
throw new Error('perception fetchedAt 不是合法时间');
|
|
27
|
+
}
|
|
28
|
+
if (input.quote !== undefined) {
|
|
29
|
+
assertRecord(input.quote, 'quote');
|
|
30
|
+
requireStrings(input.quote, ['conversationId', 'messageId', 'text', 'source'], 'quote');
|
|
31
|
+
if (!['host-event', 'dws-readback'].includes(input.quote.source)) {
|
|
32
|
+
throw new Error('perception quote.source 非法');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (input.recentMessages !== undefined && !Array.isArray(input.recentMessages)) {
|
|
36
|
+
throw new Error('perception recentMessages 必须是 array');
|
|
37
|
+
}
|
|
38
|
+
if ((input.recentMessages || []).length > MAX_RECENT) {
|
|
39
|
+
throw new Error(`perception recentMessages 超过 ${MAX_RECENT} 条`);
|
|
40
|
+
}
|
|
41
|
+
for (const [index, message] of (input.recentMessages || []).entries()) {
|
|
42
|
+
assertRecord(message, `recentMessages[${index}]`);
|
|
43
|
+
requireStrings(message, ['conversationId', 'messageId', 'actorId', 'text', 'source'], `recentMessages[${index}]`);
|
|
44
|
+
if (!['host-event', 'dws-readback'].includes(message.source)) {
|
|
45
|
+
throw new Error(`perception recentMessages[${index}].source 非法`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (input.identity !== undefined) {
|
|
49
|
+
assertRecord(input.identity, 'identity');
|
|
50
|
+
requireStrings(input.identity, ['subjectId', 'displayName', 'source'], 'identity');
|
|
51
|
+
if (!['host-event', 'dws-contact'].includes(input.identity.source)) {
|
|
52
|
+
throw new Error('perception identity.source 非法');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// 只保留 JSON 值,避免宿主把函数、原型对象或隐式 getter 带进 durable inbox。
|
|
56
|
+
const clean = JSON.parse(JSON.stringify(input));
|
|
57
|
+
if (Buffer.byteLength(JSON.stringify(clean), 'utf8') > 128 * 1024) {
|
|
58
|
+
throw new Error('perception input 超过 128 KiB');
|
|
59
|
+
}
|
|
60
|
+
return clean;
|
|
61
|
+
}
|
|
62
|
+
export function enrichInvocation(event, invocation, value = {}, enrichers = BUILT_INS) {
|
|
63
|
+
const input = sanitizePerceptionInput(value);
|
|
64
|
+
const fetchedAt = validTime(input.fetchedAt) || event.occurredAt;
|
|
65
|
+
const items = Object.fromEntries(enrichers.map((enricher) => [
|
|
66
|
+
enricher.id, enricher.enrich(event, input, fetchedAt),
|
|
67
|
+
]));
|
|
68
|
+
const burstText = items.burst.status === 'available'
|
|
69
|
+
? String(items.burst.data?.effectiveText || event.message.text)
|
|
70
|
+
: event.message.text;
|
|
71
|
+
const unsigned = {
|
|
72
|
+
$schema: 'dingtalk-agent/enriched-invocation@1',
|
|
73
|
+
invocation,
|
|
74
|
+
message: { originalText: event.message.text, effectiveText: burstText },
|
|
75
|
+
enrichments: items,
|
|
76
|
+
missing: Object.entries(items)
|
|
77
|
+
.filter(([, item]) => item.status !== 'available')
|
|
78
|
+
.map(([name]) => name),
|
|
79
|
+
};
|
|
80
|
+
return { ...unsigned, hash: digest(stableStringify(unsigned)) };
|
|
81
|
+
}
|
|
82
|
+
export function verifyEnrichedInvocation(input, expectedHash = '') {
|
|
83
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
84
|
+
throw new Error('Enriched Invocation 必须是 object');
|
|
85
|
+
}
|
|
86
|
+
const value = input;
|
|
87
|
+
const { hash, ...unsigned } = value;
|
|
88
|
+
const actual = digest(stableStringify(unsigned));
|
|
89
|
+
if (!hash || hash !== actual || (expectedHash && hash !== expectedHash)) {
|
|
90
|
+
throw new Error('Enriched Invocation hash 不一致');
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
function enrichQuote(event, input, fetchedAt) {
|
|
95
|
+
const quote = input.quote;
|
|
96
|
+
if (!quote)
|
|
97
|
+
return missing('quote-not-provided', fetchedAt);
|
|
98
|
+
if (!quote.messageId || quote.conversationId !== event.conversation.id) {
|
|
99
|
+
return rejected('quote-scope-mismatch', quote.source || 'host', fetchedAt);
|
|
100
|
+
}
|
|
101
|
+
const text = truncate(quote.text);
|
|
102
|
+
return available(quote.source, fetchedAt, text.truncated, {
|
|
103
|
+
conversationId: quote.conversationId,
|
|
104
|
+
messageId: quote.messageId,
|
|
105
|
+
text: text.value,
|
|
106
|
+
occurredAt: validTime(quote.occurredAt),
|
|
107
|
+
author: {
|
|
108
|
+
id: clean(quote.author?.id),
|
|
109
|
+
displayName: clean(quote.author?.displayName),
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function enrichBurst(event, input, fetchedAt) {
|
|
114
|
+
const actorIds = new Set([
|
|
115
|
+
event.actor.id, event.actor.userId, event.actor.openDingTalkId,
|
|
116
|
+
].filter(Boolean));
|
|
117
|
+
const eventTime = timeMs(event.occurredAt);
|
|
118
|
+
const matching = (input.recentMessages || []).filter((item) => {
|
|
119
|
+
if (!item.messageId || item.messageId === event.message.id ||
|
|
120
|
+
item.conversationId !== event.conversation.id || !actorIds.has(item.actorId))
|
|
121
|
+
return false;
|
|
122
|
+
const itemTime = timeMs(item.occurredAt);
|
|
123
|
+
return !eventTime || !itemTime || (itemTime <= eventTime && eventTime - itemTime <= BURST_WINDOW_MS);
|
|
124
|
+
}).sort((a, b) => timeMs(a.occurredAt) - timeMs(b.occurredAt));
|
|
125
|
+
if (!matching.length)
|
|
126
|
+
return missing('no-same-actor-burst', fetchedAt);
|
|
127
|
+
const selected = matching.slice(-(MAX_BURST_MESSAGES - 1));
|
|
128
|
+
let truncated = selected.length !== matching.length;
|
|
129
|
+
const messages = selected.map((item) => {
|
|
130
|
+
const text = truncate(item.text);
|
|
131
|
+
truncated ||= text.truncated;
|
|
132
|
+
return {
|
|
133
|
+
messageId: item.messageId,
|
|
134
|
+
actorId: item.actorId,
|
|
135
|
+
actorName: clean(item.actorName),
|
|
136
|
+
text: text.value,
|
|
137
|
+
occurredAt: validTime(item.occurredAt),
|
|
138
|
+
source: item.source,
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
const current = truncate(event.message.text);
|
|
142
|
+
truncated ||= current.truncated;
|
|
143
|
+
messages.push({
|
|
144
|
+
messageId: event.message.id,
|
|
145
|
+
actorId: event.actor.id,
|
|
146
|
+
actorName: event.actor.displayName,
|
|
147
|
+
text: current.value,
|
|
148
|
+
occurredAt: event.occurredAt,
|
|
149
|
+
source: 'host-event',
|
|
150
|
+
});
|
|
151
|
+
return available('trusted-message-window', fetchedAt, truncated, {
|
|
152
|
+
windowMs: BURST_WINDOW_MS,
|
|
153
|
+
messages,
|
|
154
|
+
effectiveText: messages.map((item) => item.text).filter(Boolean).join('\n\n'),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function enrichIdentity(event, input, fetchedAt) {
|
|
158
|
+
const identity = input.identity;
|
|
159
|
+
const actorIds = [event.actor.id, event.actor.userId, event.actor.openDingTalkId].filter(Boolean);
|
|
160
|
+
if (identity && !actorIds.includes(identity.subjectId)) {
|
|
161
|
+
return rejected('identity-subject-mismatch', identity.source || 'host', fetchedAt, {
|
|
162
|
+
eventActor: event.actor,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (!identity) {
|
|
166
|
+
return available('event-envelope', fetchedAt, false, {
|
|
167
|
+
id: event.actor.id,
|
|
168
|
+
userId: event.actor.userId,
|
|
169
|
+
openDingTalkId: event.actor.openDingTalkId,
|
|
170
|
+
displayName: event.actor.displayName,
|
|
171
|
+
resolved: false,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return available(identity.source, fetchedAt, false, {
|
|
175
|
+
id: identity.subjectId,
|
|
176
|
+
userId: clean(identity.userId) || event.actor.userId,
|
|
177
|
+
openDingTalkId: clean(identity.openDingTalkId) || event.actor.openDingTalkId,
|
|
178
|
+
displayName: clean(identity.displayName) || event.actor.displayName,
|
|
179
|
+
department: clean(identity.department),
|
|
180
|
+
title: clean(identity.title),
|
|
181
|
+
resolved: true,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function available(source, fetchedAt, truncated, data) {
|
|
185
|
+
return { status: 'available', source, fetchedAt, confidence: 1,
|
|
186
|
+
truncated, reason: '', data };
|
|
187
|
+
}
|
|
188
|
+
function missing(reason, fetchedAt) {
|
|
189
|
+
return { status: 'missing', source: 'none', fetchedAt, confidence: 0,
|
|
190
|
+
truncated: false, reason, data: null };
|
|
191
|
+
}
|
|
192
|
+
function rejected(reason, source, fetchedAt, data = null) {
|
|
193
|
+
return { status: 'rejected', source, fetchedAt, confidence: 0,
|
|
194
|
+
truncated: false, reason, data };
|
|
195
|
+
}
|
|
196
|
+
function truncate(value) {
|
|
197
|
+
const text = String(value || '');
|
|
198
|
+
return text.length <= MAX_TEXT
|
|
199
|
+
? { value: text, truncated: false }
|
|
200
|
+
: { value: text.slice(0, MAX_TEXT), truncated: true };
|
|
201
|
+
}
|
|
202
|
+
function clean(value) {
|
|
203
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
204
|
+
}
|
|
205
|
+
function assertRecord(value, label) {
|
|
206
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
207
|
+
throw new Error(`perception ${label} 必须是 object`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function requireStrings(value, keys, label) {
|
|
211
|
+
const missing = keys.filter((key) => typeof value[key] !== 'string' || !value[key].trim());
|
|
212
|
+
if (missing.length)
|
|
213
|
+
throw new Error(`perception ${label} 缺少 ${missing.join(' / ')}`);
|
|
214
|
+
}
|
|
215
|
+
function validTime(value) {
|
|
216
|
+
if (!value)
|
|
217
|
+
return null;
|
|
218
|
+
const date = new Date(String(value));
|
|
219
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
220
|
+
}
|
|
221
|
+
function timeMs(value) {
|
|
222
|
+
const time = validTime(value);
|
|
223
|
+
return time ? new Date(time).getTime() : 0;
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=perception.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perception.js","sourceRoot":"","sources":["../../src/perception.ts"],"names":[],"mappings":"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","sourcesContent":["import { digest, stableStringify } from './events.js'\nimport type {\n EnrichedInvocation, EnrichmentItem, InvocationContext, NormalizedEvent,\n} from './types.js'\n\nexport interface TrustedPerceptionInput {\n $schema?: 'dingtalk-agent/perception-input@1'\n fetchedAt?: string\n quote?: {\n conversationId: string\n messageId: string\n text: string\n occurredAt?: string\n author?: { id?: string; displayName?: string }\n source: 'host-event' | 'dws-readback'\n }\n recentMessages?: Array<{\n conversationId: string\n messageId: string\n actorId: string\n actorName?: string\n text: string\n occurredAt?: string\n source: 'host-event' | 'dws-readback'\n }>\n identity?: {\n subjectId: string\n displayName: string\n userId?: string\n openDingTalkId?: string\n department?: string\n title?: string\n source: 'host-event' | 'dws-contact'\n }\n}\n\nexport interface PerceptionEnricher {\n id: 'quote' | 'burst' | 'identity'\n enrich(\n event: NormalizedEvent,\n input: TrustedPerceptionInput,\n fetchedAt: string | null,\n ): EnrichmentItem\n}\n\nconst MAX_TEXT = 8_000\nconst MAX_RECENT = 50\nconst MAX_BURST_MESSAGES = 5\nconst BURST_WINDOW_MS = 120_000\n\nconst BUILT_INS: PerceptionEnricher[] = [\n { id: 'quote', enrich: enrichQuote },\n { id: 'burst', enrich: enrichBurst },\n { id: 'identity', enrich: enrichIdentity },\n]\n\nexport function sanitizePerceptionInput(value: unknown): TrustedPerceptionInput {\n if (value === undefined || value === null || value === '') return {}\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error('perception input 必须是 JSON object')\n }\n const input = value as Record<string, any>\n const allowed = new Set(['$schema', 'fetchedAt', 'quote', 'recentMessages', 'identity'])\n const unknown = Object.keys(input).filter((key) => !allowed.has(key))\n if (unknown.length) throw new Error(`perception input 含未知字段: ${unknown.join(', ')}`)\n if (input.$schema && input.$schema !== 'dingtalk-agent/perception-input@1') {\n throw new Error('perception input schema 非法')\n }\n if (input.fetchedAt !== undefined && !validTime(input.fetchedAt)) {\n throw new Error('perception fetchedAt 不是合法时间')\n }\n if (input.quote !== undefined) {\n assertRecord(input.quote, 'quote')\n requireStrings(input.quote, ['conversationId', 'messageId', 'text', 'source'], 'quote')\n if (!['host-event', 'dws-readback'].includes(input.quote.source)) {\n throw new Error('perception quote.source 非法')\n }\n }\n if (input.recentMessages !== undefined && !Array.isArray(input.recentMessages)) {\n throw new Error('perception recentMessages 必须是 array')\n }\n if ((input.recentMessages || []).length > MAX_RECENT) {\n throw new Error(`perception recentMessages 超过 ${MAX_RECENT} 条`)\n }\n for (const [index, message] of (input.recentMessages || []).entries()) {\n assertRecord(message, `recentMessages[${index}]`)\n requireStrings(message, ['conversationId', 'messageId', 'actorId', 'text', 'source'],\n `recentMessages[${index}]`)\n if (!['host-event', 'dws-readback'].includes(message.source)) {\n throw new Error(`perception recentMessages[${index}].source 非法`)\n }\n }\n if (input.identity !== undefined) {\n assertRecord(input.identity, 'identity')\n requireStrings(input.identity, ['subjectId', 'displayName', 'source'], 'identity')\n if (!['host-event', 'dws-contact'].includes(input.identity.source)) {\n throw new Error('perception identity.source 非法')\n }\n }\n // 只保留 JSON 值,避免宿主把函数、原型对象或隐式 getter 带进 durable inbox。\n const clean = JSON.parse(JSON.stringify(input))\n if (Buffer.byteLength(JSON.stringify(clean), 'utf8') > 128 * 1024) {\n throw new Error('perception input 超过 128 KiB')\n }\n return clean as TrustedPerceptionInput\n}\n\nexport function enrichInvocation(\n event: NormalizedEvent,\n invocation: InvocationContext,\n value: unknown = {},\n enrichers: PerceptionEnricher[] = BUILT_INS,\n): EnrichedInvocation {\n const input = sanitizePerceptionInput(value)\n const fetchedAt = validTime(input.fetchedAt) || event.occurredAt\n const items = Object.fromEntries(enrichers.map((enricher) => [\n enricher.id, enricher.enrich(event, input, fetchedAt),\n ])) as EnrichedInvocation['enrichments']\n const burstText = items.burst.status === 'available'\n ? String(items.burst.data?.effectiveText || event.message.text)\n : event.message.text\n const unsigned = {\n $schema: 'dingtalk-agent/enriched-invocation@1' as const,\n invocation,\n message: { originalText: event.message.text, effectiveText: burstText },\n enrichments: items,\n missing: Object.entries(items)\n .filter(([, item]) => item.status !== 'available')\n .map(([name]) => name),\n }\n return { ...unsigned, hash: digest(stableStringify(unsigned)) }\n}\n\nexport function verifyEnrichedInvocation(\n input: unknown,\n expectedHash = '',\n): EnrichedInvocation {\n if (!input || typeof input !== 'object' || Array.isArray(input)) {\n throw new Error('Enriched Invocation 必须是 object')\n }\n const value = input as EnrichedInvocation\n const { hash, ...unsigned } = value\n const actual = digest(stableStringify(unsigned))\n if (!hash || hash !== actual || (expectedHash && hash !== expectedHash)) {\n throw new Error('Enriched Invocation hash 不一致')\n }\n return value\n}\n\nfunction enrichQuote(\n event: NormalizedEvent,\n input: TrustedPerceptionInput,\n fetchedAt: string | null,\n): EnrichmentItem {\n const quote = input.quote\n if (!quote) return missing('quote-not-provided', fetchedAt)\n if (!quote.messageId || quote.conversationId !== event.conversation.id) {\n return rejected('quote-scope-mismatch', quote.source || 'host', fetchedAt)\n }\n const text = truncate(quote.text)\n return available(quote.source, fetchedAt, text.truncated, {\n conversationId: quote.conversationId,\n messageId: quote.messageId,\n text: text.value,\n occurredAt: validTime(quote.occurredAt),\n author: {\n id: clean(quote.author?.id),\n displayName: clean(quote.author?.displayName),\n },\n })\n}\n\nfunction enrichBurst(\n event: NormalizedEvent,\n input: TrustedPerceptionInput,\n fetchedAt: string | null,\n): EnrichmentItem {\n const actorIds = new Set([\n event.actor.id, event.actor.userId, event.actor.openDingTalkId,\n ].filter(Boolean))\n const eventTime = timeMs(event.occurredAt)\n const matching = (input.recentMessages || []).filter((item) => {\n if (!item.messageId || item.messageId === event.message.id ||\n item.conversationId !== event.conversation.id || !actorIds.has(item.actorId)) return false\n const itemTime = timeMs(item.occurredAt)\n return !eventTime || !itemTime || (itemTime <= eventTime && eventTime - itemTime <= BURST_WINDOW_MS)\n }).sort((a, b) => timeMs(a.occurredAt) - timeMs(b.occurredAt))\n if (!matching.length) return missing('no-same-actor-burst', fetchedAt)\n const selected = matching.slice(-(MAX_BURST_MESSAGES - 1))\n let truncated = selected.length !== matching.length\n const messages = selected.map((item) => {\n const text = truncate(item.text)\n truncated ||= text.truncated\n return {\n messageId: item.messageId,\n actorId: item.actorId,\n actorName: clean(item.actorName),\n text: text.value,\n occurredAt: validTime(item.occurredAt),\n source: item.source,\n }\n })\n const current = truncate(event.message.text)\n truncated ||= current.truncated\n messages.push({\n messageId: event.message.id,\n actorId: event.actor.id,\n actorName: event.actor.displayName,\n text: current.value,\n occurredAt: event.occurredAt,\n source: 'host-event',\n })\n return available('trusted-message-window', fetchedAt, truncated, {\n windowMs: BURST_WINDOW_MS,\n messages,\n effectiveText: messages.map((item) => item.text).filter(Boolean).join('\\n\\n'),\n })\n}\n\nfunction enrichIdentity(\n event: NormalizedEvent,\n input: TrustedPerceptionInput,\n fetchedAt: string | null,\n): EnrichmentItem {\n const identity = input.identity\n const actorIds = [event.actor.id, event.actor.userId, event.actor.openDingTalkId].filter(Boolean)\n if (identity && !actorIds.includes(identity.subjectId)) {\n return rejected('identity-subject-mismatch', identity.source || 'host', fetchedAt, {\n eventActor: event.actor,\n })\n }\n if (!identity) {\n return available('event-envelope', fetchedAt, false, {\n id: event.actor.id,\n userId: event.actor.userId,\n openDingTalkId: event.actor.openDingTalkId,\n displayName: event.actor.displayName,\n resolved: false,\n })\n }\n return available(identity.source, fetchedAt, false, {\n id: identity.subjectId,\n userId: clean(identity.userId) || event.actor.userId,\n openDingTalkId: clean(identity.openDingTalkId) || event.actor.openDingTalkId,\n displayName: clean(identity.displayName) || event.actor.displayName,\n department: clean(identity.department),\n title: clean(identity.title),\n resolved: true,\n })\n}\n\nfunction available(\n source: string,\n fetchedAt: string | null,\n truncated: boolean,\n data: Record<string, unknown>,\n): EnrichmentItem {\n return { status: 'available', source, fetchedAt, confidence: 1,\n truncated, reason: '', data }\n}\n\nfunction missing(reason: string, fetchedAt: string | null): EnrichmentItem {\n return { status: 'missing', source: 'none', fetchedAt, confidence: 0,\n truncated: false, reason, data: null }\n}\n\nfunction rejected(\n reason: string,\n source: string,\n fetchedAt: string | null,\n data: Record<string, unknown> | null = null,\n): EnrichmentItem {\n return { status: 'rejected', source, fetchedAt, confidence: 0,\n truncated: false, reason, data }\n}\n\nfunction truncate(value: unknown): { value: string; truncated: boolean } {\n const text = String(value || '')\n return text.length <= MAX_TEXT\n ? { value: text, truncated: false }\n : { value: text.slice(0, MAX_TEXT), truncated: true }\n}\n\nfunction clean(value: unknown): string {\n return typeof value === 'string' ? value.trim() : ''\n}\n\nfunction assertRecord(value: unknown, label: string): asserts value is Record<string, any> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`perception ${label} 必须是 object`)\n }\n}\n\nfunction requireStrings(value: Record<string, any>, keys: string[], label: string): void {\n const missing = keys.filter((key) => typeof value[key] !== 'string' || !value[key].trim())\n if (missing.length) throw new Error(`perception ${label} 缺少 ${missing.join(' / ')}`)\n}\n\nfunction validTime(value: unknown): string | null {\n if (!value) return null\n const date = new Date(String(value))\n return Number.isNaN(date.getTime()) ? null : date.toISOString()\n}\n\nfunction timeMs(value: unknown): number {\n const time = validTime(value)\n return time ? new Date(time).getTime() : 0\n}\n"]}
|