@xdxer/dingtalk-agent 0.1.5-beta.2 → 0.1.5-beta.3
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 +15 -0
- package/README.en.md +6 -4
- package/README.md +6 -4
- package/dist/src/agent-audit.js +69 -55
- package/dist/src/agent-audit.js.map +1 -1
- package/dist/src/agent-enhance.js +49 -30
- package/dist/src/agent-enhance.js.map +1 -1
- package/dist/src/bootstrap.js +6 -2
- package/dist/src/bootstrap.js.map +1 -1
- package/dist/src/development-workspace.js +42 -15
- package/dist/src/development-workspace.js.map +1 -1
- package/dist/src/multica-deploy.js +27 -9
- package/dist/src/multica-deploy.js.map +1 -1
- package/dist/src/opencode-provider.js +17 -5
- package/dist/src/opencode-provider.js.map +1 -1
- package/dist/src/skills.js +2 -0
- package/dist/src/skills.js.map +1 -1
- package/dist/src/workspace.js +11 -6
- package/dist/src/workspace.js.map +1 -1
- package/docs/ARCHITECTURE.md +10 -7
- package/docs/INSTALLATION.md +1 -1
- package/docs/schemas/project.schema.json +5 -0
- package/examples/agents/README.md +8 -6
- package/examples/agents/fde-coach/AGENTS.md +2 -34
- package/examples/agents/fde-coach/agent/AGENTS.md +35 -0
- package/examples/agents/fde-coach/agent.bindings.json +10 -0
- package/examples/agents/release-manager/AGENTS.md +2 -34
- package/examples/agents/release-manager/agent/AGENTS.md +35 -0
- package/examples/agents/release-manager/agent.bindings.json +10 -0
- package/lab/project-workspace/fake-multica-provider.mjs +26 -7
- package/lab/robot-eval/suite.json +1 -1
- package/package.json +1 -1
- package/skills/core/dingtalk-agent-compose/SKILL.md +21 -15
- package/skills/core/dingtalk-agent-compose/assets/REPOSITORY.template.md +10 -0
- package/skills/core/dingtalk-agent-compose/assets/agent.bindings.dingtalk-doc.template.json +2 -2
- package/skills/core/dingtalk-agent-compose/assets/agent.bindings.local.template.json +2 -2
- package/skills/core/dingtalk-agent-compose/assets/hosts/opencode/opencode.template.json +2 -1
- package/skills/core/dingtalk-agent-compose/evals/evals.json +1 -1
- package/skills/core/dingtalk-agent-compose/references/agent-definition-contract.md +6 -6
- package/skills/core/dingtalk-agent-compose/references/host-loading-contract.md +10 -12
- package/skills/core/dingtalk-agent-compose/references/hosts/claude-code.md +13 -12
- package/skills/core/dingtalk-agent-compose/references/hosts/opencode.md +13 -12
- package/skills/core/dingtalk-basic-behavior/SKILL.md +9 -6
- package/skills/core/dingtalk-basic-behavior/references/perception-and-gates.md +2 -0
- /package/examples/agents/fde-coach/{skills → agent/skills}/fde-coach/SKILL.md +0 -0
- /package/examples/agents/release-manager/{skills → agent/skills}/release-manager/SKILL.md +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@xdxer/dingtalk-agent` are documented here. The project follows semantic versioning; prerelease entries describe release candidates and do not imply that Live canaries or registry publication have completed.
|
|
4
4
|
|
|
5
|
+
## 0.1.5-beta.3 - 2026-07-22
|
|
6
|
+
|
|
7
|
+
Agent delivery-package single-source layout and Multica Issue-Adapter receipt compatibility (PR #11). Ships on the `beta` dist-tag; `latest` stays at 0.1.4.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Agent delivery packages now have a single source of truth: new projects generate `agent/AGENTS.md` and `agent/skills/`, and Host exposure is materialized only into temporary isolated workspaces instead of being committed alongside the source. The project manifest gains `skillsRoot`; explicit paths and the legacy root layout keep working.
|
|
12
|
+
- Basic Behavior 0.11.11 makes IM history file-identifier recovery mandatory: rich-message recovery follows a fixed readback-before-gap-judgment order, and a fileId present in a history file card must go through `drive info`/read-only download instead of the Agent claiming it cannot read the file. A deterministic history-file-card fixture locks the behavior.
|
|
13
|
+
- Golden Path and load-evidence fixtures moved to the `agent/AGENTS.md` + `agent/skills` single-source layout; load evidence now verifies resolved instructions against the trusted `definitionInstruction` exactly, while the legacy implicit root-`AGENTS.md` rule remains accepted.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- The Multica deployment load-smoke gate no longer misjudges the platform's constrained Issue-Adapter receipts as arbitrary Shell. It accepts the combined here-doc write/comment/cleanup control-plane command only when content, filename and target Issue all match exactly, extracts the response evidence from that command, adds `result.md` to the approved result filenames (alongside `reply.md`/`result.json`), and allows `rm`/`rm -f` cleanup only for those three whitelisted files. DWS, network and any other Shell use still fail closed.
|
|
18
|
+
- Contract scenario 54's injected-timeout legs used a 1-second per-call provider budget, which a loaded machine could exhaust on an innocent preflight read (each fake-provider call spawns a Node process) before ever reaching the write the timeout was injected on — the pre-release `--full` run failed exactly this way while the isolated rerun passed. The runtime behaved correctly (read timeout → fail closed before any remote write); the eval budgets were raised to 4 seconds with the simulated hang raised to 8, and scenario 54's expectation texts in `evals.json` were reordered to match the runner's check order, which had drifted and mislabeled the failing assertion.
|
|
19
|
+
|
|
5
20
|
## 0.1.5-beta.2 - 2026-07-21
|
|
6
21
|
|
|
7
22
|
Multica native-Skill refactor and DingTalk IM context recovery (PR #9, #10). Ships on the `beta` dist-tag; `latest` stays at 0.1.4.
|
package/README.en.md
CHANGED
|
@@ -53,7 +53,9 @@ A model that talks is not a digital employee that works reliably. `dta` turns th
|
|
|
53
53
|
|
|
54
54
|
## What an Agent is made of
|
|
55
55
|
|
|
56
|
-
Four things: the **Definition** (`AGENTS.md` — who I am, where my duties stop), **shared behavior** (
|
|
56
|
+
Four things: the **Definition** (`agent/AGENTS.md` — who I am, where my duties stop), **shared behavior** (`agent/skills/dingtalk-basic-behavior`), **role capability** (`agent/skills/<role>/SKILL.md`), and **storage routing** (`agent.bindings.json` — optional; the same configuration can come from host context, environment variables, or a Workspace manifest). The repository-root `AGENTS.md` only governs coding agents and is never the remote System Prompt.
|
|
57
|
+
|
|
58
|
+
For a new project, `agent/` is the only managed-platform delivery boundary; do not commit duplicate Skills under root `skills/` or `.agents/skills/`. Existing code repositories can retain custom layouts by explicitly declaring `agent.definition` and `agent.skillsRoot` in `dingtalk-agent.json`.
|
|
57
59
|
|
|
58
60
|
Full composition, dependencies, and boundaries: [Architecture](docs/ARCHITECTURE.md#2-一个-agent-由什么构成模型-a).
|
|
59
61
|
|
|
@@ -104,7 +106,7 @@ The placeholders above are not yours to invent — each has a definite source:
|
|
|
104
106
|
| `<plan-id>` | Top-level `.planId` in the preceding `--dry-run --json` output. Any source or config change invalidates it, so re-run the dry run |
|
|
105
107
|
| `local-dev` / `multica-dev` | Workspace names **you** declare in `dingtalk-agent.json#workspaces` — not built-in values. Sample: [`lab/project-workspace/project.fixture.json`](lab/project-workspace/project.fixture.json) |
|
|
106
108
|
| `<promotion-id>` | `.promotionId` on the `promotion-receipt@1` produced by `dta promote --yes` |
|
|
107
|
-
| `<role-skill-name>` | The `name` in your `skills/<role>/SKILL.md` frontmatter |
|
|
109
|
+
| `<role-skill-name>` | The `name` in your `agent/skills/<role>/SKILL.md` frontmatter |
|
|
108
110
|
|
|
109
111
|
### Minimal runnable path (local only, three commands)
|
|
110
112
|
|
|
@@ -116,9 +118,9 @@ dta bootstrap --json # Was the body recognized? Check definition.status and
|
|
|
116
118
|
dta agent audit --json # Expect partial — the role semantics are still template text, which is correct
|
|
117
119
|
```
|
|
118
120
|
|
|
119
|
-
None of this needs a DingTalk account or a managed platform, and none of it has side effects. Fill in the real role semantics in `AGENTS.md`, run `agent audit` again, and the only remaining gap is `host.load-probe` — which needs an Agent Host, see the [Compose Skill](skills/core/dingtalk-agent-compose/SKILL.md). The third command group above only applies once you have a Multica binding and declared Workspaces.
|
|
121
|
+
None of this needs a DingTalk account or a managed platform, and none of it has side effects. Fill in the real role semantics in `agent/AGENTS.md`, run `agent audit` again, and the only remaining gap is `host.load-probe` — which needs an Agent Host, see the [Compose Skill](skills/core/dingtalk-agent-compose/SKILL.md). The third command group above only applies once you have a Multica binding and declared Workspaces.
|
|
120
122
|
|
|
121
|
-
When deploying to Multica, the remote System Prompt must equal the
|
|
123
|
+
When deploying to Multica, the remote System Prompt must equal the manifest-selected `agent/AGENTS.md` source. Deployment hashes, the Skill manifest, and assignment evidence stay in the plan/operation/Receipt control plane. Basic and Role Skills use the platform's native assignment instead of an extra startup prompt or startup Skill.
|
|
122
124
|
|
|
123
125
|
## Support
|
|
124
126
|
|
package/README.md
CHANGED
|
@@ -52,7 +52,9 @@ npm install --global --prefix "$HOME/.local" \
|
|
|
52
52
|
|
|
53
53
|
## 一个 Agent 由什么构成
|
|
54
54
|
|
|
55
|
-
四件东西:**本体**(`AGENTS.md`,我是谁、职责边界)、**公共行为**(`dingtalk-basic-behavior
|
|
55
|
+
四件东西:**本体**(`agent/AGENTS.md`,我是谁、职责边界)、**公共行为**(`agent/skills/dingtalk-basic-behavior`,何时响应/追问/沉默/收口)、**岗位能力**(`agent/skills/<role>/SKILL.md`)、**存储路由**(`agent.bindings.json`,可选——等价配置也可来自宿主 context、环境变量或 Workspace manifest)。仓库根 `AGENTS.md` 只约束 Coding Agent 开发,不属于远端本体。Agent Host、DWS、托管平台和事件源也不属于 Agent 交付包。
|
|
56
|
+
|
|
57
|
+
从零创建时,`agent/` 是 managed agent platform 的唯一同步边界,Skill 不能再复制到根 `skills/` 或 `.agents/skills/`。已有代码仓库不强制迁目录:在 `dingtalk-agent.json#agent` 显式声明 `definition` 与 `skillsRoot` 即可继续按原布局同步。
|
|
56
58
|
|
|
57
59
|
完整的构成、依赖与边界见 [Architecture](docs/ARCHITECTURE.md#2-一个-agent-由什么构成模型-a)。
|
|
58
60
|
|
|
@@ -103,7 +105,7 @@ dta observe --promotion-id <promotion-id> --input <observation.json> --dry-run -
|
|
|
103
105
|
| `<plan-id>` | 上一条 `--dry-run --json` 输出的顶层 `.planId`。源码或配置一变它就失效,必须重新 dry-run |
|
|
104
106
|
| `local-dev` / `multica-dev` | **你自己**在 `dingtalk-agent.json#workspaces` 里声明的 Workspace 名,不是内置值。样例见 [`lab/project-workspace/project.fixture.json`](lab/project-workspace/project.fixture.json) |
|
|
105
107
|
| `<promotion-id>` | `dta promote --yes` 产出的 `promotion-receipt@1` 的 `.promotionId` |
|
|
106
|
-
| `<role-skill-name>` | 你 `skills/<role>/SKILL.md` 里 frontmatter 的 `name` |
|
|
108
|
+
| `<role-skill-name>` | 你 `agent/skills/<role>/SKILL.md` 里 frontmatter 的 `name` |
|
|
107
109
|
|
|
108
110
|
### 最小可运行路径(纯本地,三条命令)
|
|
109
111
|
|
|
@@ -115,9 +117,9 @@ dta bootstrap --json # 本体被识别了吗:看 definition.status 与 st
|
|
|
115
117
|
dta agent audit --json # 预期 partial —— 岗位语义还是模板,这是正确结果
|
|
116
118
|
```
|
|
117
119
|
|
|
118
|
-
到这一步不需要钉钉账号、不需要托管平台、不产生任何副作用。把 `AGENTS.md` 里的岗位语义填成真的,再跑一次 `agent audit`,缺口就只剩 `host.load-probe`——那需要一个 Agent Host,见 [Compose Skill](skills/core/dingtalk-agent-compose/SKILL.md)。上面第 3 组云端命令要等你有了 Multica 归属和已声明的 Workspace 才用得上。
|
|
120
|
+
到这一步不需要钉钉账号、不需要托管平台、不产生任何副作用。把 `agent/AGENTS.md` 里的岗位语义填成真的,再跑一次 `agent audit`,缺口就只剩 `host.load-probe`——那需要一个 Agent Host,见 [Compose Skill](skills/core/dingtalk-agent-compose/SKILL.md)。上面第 3 组云端命令要等你有了 Multica 归属和已声明的 Workspace 才用得上。
|
|
119
121
|
|
|
120
|
-
发布到 Multica 时,远端 System Prompt
|
|
122
|
+
发布到 Multica 时,远端 System Prompt 必须与 manifest 指定的 `agent/AGENTS.md` 原文一致;部署哈希、Skill 清单和 assignment 证据只存在于 plan/operation/Receipt 控制面。Basic 与 Role Skills 通过平台原生 assignment 挂载,不再用额外启动 Prompt 或启动 Skill 承载机器协议。
|
|
121
123
|
|
|
122
124
|
## 支持范围
|
|
123
125
|
|
package/dist/src/agent-audit.js
CHANGED
|
@@ -12,6 +12,7 @@ import { resolvePackageRoot } from './package-root.js';
|
|
|
12
12
|
import { bundledSkillPath } from './skill-manager.js';
|
|
13
13
|
import { createIsolatedOpenCodeHost } from './opencode-isolation.js';
|
|
14
14
|
const COMPOSE_TEMPLATE_MARKER = /<(?:Agent 名称|岗位\/角色|人群、团队或工作场域|长期目标|主动负责的事项|可观察的交付物|明确不做或必须升级确认的事项|岗位稳定的输入、判断、交付与验收范式|何时自行推进、何时交给谁|本体级常见错误 [12]|纠偏动作 [12]|Role Skill names|role-skill-name|Role Skill 名称|岗位职责或工作流|领域判断、输入与交付物|相邻职责或明确交接边界|岗位特有的禁区或必须升级条件|领域输入|领域方法\/SOP|领域常见错误|领域纠偏动作|可观察结果 [12]|route)>/u;
|
|
15
|
+
const BASIC = 'dingtalk-basic-behavior';
|
|
15
16
|
const BASIC_INSTRUCTION = '.agents/skills/dingtalk-basic-behavior/SKILL.md';
|
|
16
17
|
const DEFINITION_PROBE_PROMPT = '这是 Agent Definition 继承探针。只依据已强制加载的角色宪法末尾注释回答;' +
|
|
17
18
|
'严格输出纯文本 dta-definition-probe=<值>,若看不到则输出纯文本 ' +
|
|
@@ -77,28 +78,33 @@ export function auditAgent(root, options = {}) {
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
const basic = readCanonicalBasicSkill();
|
|
80
|
-
const
|
|
81
|
+
const packagedBasic = definition.skills.find((item) => item.name === BASIC);
|
|
82
|
+
const basicExposurePath = packagedBasic?.path || join(rootReal, '.agents', 'skills', BASIC, 'SKILL.md');
|
|
81
83
|
const basicExposure = inspectSkill(basicExposurePath);
|
|
82
84
|
const basicExposureTree = inspectSkillTree(basicExposurePath);
|
|
83
85
|
add('host.basic-exposure', 'host', basicExposure.exists && basicExposure.name === basic.name &&
|
|
84
86
|
basicExposure.version === basic.version && basicExposure.hash === basic.hash &&
|
|
85
87
|
basicExposureTree.hash === basic.treeHash ? 'pass' : 'fail', true, basicExposure.exists && basicExposure.hash === basic.hash &&
|
|
86
88
|
basicExposureTree.hash === basic.treeHash
|
|
87
|
-
? '
|
|
88
|
-
: '
|
|
89
|
+
? 'Agent package 的 Basic Skill 全树与 canonical source 一致'
|
|
90
|
+
: 'Agent package 的 Basic Skill 入口、references 或 assets 缺失或发生漂移', { expected: basic, actual: { ...basicExposure, treeHash: basicExposureTree.hash,
|
|
89
91
|
files: basicExposureTree.files, error: basicExposureTree.error } });
|
|
90
92
|
const config = inspectOpenCodeConfig(rootReal);
|
|
91
93
|
const definitionInstruction = definition.body?.path
|
|
92
94
|
? relative(rootReal, definition.body.path).split('\\').join('/') : '';
|
|
93
95
|
const definitionInstructionCount = countInstructionsForPath(rootReal, config.instructions, definitionInstruction);
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
+
const definitionInstructionExpected = definitionInstruction === 'AGENTS.md' ? 0 : 1;
|
|
97
|
+
const definitionRuleReady = !config.error && Boolean(definitionInstruction) &&
|
|
98
|
+
definitionInstructionCount === definitionInstructionExpected;
|
|
96
99
|
add('host.definition-rule', 'host', definitionRuleReady
|
|
97
100
|
? 'pass' : 'fail', true, definitionRuleReady
|
|
98
|
-
?
|
|
99
|
-
|
|
101
|
+
? definitionInstruction === 'AGENTS.md'
|
|
102
|
+
? 'OpenCode 使用项目根开发兼容模式的原生 Agent Definition'
|
|
103
|
+
: 'OpenCode 以唯一 custom instruction 加载独立 Agent Definition'
|
|
104
|
+
: 'Agent Definition 未形成唯一加载路径', { path: config.path, error: config.error, instruction: definitionInstruction,
|
|
100
105
|
instructionCount: definitionInstructionCount });
|
|
101
|
-
const
|
|
106
|
+
const basicInstruction = relative(rootReal, basicExposurePath).split('\\').join('/');
|
|
107
|
+
const instructionCount = countInstructionsForPath(rootReal, config.instructions, basicInstruction);
|
|
102
108
|
add('host.basic-instruction', 'host', !config.error && instructionCount === 1 &&
|
|
103
109
|
config.permissions['dingtalk-basic-behavior'] === 'allow' ? 'pass' : 'fail', true, !config.error && instructionCount === 1 &&
|
|
104
110
|
config.permissions['dingtalk-basic-behavior'] === 'allow'
|
|
@@ -107,12 +113,10 @@ export function auditAgent(root, options = {}) {
|
|
|
107
113
|
permission: config.permissions['dingtalk-basic-behavior'] || '' });
|
|
108
114
|
for (const name of requiredSkills) {
|
|
109
115
|
const source = roleSkills.get(name);
|
|
110
|
-
const exposure = inspectSkill(join(rootReal, '.agents', 'skills', name, 'SKILL.md'));
|
|
111
116
|
const permission = config.permissions[name] || '';
|
|
112
|
-
const matched = Boolean(source?.hash &&
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
: `Role Skill ${name} exposure、hash 或 permission 不一致`, { sourceHash: source?.hash || '', exposure, permission });
|
|
117
|
+
const matched = Boolean(source?.hash && source.path && permission === 'allow');
|
|
118
|
+
add(`host.role-exposure.${name}`, 'host', matched ? 'pass' : 'fail', true, matched ? `Role Skill ${name} 单一发布源与 Host permission 一致`
|
|
119
|
+
: `Role Skill ${name} 发布源或 permission 不一致`, { sourceHash: source?.hash || '', sourcePath: source?.path || '', permission });
|
|
116
120
|
}
|
|
117
121
|
const staticPrerequisitesReady = checks
|
|
118
122
|
.filter((item) => item.blocking)
|
|
@@ -129,7 +133,7 @@ export function auditAgent(root, options = {}) {
|
|
|
129
133
|
if (!options.yes)
|
|
130
134
|
throw new Error('agent audit --verify-load 会调用 OpenCode;必须同时传 --yes');
|
|
131
135
|
if (staticPrerequisitesReady) {
|
|
132
|
-
const execution = executeLoadAudit(rootReal, options, loadTargetHash, definitionInstruction, effectiveModel, () => currentLoadTargetHash(rootReal, options, requiredSkills, effectiveModel));
|
|
136
|
+
const execution = executeLoadAudit(rootReal, options, loadTargetHash, definitionInstruction, basicInstruction, effectiveModel, () => currentLoadTargetHash(rootReal, options, requiredSkills, effectiveModel));
|
|
133
137
|
loadReport = execution.report;
|
|
134
138
|
loadReportPath = execution.evidencePath;
|
|
135
139
|
verifiedReportPath = execution.reportPath;
|
|
@@ -374,17 +378,14 @@ function currentLoadTargetHash(root, options, requiredSkills, model) {
|
|
|
374
378
|
const hydrated = bootstrap(root, { ...(options.bootstrap || {}), deferRemote: true });
|
|
375
379
|
const definition = hydrated.definition;
|
|
376
380
|
const basic = readCanonicalBasicSkill();
|
|
377
|
-
const
|
|
381
|
+
const packagedBasic = definition.skills.find((item) => item.name === BASIC);
|
|
382
|
+
const exposedPath = packagedBasic?.path || join(root, '.agents', 'skills', BASIC, 'SKILL.md');
|
|
378
383
|
const exposed = inspectSkill(exposedPath);
|
|
379
384
|
const exposedTree = inspectSkillTree(exposedPath);
|
|
380
385
|
const config = inspectOpenCodeConfig(root);
|
|
381
386
|
const roleSkills = new Map(definition.skills
|
|
382
387
|
.filter((item) => item.name !== 'dingtalk-basic-behavior')
|
|
383
388
|
.map((item) => [item.name, item]));
|
|
384
|
-
const roleExposureHashes = Object.fromEntries(requiredSkills.map((name) => [
|
|
385
|
-
name,
|
|
386
|
-
inspectSkill(join(root, '.agents', 'skills', name, 'SKILL.md')).hash || '',
|
|
387
|
-
]));
|
|
388
389
|
const roleSourceHashes = Object.fromEntries(requiredSkills.map((name) => [
|
|
389
390
|
name, roleSkills.get(name)?.hash || '',
|
|
390
391
|
]));
|
|
@@ -402,11 +403,10 @@ function currentLoadTargetHash(root, options, requiredSkills, model) {
|
|
|
402
403
|
opencodeConfigHash: config.hash,
|
|
403
404
|
requiredSkills,
|
|
404
405
|
roleSourceHashes,
|
|
405
|
-
roleExposureHashes,
|
|
406
406
|
model,
|
|
407
407
|
}));
|
|
408
408
|
}
|
|
409
|
-
function executeLoadAudit(root, options, targetHash, definitionInstruction, model, revalidate) {
|
|
409
|
+
function executeLoadAudit(root, options, targetHash, definitionInstruction, basicInstruction, model, revalidate) {
|
|
410
410
|
const runId = `agent_audit_${Date.now()}_${randomUUID().slice(0, 8)}`;
|
|
411
411
|
const outputInspection = inspectContainedPath(root, options.out || join('.dingtalk-agent', 'agent-audit', 'results', runId));
|
|
412
412
|
const outputRoot = outputInspection.path;
|
|
@@ -424,8 +424,8 @@ function executeLoadAudit(root, options, targetHash, definitionInstruction, mode
|
|
|
424
424
|
const host = createIsolatedOpenCodeHost(model.split('/')[0]);
|
|
425
425
|
try {
|
|
426
426
|
const command = process.env.DTA_OPENCODE_BIN || 'opencode';
|
|
427
|
-
const sourcePreflight = inspectSourceOpenCodeRuntime(command, root, host.environment, definitionInstruction);
|
|
428
|
-
materializeAuditProbeWorkspace(root, auditWorkspace, definitionInstruction);
|
|
427
|
+
const sourcePreflight = inspectSourceOpenCodeRuntime(command, root, host.environment, definitionInstruction, basicInstruction);
|
|
428
|
+
materializeAuditProbeWorkspace(root, auditWorkspace, definitionInstruction, basicInstruction);
|
|
429
429
|
const suitePath = join(auditWorkspace, 'suite.json');
|
|
430
430
|
writeAtomic(suitePath, JSON.stringify({
|
|
431
431
|
$schema: 'dingtalk-agent/robot-eval-suite@1',
|
|
@@ -494,19 +494,19 @@ function executeLoadAudit(root, options, targetHash, definitionInstruction, mode
|
|
|
494
494
|
rmSync(auditWorkspace, { recursive: true, force: true });
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
|
-
function materializeAuditProbeWorkspace(sourceRoot, targetRoot, definitionInstruction) {
|
|
498
|
-
if (definitionInstruction !== 'AGENTS.md') {
|
|
499
|
-
throw new Error('OpenCode 原生 Definition 探针只接受项目根 AGENTS.md');
|
|
500
|
-
}
|
|
497
|
+
function materializeAuditProbeWorkspace(sourceRoot, targetRoot, definitionInstruction, basicInstruction) {
|
|
501
498
|
mkdirSync(targetRoot, { recursive: true });
|
|
502
|
-
writeAtomic(join(targetRoot,
|
|
503
|
-
const sourceBasic = join(sourceRoot,
|
|
499
|
+
writeAtomic(join(targetRoot, definitionInstruction), readFileSync(join(sourceRoot, definitionInstruction), 'utf8'));
|
|
500
|
+
const sourceBasic = dirname(join(sourceRoot, basicInstruction));
|
|
504
501
|
const targetBasic = join(targetRoot, '.agents', 'skills', 'dingtalk-basic-behavior');
|
|
505
502
|
mkdirSync(dirname(targetBasic), { recursive: true });
|
|
506
503
|
cpSync(sourceBasic, targetBasic, { recursive: true });
|
|
507
504
|
const config = {
|
|
508
505
|
$schema: 'https://opencode.ai/config.json',
|
|
509
|
-
instructions: [
|
|
506
|
+
instructions: [
|
|
507
|
+
...(definitionInstruction === 'AGENTS.md' ? [] : [definitionInstruction]),
|
|
508
|
+
BASIC_INSTRUCTION,
|
|
509
|
+
],
|
|
510
510
|
permission: { skill: { 'dingtalk-basic-behavior': 'allow' } },
|
|
511
511
|
plugin: [],
|
|
512
512
|
mcp: {},
|
|
@@ -515,19 +515,21 @@ function materializeAuditProbeWorkspace(sourceRoot, targetRoot, definitionInstru
|
|
|
515
515
|
};
|
|
516
516
|
writeAtomic(join(targetRoot, 'opencode.json'), JSON.stringify(config, null, 2) + '\n');
|
|
517
517
|
}
|
|
518
|
-
function inspectSourceOpenCodeRuntime(command, root, environment, definitionInstruction) {
|
|
518
|
+
function inspectSourceOpenCodeRuntime(command, root, environment, definitionInstruction, basicInstruction) {
|
|
519
519
|
const parent = mkdtempSync(join(tmpdir(), 'dta-source-config-preflight-'));
|
|
520
520
|
const workspace = join(parent, 'workspace');
|
|
521
521
|
let stdout = '';
|
|
522
522
|
try {
|
|
523
523
|
mkdirSync(workspace, { recursive: true });
|
|
524
524
|
cpSync(join(root, 'opencode.json'), join(workspace, 'opencode.json'));
|
|
525
|
-
if (existsSync(join(root,
|
|
526
|
-
|
|
525
|
+
if (existsSync(join(root, definitionInstruction))) {
|
|
526
|
+
const target = join(workspace, definitionInstruction);
|
|
527
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
528
|
+
cpSync(join(root, definitionInstruction), target);
|
|
527
529
|
}
|
|
528
|
-
const basic = join(root,
|
|
530
|
+
const basic = dirname(join(root, basicInstruction));
|
|
529
531
|
if (existsSync(basic)) {
|
|
530
|
-
const target = join(workspace,
|
|
532
|
+
const target = dirname(join(workspace, basicInstruction));
|
|
531
533
|
mkdirSync(dirname(target), { recursive: true });
|
|
532
534
|
cpSync(basic, target, {
|
|
533
535
|
recursive: true,
|
|
@@ -564,9 +566,10 @@ function inspectSourceOpenCodeRuntime(command, root, environment, definitionInst
|
|
|
564
566
|
throw new Error('OpenCode 原项目 resolved instructions 不是 string array');
|
|
565
567
|
}
|
|
566
568
|
const definitionCount = countInstructionsForPath(root, instructions, definitionInstruction);
|
|
567
|
-
const basicCount = countInstructionsForPath(root, instructions,
|
|
568
|
-
|
|
569
|
-
|
|
569
|
+
const basicCount = countInstructionsForPath(root, instructions, basicInstruction);
|
|
570
|
+
const expectedDefinitionCount = definitionInstruction === 'AGENTS.md' ? 0 : 1;
|
|
571
|
+
if (definitionCount !== expectedDefinitionCount || basicCount !== 1) {
|
|
572
|
+
throw new Error(`OpenCode 原项目 resolved config 未形成唯一 Agent Definition + Basic: ` +
|
|
570
573
|
`definition=${definitionCount} basic=${basicCount}`);
|
|
571
574
|
}
|
|
572
575
|
if (value.agent?.['dta-eval'] !== undefined) {
|
|
@@ -625,7 +628,10 @@ function executeInheritanceProbe(sourceRoot, model, command, timeoutMs, reportEv
|
|
|
625
628
|
!Array.isArray(sourceConfig.permission) ? sourceConfig.permission : {};
|
|
626
629
|
const skill = permission.skill && typeof permission.skill === 'object' &&
|
|
627
630
|
!Array.isArray(permission.skill) ? permission.skill : {};
|
|
628
|
-
sourceConfig.instructions = [
|
|
631
|
+
sourceConfig.instructions = [
|
|
632
|
+
...(definitionInstruction === 'AGENTS.md' ? [] : [definitionInstruction]),
|
|
633
|
+
BASIC_INSTRUCTION,
|
|
634
|
+
];
|
|
629
635
|
delete sourceConfig.provider;
|
|
630
636
|
sourceConfig.plugin = [];
|
|
631
637
|
sourceConfig.mcp = {};
|
|
@@ -665,7 +671,7 @@ function executeInheritanceProbe(sourceRoot, model, command, timeoutMs, reportEv
|
|
|
665
671
|
const definitionRaw = persistProbeExecution(reportEvidenceRoot, 'definition', definitionExecution);
|
|
666
672
|
const referenceRaw = persistProbeExecution(reportEvidenceRoot, 'reference', referenceExecution);
|
|
667
673
|
const result = {
|
|
668
|
-
passed: definitionInstruction
|
|
674
|
+
passed: Boolean(definitionInstruction) && basicResolvedCount === 1 &&
|
|
669
675
|
definitionPermissionPassed && referencePermissionPassed &&
|
|
670
676
|
definitionExecution.exitCode === 0 && definitionExecution.directoryMatched &&
|
|
671
677
|
definitionExecution.toolCalls === 0 &&
|
|
@@ -830,7 +836,7 @@ function validRandomProbe(value, name) {
|
|
|
830
836
|
const count = name === 'dta-reference-probe' ? 2 : 1;
|
|
831
837
|
return new RegExp(`^${name}=${uuid}${count === 2 ? `:${uuid}` : ''}$`).test(value);
|
|
832
838
|
}
|
|
833
|
-
function verifyLoadProbeSet(root, reportPath, report, primaryName, comparisonName, basic, expectedRuns) {
|
|
839
|
+
function verifyLoadProbeSet(root, reportPath, report, primaryName, comparisonName, basic, definitionInstruction, expectedRuns) {
|
|
834
840
|
const all = Array.isArray(report.loadProbes) ? report.loadProbes : [];
|
|
835
841
|
const declaredRuns = Number(report.summary?.runsPerConfiguration);
|
|
836
842
|
const primary = all.filter((item) => item.configuration === primaryName);
|
|
@@ -841,7 +847,12 @@ function verifyLoadProbeSet(root, reportPath, report, primaryName, comparisonNam
|
|
|
841
847
|
items.every((item) => Number.isInteger(item.runNumber) &&
|
|
842
848
|
item.runNumber >= 1 && item.runNumber <= expectedRuns);
|
|
843
849
|
const common = (item) => {
|
|
844
|
-
const expectedInstructions = item.expectsLoad === true
|
|
850
|
+
const expectedInstructions = item.expectsLoad === true
|
|
851
|
+
? [
|
|
852
|
+
BASIC_INSTRUCTION,
|
|
853
|
+
...(definitionInstruction === 'AGENTS.md' ? [] : [definitionInstruction]),
|
|
854
|
+
].sort()
|
|
855
|
+
: [];
|
|
845
856
|
return loadProbeCorePassed(item, version) &&
|
|
846
857
|
stableStringify(item.resolvedInstructions) === stableStringify(expectedInstructions) &&
|
|
847
858
|
verifyLoadProbeRaw(root, reportPath, item, String(report.engine?.model || ''));
|
|
@@ -954,7 +965,7 @@ function readLoadEvidence(root, input, expectedTargetHash) {
|
|
|
954
965
|
function verifyLoadReport(root, reportPath, report, basic, definitionInstruction, model, currentHostVersion) {
|
|
955
966
|
const configuration = 'with_skill';
|
|
956
967
|
const comparison = 'without_skill';
|
|
957
|
-
const probeSet = verifyLoadProbeSet(root, reportPath, report, configuration, comparison, basic, 1);
|
|
968
|
+
const probeSet = verifyLoadProbeSet(root, reportPath, report, configuration, comparison, basic, definitionInstruction, 1);
|
|
958
969
|
const probes = probeSet.primary;
|
|
959
970
|
const source = report.skills?.[configuration];
|
|
960
971
|
const gate = report.summary?.configurationLoadGates?.[configuration];
|
|
@@ -966,7 +977,7 @@ function verifyLoadReport(root, reportPath, report, basic, definitionInstruction
|
|
|
966
977
|
Array.isArray(definition.toolNames) && definition.toolNames.length === 0 &&
|
|
967
978
|
Array.isArray(definition.toolPaths) && definition.toolPaths.length === 0 &&
|
|
968
979
|
Array.isArray(definition.toolTrace) && definition.toolTrace.length === 0 &&
|
|
969
|
-
definition.nativeRule ===
|
|
980
|
+
definition.nativeRule === definitionInstruction && Boolean(definitionInstruction) &&
|
|
970
981
|
definition.permissionPassed === true &&
|
|
971
982
|
zeroToolPermission(definition.evalAgent || {});
|
|
972
983
|
const referenceTrace = Array.isArray(reference.toolTrace) ? reference.toolTrace : [];
|
|
@@ -1017,7 +1028,7 @@ function verifyLoadReport(root, reportPath, report, basic, definitionInstruction
|
|
|
1017
1028
|
baselineAntiGuessProbes: probeSet.passedComparison,
|
|
1018
1029
|
expectedRunsPerConfiguration: probeSet.expectedRuns,
|
|
1019
1030
|
definitionInstruction,
|
|
1020
|
-
definitionNativeRule: definition.nativeRule ===
|
|
1031
|
+
definitionNativeRule: definition.nativeRule === definitionInstruction,
|
|
1021
1032
|
totalProbes: probes.length,
|
|
1022
1033
|
definitionProbePassed: definitionPassed,
|
|
1023
1034
|
referenceProbePassed: referencePassed,
|
|
@@ -1037,8 +1048,11 @@ function verifySourceConfigEvidence(root, reportPath, summary, evidence) {
|
|
|
1037
1048
|
return false;
|
|
1038
1049
|
const value = summary;
|
|
1039
1050
|
const manifest = evidence;
|
|
1040
|
-
|
|
1041
|
-
|
|
1051
|
+
const expectedDefinitionCount = value.definitionInstruction === 'AGENTS.md' ? 0 : 1;
|
|
1052
|
+
if (value.passed !== true || typeof value.definitionInstruction !== 'string' ||
|
|
1053
|
+
!value.definitionInstruction ||
|
|
1054
|
+
value.definitionInstructionCount !== expectedDefinitionCount ||
|
|
1055
|
+
value.basicInstructionCount !== 1 ||
|
|
1042
1056
|
typeof value.rawHash !== 'string' || !value.rawHash ||
|
|
1043
1057
|
manifest.rawHash !== value.rawHash)
|
|
1044
1058
|
return false;
|
|
@@ -1227,12 +1241,12 @@ function repairFor(id) {
|
|
|
1227
1241
|
if (id === 'definition.semantic-contract')
|
|
1228
1242
|
return {
|
|
1229
1243
|
id, why: '结构化模板不能替代真实的本体定义、岗位底线、做事范式和常犯错误。',
|
|
1230
|
-
actions: ['评审并替换
|
|
1244
|
+
actions: ['评审并替换 manifest 中 agent.definition 指向文件的所有 <...> 占位符,再复跑 audit。'],
|
|
1231
1245
|
};
|
|
1232
1246
|
if (id === 'definition.body')
|
|
1233
1247
|
return {
|
|
1234
1248
|
id, why: 'Agent 必须有长期定义、岗位底线、做事范式和常犯错误。',
|
|
1235
|
-
actions: ['从 compose Skill 的 AGENTS.template.md 创建 AGENTS.md
|
|
1249
|
+
actions: ['从 compose Skill 的 AGENTS.template.md 创建 agent/AGENTS.md,并由 manifest 的 agent.definition 指向它,再复跑 audit。'],
|
|
1236
1250
|
};
|
|
1237
1251
|
if (id === 'storage.memory')
|
|
1238
1252
|
return {
|
|
@@ -1257,22 +1271,22 @@ function repairFor(id) {
|
|
|
1257
1271
|
if (id.startsWith('skill.role.') && id.endsWith('.semantic'))
|
|
1258
1272
|
return {
|
|
1259
1273
|
id, why: 'Role Skill 必须包含真实的领域输入、方法、产物和验收,而不是模板提示。',
|
|
1260
|
-
actions: [`评审并替换
|
|
1274
|
+
actions: [`评审并替换 agent.skillsRoot 下 ${id.slice('skill.role.'.length, -'.semantic'.length)}/SKILL.md 中所有 <...> 占位符。`],
|
|
1261
1275
|
};
|
|
1262
1276
|
if (id.startsWith('skill.role.'))
|
|
1263
1277
|
return {
|
|
1264
1278
|
id, why: '领域职责需要独立 Role Skill,不能写回 Basic Behavior。',
|
|
1265
|
-
actions: [
|
|
1279
|
+
actions: [`在 manifest 声明的 agent.skillsRoot 下创建 ${id.slice('skill.role.'.length)}/SKILL.md,并确保目录名等于 frontmatter name。`],
|
|
1266
1280
|
};
|
|
1267
1281
|
if (id === 'host.basic-exposure')
|
|
1268
1282
|
return {
|
|
1269
1283
|
id, why: 'OpenCode 只能从项目 exposure 完整继承本次装配的 Basic Skill 入口、references 与 assets。',
|
|
1270
|
-
actions: ['把 canonical dingtalk-basic-behavior
|
|
1284
|
+
actions: ['把 canonical dingtalk-basic-behavior 全树放入 agent.skillsRoot;由 dta 在隔离 Host 工作区临时物化 exposure。'],
|
|
1271
1285
|
};
|
|
1272
1286
|
if (id === 'host.definition-rule')
|
|
1273
1287
|
return {
|
|
1274
|
-
id, why: '
|
|
1275
|
-
actions: ['
|
|
1288
|
+
id, why: 'Agent Definition 与仓库开发约束必须分离,重复 custom instruction 会形成冗余通道。',
|
|
1289
|
+
actions: ['让 managed Host 只加载一次 manifest 的 agent.definition;仓库根 AGENTS.md 仅保留 Coding Agent 开发约束。'],
|
|
1276
1290
|
};
|
|
1277
1291
|
if (id === 'host.basic-instruction')
|
|
1278
1292
|
return {
|
|
@@ -1282,7 +1296,7 @@ function repairFor(id) {
|
|
|
1282
1296
|
if (id.startsWith('host.role-exposure.'))
|
|
1283
1297
|
return {
|
|
1284
1298
|
id, why: 'Role Skill 的发布源、Host exposure 与权限必须指向同一版本。',
|
|
1285
|
-
actions: [
|
|
1299
|
+
actions: [`修复 agent.skillsRoot 下 ${id.slice('host.role-exposure.'.length)} 的唯一发布源;由 dta 临时物化 Host exposure、校验 hash 并设置 permission=allow。`],
|
|
1286
1300
|
};
|
|
1287
1301
|
if (id === 'host.load-probe')
|
|
1288
1302
|
return {
|