@xdxer/dingtalk-agent 0.1.5-beta.11 → 0.1.5-beta.13
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 +16 -0
- package/dist/bin/dingtalk-agent.js +6 -0
- package/dist/bin/dingtalk-agent.js.map +1 -1
- package/dist/src/multica-deploy.js +77 -5
- package/dist/src/multica-deploy.js.map +1 -1
- package/dist/src/predicate-registry.js +187 -0
- package/dist/src/predicate-registry.js.map +1 -0
- package/dist/src/promotion.js +1 -1
- package/dist/src/promotion.js.map +1 -1
- package/dist/src/schedule-plan.js +27 -3
- package/dist/src/schedule-plan.js.map +1 -1
- package/docs/schemas/multica-deployment-plan.schema.json +14 -1
- package/docs/schemas/multica-deployment-receipt.schema.json +14 -1
- package/lab/robot-eval/suite.json +1 -1
- package/package.json +1 -1
- package/skills/core/dta-agent-compose/SKILL.md +1 -1
- package/skills/core/dta-agent-compose/references/drive-and-schedules.md +3 -1
- package/skills/core/dta-basic-behavior/SKILL.md +1 -1
- package/skills/core/dta-basic-behavior/references/task-lifecycle.md +6 -1
- package/skills/platforms/multica-dingtalk/dta-ops-multica/SKILL.md +25 -17
- package/skills/platforms/multica-dingtalk/dta-ops-multica/scripts/multica_ext.py +74 -8
- package/dist/src/map.js +0 -157
- package/dist/src/map.js.map +0 -1
|
@@ -4,6 +4,8 @@ import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSyn
|
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
6
6
|
import { WORKSPACE_STATE_ROOT, inspectAgentProject, projectSkillRef, showDevelopmentWorkspace, } from './development-workspace.js';
|
|
7
|
+
import { PREDICATE_REGISTRY_PATH, buildPredicateRegistry, predicateRegistryOwner, serializeRegistry, } from './predicate-registry.js';
|
|
8
|
+
import { completionPredicateId } from './schedule-plan.js';
|
|
7
9
|
import { digest, stableStringify } from './events.js';
|
|
8
10
|
import { MULTICA_EVIDENCE_ROOT, emitProviderDiagnostic, inspectMulticaWorkspace, planMulticaWorkspace, sanitizeProviderEnv, statusMulticaWorkspace, } from './multica-provider.js';
|
|
9
11
|
import { allRuntimeVocabularies, commandInvokesDws, forbiddenSmokeTool, skillsLoadedInContent, vocabulariesForRuntimeProvider, } from './multica-runtime-vocabulary.js';
|
|
@@ -368,6 +370,7 @@ export function planMulticaDeployment(start, name, options = {}) {
|
|
|
368
370
|
inspectionId,
|
|
369
371
|
desiredHash: workspace.desiredHash,
|
|
370
372
|
deploymentHash: source.deploymentHash,
|
|
373
|
+
predicateRegistry: source.predicateRegistry,
|
|
371
374
|
expected: {
|
|
372
375
|
workspaceId: readPlan.expected.workspaceId,
|
|
373
376
|
runtimeId: readPlan.expected.runtimeId,
|
|
@@ -565,6 +568,7 @@ function executeApply(root, workspace, source, plan, preflight, operation, comma
|
|
|
565
568
|
inspectionEvidencePath: readback.evidencePath,
|
|
566
569
|
desiredHash: workspace.desiredHash,
|
|
567
570
|
deploymentHash: source.deploymentHash,
|
|
571
|
+
predicateRegistry: source.predicateRegistry,
|
|
568
572
|
observedHash,
|
|
569
573
|
agent: {
|
|
570
574
|
id: finalSnapshot.agent?.id || agentId,
|
|
@@ -626,6 +630,7 @@ function retireDeployment(root, workspace, source, plan, preflight, operation, c
|
|
|
626
630
|
inspectionEvidencePath: preflight.evidencePath,
|
|
627
631
|
desiredHash: workspace.desiredHash,
|
|
628
632
|
deploymentHash: source.deploymentHash,
|
|
633
|
+
predicateRegistry: source.predicateRegistry,
|
|
629
634
|
observedHash,
|
|
630
635
|
agent: {
|
|
631
636
|
id: agent.id,
|
|
@@ -674,11 +679,61 @@ function loadDeploymentSource(root) {
|
|
|
674
679
|
// second runtime protocol beside the Host's native Skill assignment.
|
|
675
680
|
const instructions = definition;
|
|
676
681
|
const instructionsHash = sha256(instructions);
|
|
682
|
+
// predicate registry 投影作为受管 supporting file 进入归属 Skill(issue #46):
|
|
683
|
+
// 走既有 skill-push 链路交付,投影 hash 因此进 Skill tree hash → deployment hash →
|
|
684
|
+
// Receipt,回读用现有 skill hash 对账就能发现漂移。不引入第二条传输通道。
|
|
685
|
+
const predicateRegistry = attachPredicateRegistry(info, skills);
|
|
677
686
|
const deploymentHash = digest(stableStringify({
|
|
678
687
|
definitionHash, instructionsHash,
|
|
679
688
|
skills: skills.map((item) => ({ name: item.name, hash: item.hash })),
|
|
689
|
+
predicateRegistryHash: predicateRegistry?.hash || '',
|
|
680
690
|
}));
|
|
681
|
-
return { definitionHash, instructions, instructionsHash, skills, deploymentHash };
|
|
691
|
+
return { definitionHash, instructions, instructionsHash, skills, predicateRegistry, deploymentHash };
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* 把 manifest 的 predicates 注册表投影成归属 Skill 的受管 supporting file。
|
|
695
|
+
*
|
|
696
|
+
* 归属 Skill 必须真在受管集合里:注册表声明了、却没有任何一个部署的 Skill 承载它,
|
|
697
|
+
* 那么运行拍读不到判据——这时**拒绝部署**,而不是部署一个「声明说有判据、运行侧其实
|
|
698
|
+
* 拿不到」的 Agent。同路径已有作者手写文件时同样拒绝:受管 artifact 不覆盖人写的东西。
|
|
699
|
+
*/
|
|
700
|
+
function attachPredicateRegistry(info, skills) {
|
|
701
|
+
const projection = buildPredicateRegistry({
|
|
702
|
+
name: info.project.name,
|
|
703
|
+
predicates: info.project.predicates,
|
|
704
|
+
// completion 文法只有一个 parser(schedule-plan),投影这边不再自己解析一遍
|
|
705
|
+
scheduleRefs: info.project.schedules.map((schedule) => ({
|
|
706
|
+
name: schedule.name, predicate: completionPredicateId(schedule.completion),
|
|
707
|
+
})),
|
|
708
|
+
});
|
|
709
|
+
if (!projection)
|
|
710
|
+
return null;
|
|
711
|
+
const owner = predicateRegistryOwner(info.project.skills);
|
|
712
|
+
const bundle = skills.find((item) => item.name === owner);
|
|
713
|
+
if (!bundle) {
|
|
714
|
+
throw new Error(`Project 声明了 predicates 注册表,但受管 Skill 集合里没有承载它的 Skill(期望 ${owner || '-'})——` +
|
|
715
|
+
'运行拍会读不到判据,先把该 Skill 加入 agent.skills 再部署');
|
|
716
|
+
}
|
|
717
|
+
if (bundle.files.some((item) => item.path === PREDICATE_REGISTRY_PATH)) {
|
|
718
|
+
throw new Error(`Skill ${owner} 已有 ${PREDICATE_REGISTRY_PATH}——受管 registry 投影不覆盖作者手写文件`);
|
|
719
|
+
}
|
|
720
|
+
const text = serializeRegistry(projection);
|
|
721
|
+
const content = Buffer.from(text, 'utf8');
|
|
722
|
+
bundle.files = [...bundle.files, {
|
|
723
|
+
path: PREDICATE_REGISTRY_PATH, absolutePath: '', managedContent: text,
|
|
724
|
+
hash: sha256(content), bytes: content.length,
|
|
725
|
+
}].sort((a, b) => a.path.localeCompare(b.path));
|
|
726
|
+
if (bundle.files.length > MAX_SKILL_SUPPORTING_FILES) {
|
|
727
|
+
throw new Error(`Skill ${owner} 加上受管 registry 投影后 supporting files 超过 ${MAX_SKILL_SUPPORTING_FILES} 个`);
|
|
728
|
+
}
|
|
729
|
+
bundle.hash = skillTreeHash(bundle.content, bundle.files.map((item) => ({ path: item.path, hash: item.hash })));
|
|
730
|
+
return {
|
|
731
|
+
skill: owner,
|
|
732
|
+
path: PREDICATE_REGISTRY_PATH,
|
|
733
|
+
hash: projection.hash,
|
|
734
|
+
ids: Object.keys(projection.predicates),
|
|
735
|
+
schedules: projection.schedules.map((item) => item.name),
|
|
736
|
+
};
|
|
682
737
|
}
|
|
683
738
|
function loadSkillBundle(name, root) {
|
|
684
739
|
const actual = realpathSync(root);
|
|
@@ -780,14 +835,14 @@ function reconcileSkill(root, plan, local, remote, command, env, timeoutMs, call
|
|
|
780
835
|
rollback.createdSkillIds.push(id);
|
|
781
836
|
action = 'create';
|
|
782
837
|
for (const file of local.files)
|
|
783
|
-
upsertSkillFile(root, plan, id, file.path, file
|
|
838
|
+
upsertSkillFile(root, plan, id, file.path, skillFileContentPath(file, temp), command, env, timeoutMs, calls);
|
|
784
839
|
}
|
|
785
840
|
else if (remote.hash !== local.hash) {
|
|
786
841
|
rollback.updatedSkills.push(remote);
|
|
787
842
|
runProvider(command, scopedArgs(plan, [
|
|
788
843
|
'skill', 'update', id, '--content-file', local.mainPath, '--output', 'json',
|
|
789
844
|
]), `skill.update:${local.name}`, 'write', root, env, timeoutMs, calls);
|
|
790
|
-
syncSkillFiles(root, plan, id, local, remote, command, env, timeoutMs, calls);
|
|
845
|
+
syncSkillFiles(root, plan, id, local, remote, command, env, timeoutMs, calls, temp);
|
|
791
846
|
action = 'update';
|
|
792
847
|
}
|
|
793
848
|
const readback = parseRemoteSkill(jsonObject(runProvider(command, scopedArgs(plan, ['skill', 'get', id, '--output', 'json']), `skill.get.readback:${local.name}`, 'read', root, env, timeoutMs, calls).stdout, `skill readback ${local.name}`));
|
|
@@ -835,7 +890,7 @@ function reconcileAgent(root, plan, source, remote, command, env, timeoutMs, cal
|
|
|
835
890
|
}
|
|
836
891
|
return { agent, action };
|
|
837
892
|
}
|
|
838
|
-
function syncSkillFiles(root, plan, skillId, local, remote, command, env, timeoutMs, calls) {
|
|
893
|
+
function syncSkillFiles(root, plan, skillId, local, remote, command, env, timeoutMs, calls, temp) {
|
|
839
894
|
const remoteByPath = new Map(remote.files.map((item) => [item.path, item]));
|
|
840
895
|
const localPaths = new Set(local.files.map((item) => item.path));
|
|
841
896
|
for (const file of remote.files) {
|
|
@@ -846,9 +901,25 @@ function syncSkillFiles(root, plan, skillId, local, remote, command, env, timeou
|
|
|
846
901
|
for (const file of local.files) {
|
|
847
902
|
if (remoteByPath.get(file.path)?.hash === file.hash)
|
|
848
903
|
continue;
|
|
849
|
-
upsertSkillFile(root, plan, skillId, file.path, file
|
|
904
|
+
upsertSkillFile(root, plan, skillId, file.path, skillFileContentPath(file, temp), command, env, timeoutMs, calls);
|
|
850
905
|
}
|
|
851
906
|
}
|
|
907
|
+
/**
|
|
908
|
+
* 上传用的正文来源。磁盘文件直接给路径;受管 artifact(registry 投影)在这里才物化——
|
|
909
|
+
* plan 阶段只参与 hash,不落任何文件,dry-run 的「零副作用」保持为真。
|
|
910
|
+
*/
|
|
911
|
+
function skillFileContentPath(file, temp) {
|
|
912
|
+
if (file.absolutePath)
|
|
913
|
+
return file.absolutePath;
|
|
914
|
+
if (file.managedContent === undefined) {
|
|
915
|
+
throw new Error(`受管 Skill 文件缺正文: ${file.path}`);
|
|
916
|
+
}
|
|
917
|
+
const path = writeTemporaryContent(temp, `managed-${sha256(file.path).slice(0, 8)}`, file.managedContent);
|
|
918
|
+
if (sha256(readFileSync(path)) !== file.hash) {
|
|
919
|
+
throw new Error(`受管 Skill 文件物化后 hash 不一致: ${file.path}`);
|
|
920
|
+
}
|
|
921
|
+
return path;
|
|
922
|
+
}
|
|
852
923
|
function upsertSkillFile(root, plan, skillId, path, contentFile, command, env, timeoutMs, calls) {
|
|
853
924
|
runProvider(command, scopedArgs(plan, [
|
|
854
925
|
'skill', 'files', 'upsert', skillId, '--path', path,
|
|
@@ -1301,6 +1372,7 @@ function failureReceipt(root, workspace, source, plan, preflight, operation, cal
|
|
|
1301
1372
|
inspectionEvidencePath: preflight.evidencePath,
|
|
1302
1373
|
desiredHash: workspace.desiredHash,
|
|
1303
1374
|
deploymentHash: source.deploymentHash,
|
|
1375
|
+
predicateRegistry: source.predicateRegistry,
|
|
1304
1376
|
observedHash: '',
|
|
1305
1377
|
agent: {
|
|
1306
1378
|
id: operation.agentId,
|