@world-engines/scenario-review-snapshot 0.1.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-compiler-witness.d.ts +21 -0
- package/dist/artifact-compiler-witness.js +75 -0
- package/dist/artifact-digest.d.ts +9 -1
- package/dist/artifact-digest.js +117 -5
- package/dist/artifact.d.ts +12 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -2
- package/dist/neutral-artifact-assembler.d.ts +8 -0
- package/dist/neutral-artifact-assembler.js +130 -37
- package/dist/server-bound-view-compiler.js +1 -1
- package/dist/submit-wire.d.ts +9 -0
- package/dist/submit-wire.js +9 -0
- package/package.json +11 -11
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CompileTargetProfile } from "@world-engines/tmw/compile-target-profile";
|
|
2
|
+
import { type NeutralSourceCompilerReceiptCore } from "@world-engines/tmw/neutral-source-compiler";
|
|
3
|
+
export interface ArtifactCompilerWitnessBlob {
|
|
4
|
+
readonly bytes: Uint8Array;
|
|
5
|
+
readonly sha256: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ArtifactCompilerWitnessVerificationInput {
|
|
8
|
+
readonly scenario_id: string;
|
|
9
|
+
readonly source_manifest_sha256: string;
|
|
10
|
+
readonly target_profile: ArtifactCompilerWitnessBlob;
|
|
11
|
+
readonly compiler_receipt: ArtifactCompilerWitnessBlob;
|
|
12
|
+
}
|
|
13
|
+
export interface VerifiedArtifactCompilerWitness {
|
|
14
|
+
readonly target_profile: CompileTargetProfile;
|
|
15
|
+
readonly compiler_receipt: NeutralSourceCompilerReceiptCore;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 验证 ArtifactV5 中两份 compiler witness 的共同语义,而不信任生产者
|
|
19
|
+
* 曾经走过哪个 compile path。三个 Player reader 与 bundle parser 共用此门。
|
|
20
|
+
*/
|
|
21
|
+
export declare function verifyArtifactCompilerWitness(input: ArtifactCompilerWitnessVerificationInput): VerifiedArtifactCompilerWitness;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { canonicalCompileTargetProfileBytes, parseCompileTargetProfile, } from "@world-engines/tmw/compile-target-profile";
|
|
2
|
+
import { canonicalNeutralSourceCompilerReceiptCoreBytes, parseNeutralSourceCompilerReceiptCore, } from "@world-engines/tmw/neutral-source-compiler";
|
|
3
|
+
import { sha256Hex } from "./canonical.js";
|
|
4
|
+
const strictDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
5
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/u;
|
|
6
|
+
function bytesEqual(left, right) {
|
|
7
|
+
if (left.byteLength !== right.byteLength)
|
|
8
|
+
return false;
|
|
9
|
+
let difference = 0;
|
|
10
|
+
for (let index = 0; index < left.byteLength; index += 1) {
|
|
11
|
+
difference |= (left[index] ?? 0) ^ (right[index] ?? 0);
|
|
12
|
+
}
|
|
13
|
+
return difference === 0;
|
|
14
|
+
}
|
|
15
|
+
function parseJson(bytes, kind) {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(strictDecoder.decode(bytes));
|
|
18
|
+
}
|
|
19
|
+
catch (cause) {
|
|
20
|
+
throw new Error(`artifact_compiler_witness_json_invalid:${kind}`, { cause });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function requireBlobIdentity(blob, kind) {
|
|
24
|
+
if (!(blob.bytes instanceof Uint8Array)
|
|
25
|
+
|| !SHA256_PATTERN.test(blob.sha256)
|
|
26
|
+
|| sha256Hex(blob.bytes) !== blob.sha256) {
|
|
27
|
+
throw new Error(`artifact_compiler_witness_identity_invalid:${kind}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 验证 ArtifactV5 中两份 compiler witness 的共同语义,而不信任生产者
|
|
32
|
+
* 曾经走过哪个 compile path。三个 Player reader 与 bundle parser 共用此门。
|
|
33
|
+
*/
|
|
34
|
+
export function verifyArtifactCompilerWitness(input) {
|
|
35
|
+
if (input.scenario_id.length === 0
|
|
36
|
+
|| input.scenario_id !== input.scenario_id.trim().normalize("NFC")
|
|
37
|
+
|| !SHA256_PATTERN.test(input.source_manifest_sha256)) {
|
|
38
|
+
throw new Error("artifact_compiler_witness_scope_invalid");
|
|
39
|
+
}
|
|
40
|
+
requireBlobIdentity(input.target_profile, "compile_target_profile");
|
|
41
|
+
requireBlobIdentity(input.compiler_receipt, "compiler_receipt");
|
|
42
|
+
const targetProfile = parseCompileTargetProfile(parseJson(input.target_profile.bytes, "compile_target_profile"));
|
|
43
|
+
if (!bytesEqual(input.target_profile.bytes, canonicalCompileTargetProfileBytes(targetProfile))) {
|
|
44
|
+
throw new Error("artifact_compiler_witness_target_profile_noncanonical");
|
|
45
|
+
}
|
|
46
|
+
const compilerReceipt = parseNeutralSourceCompilerReceiptCore(parseJson(input.compiler_receipt.bytes, "compiler_receipt"));
|
|
47
|
+
if (!bytesEqual(input.compiler_receipt.bytes, canonicalNeutralSourceCompilerReceiptCoreBytes(compilerReceipt))) {
|
|
48
|
+
throw new Error("artifact_compiler_witness_compiler_receipt_noncanonical");
|
|
49
|
+
}
|
|
50
|
+
if (compilerReceipt.scenario_id !== input.scenario_id) {
|
|
51
|
+
throw new Error("artifact_compiler_witness_scenario_mismatch");
|
|
52
|
+
}
|
|
53
|
+
if (compilerReceipt.source_manifest_sha256 !== input.source_manifest_sha256) {
|
|
54
|
+
throw new Error("artifact_compiler_witness_source_manifest_mismatch");
|
|
55
|
+
}
|
|
56
|
+
if (compilerReceipt.compiler_release !== targetProfile.compiler_release) {
|
|
57
|
+
throw new Error("artifact_compiler_witness_compiler_release_mismatch");
|
|
58
|
+
}
|
|
59
|
+
if (compilerReceipt.target_profile_sha256 !== input.target_profile.sha256) {
|
|
60
|
+
throw new Error("artifact_compiler_witness_target_profile_digest_mismatch");
|
|
61
|
+
}
|
|
62
|
+
const receiptVersions = new Map(compilerReceipt.stages.map((stage) => [stage.stage_id, stage.stage_version]));
|
|
63
|
+
if (receiptVersions.size !== targetProfile.stage_versions.length
|
|
64
|
+
|| compilerReceipt.stages.length !== targetProfile.stage_versions.length
|
|
65
|
+
|| targetProfile.stage_versions.some((stage) => receiptVersions.get(stage.stage_id) !== stage.stage_version)) {
|
|
66
|
+
throw new Error("artifact_compiler_witness_stage_set_mismatch");
|
|
67
|
+
}
|
|
68
|
+
if (compilerReceipt.stages.some((stage) => stage.dependency_outputs.some((dependency) => !receiptVersions.has(dependency.stage_id)))) {
|
|
69
|
+
throw new Error("artifact_compiler_witness_stage_dependency_unknown");
|
|
70
|
+
}
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
target_profile: targetProfile,
|
|
73
|
+
compiler_receipt: compilerReceipt,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { JsonValue } from "./types.js";
|
|
2
|
-
import { type ArtifactV5, type FrozenPublishInput, type FrozenArtifactBundle, type FrozenRuntimeIdentity, type LegacyArtifactV5Package2Read, type PublishedArtifactHead, type PublishedArtifactHeadCore } from "./artifact.js";
|
|
2
|
+
import { type ArtifactV5, type BinaryIdentity, type FrozenPublishInput, type FrozenArtifactBundle, type FrozenViewPublicationCapture, type FrozenRuntimeIdentity, type LegacyArtifactV5Package2Read, type PublishedArtifactHead, type PublishedArtifactHeadCore } from "./artifact.js";
|
|
3
3
|
export declare function canonicalBytes(value: JsonValue): Uint8Array;
|
|
4
4
|
export declare function canonicalSha256(value: JsonValue): string;
|
|
5
5
|
export declare function buildArtifactV5(input: FrozenPublishInput): ArtifactV5;
|
|
6
6
|
export declare function buildFrozenArtifactBundle(input: FrozenPublishInput): FrozenArtifactBundle;
|
|
7
7
|
export declare function parseFrozenArtifactBundle(value: unknown): FrozenArtifactBundle;
|
|
8
|
+
/**
|
|
9
|
+
* 解码后 submit、恢复 ledger 与 bundle parser 共用的唯一 HTML capture 校验器。
|
|
10
|
+
* 未携带旧字段只兼容两端均为空的历史 bundle;非空 manifest 必须 fail closed。
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseFrozenViewPublications(value: unknown, views: Readonly<{
|
|
13
|
+
publish_mobile: BinaryIdentity;
|
|
14
|
+
publish_pc: BinaryIdentity;
|
|
15
|
+
}>): readonly FrozenViewPublicationCapture[];
|
|
8
16
|
export declare function canonicalArtifactV5Bytes(artifact: ArtifactV5): Uint8Array;
|
|
9
17
|
export declare function artifactV5ManifestSha256(artifact: ArtifactV5): string;
|
|
10
18
|
export declare function parseArtifactV5(value: unknown): ArtifactV5;
|
package/dist/artifact-digest.js
CHANGED
|
@@ -2,6 +2,7 @@ import { canonicalJson, compareUtf8, sha256Hex, toJsonValue } from "./canonical.
|
|
|
2
2
|
import { parsePublishedDeviceViewReceipt, assertDeviceViewsMatchArtifact } from "./device-view-receipt.js";
|
|
3
3
|
import { parseViewInteractionManifestV3, } from "@world-engines/tmw/view-interaction-manifest-v3";
|
|
4
4
|
import { parseMusicCandidateSourceV3, } from "@world-engines/tmw/music-candidate-set";
|
|
5
|
+
import { verifyArtifactCompilerWitness } from "./artifact-compiler-witness.js";
|
|
5
6
|
import { ARTIFACT_COMPILER_WITNESS_OBJECTS, ARTIFACT_COMPILER_WITNESS_PATHS, ARTIFACT_COMPILER_WITNESS_RESERVED_PREFIX, ARTIFACT_MANIFEST_VERSION_V5, ARTIFACT_PACKAGE_FORMAT_VERSION_V5, CAPTURED_REVISION_SCHEMA_VERSION, FROZEN_PUBLISH_INPUT_SCHEMA_VERSION, FROZEN_ARTIFACT_OBJECT_COUNT, FROZEN_DATABASE_DELIVERY_CODEC, FROZEN_DATABASE_DELIVERY_MEDIA_TYPE, FROZEN_DATABASE_DELIVERY_MAGIC, FROZEN_DATABASE_DELIVERY_RELATIVE_PATH, EARLIER_LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, PUBLISHED_ARTIFACT_HEAD_SCHEMA_VERSION, } from "./artifact.js";
|
|
6
7
|
const encoder = new TextEncoder();
|
|
7
8
|
const strictDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
@@ -111,11 +112,18 @@ export function buildFrozenArtifactBundle(input) {
|
|
|
111
112
|
].sort((left, right) => compareUtf8(left.relative_path, right.relative_path));
|
|
112
113
|
assertStrictlySortedUnique(objects.map((object) => object.relative_path), "frozen_artifact_object_path_duplicate");
|
|
113
114
|
assertBundleMatchesArtifact(artifact, objects);
|
|
114
|
-
|
|
115
|
+
const viewPublications = parseFrozenViewPublications([
|
|
116
|
+
...(normalized.view_publish_mobile_bytes.byteLength === 0 ? [] : [frozenViewPublication("mobile", normalized.view_publish_mobile_bytes)]),
|
|
117
|
+
...(normalized.view_publish_pc_bytes.byteLength === 0 ? [] : [frozenViewPublication("pc", normalized.view_publish_pc_bytes)]),
|
|
118
|
+
], artifact.views);
|
|
119
|
+
return parseFrozenArtifactBundle({ artifact, objects, view_publications: viewPublications });
|
|
115
120
|
}
|
|
116
121
|
export function parseFrozenArtifactBundle(value) {
|
|
117
122
|
const root = plainObject(value, "frozen_artifact_bundle_object_required");
|
|
118
|
-
|
|
123
|
+
const bundleKeys = Object.hasOwn(root, "view_publications")
|
|
124
|
+
? ["artifact", "objects", "view_publications"]
|
|
125
|
+
: ["artifact", "objects"];
|
|
126
|
+
exactKeys(root, bundleKeys, "frozen_artifact_bundle");
|
|
119
127
|
const artifact = parseArtifactV5(root.artifact);
|
|
120
128
|
if (!Array.isArray(root.objects))
|
|
121
129
|
throw new Error("frozen_artifact_bundle_objects_invalid");
|
|
@@ -123,7 +131,102 @@ export function parseFrozenArtifactBundle(value) {
|
|
|
123
131
|
assertStrictlySortedUnique(objects.map((object) => object.relative_path), "frozen_artifact_object_order_invalid");
|
|
124
132
|
assertBundleMatchesArtifact(artifact, objects);
|
|
125
133
|
assertCanonicalBundlePayloads(artifact, objects);
|
|
126
|
-
|
|
134
|
+
const viewPublications = parseFrozenViewPublications(root.view_publications, artifact.views);
|
|
135
|
+
return Object.freeze({
|
|
136
|
+
artifact,
|
|
137
|
+
objects: Object.freeze(objects),
|
|
138
|
+
view_publications: viewPublications,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const FROZEN_VIEW_PUBLICATION_SPECS = Object.freeze({
|
|
142
|
+
mobile: Object.freeze({ relative_path: "view-publish-mobile.html" }),
|
|
143
|
+
pc: Object.freeze({ relative_path: "view-publish-pc.html" }),
|
|
144
|
+
});
|
|
145
|
+
const FROZEN_VIEW_PUBLICATION_MEDIA_TYPE = "text/html; charset=utf-8";
|
|
146
|
+
function frozenViewPublication(device, value) {
|
|
147
|
+
const bytes = requireBytes(value, `frozen_view_publication_${device}_bytes_invalid`);
|
|
148
|
+
return Object.freeze({
|
|
149
|
+
device,
|
|
150
|
+
relative_path: FROZEN_VIEW_PUBLICATION_SPECS[device].relative_path,
|
|
151
|
+
media_type: FROZEN_VIEW_PUBLICATION_MEDIA_TYPE,
|
|
152
|
+
...binaryIdentity(bytes),
|
|
153
|
+
bytes,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 解码后 submit、恢复 ledger 与 bundle parser 共用的唯一 HTML capture 校验器。
|
|
158
|
+
* 未携带旧字段只兼容两端均为空的历史 bundle;非空 manifest 必须 fail closed。
|
|
159
|
+
*/
|
|
160
|
+
export function parseFrozenViewPublications(value, views) {
|
|
161
|
+
const expected = Object.freeze({
|
|
162
|
+
mobile: parseBinaryIdentity(views.publish_mobile),
|
|
163
|
+
pc: parseBinaryIdentity(views.publish_pc),
|
|
164
|
+
});
|
|
165
|
+
const emptySha256 = sha256Hex(new Uint8Array());
|
|
166
|
+
for (const device of ["mobile", "pc"]) {
|
|
167
|
+
if (expected[device].size_bytes === 0 && expected[device].sha256 !== emptySha256) {
|
|
168
|
+
throw new Error(`frozen_view_publication_empty_identity_mismatch:${device}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (value === undefined) {
|
|
172
|
+
if (expected.mobile.size_bytes !== 0 || expected.pc.size_bytes !== 0) {
|
|
173
|
+
throw new Error("frozen_view_publications_missing");
|
|
174
|
+
}
|
|
175
|
+
return Object.freeze([]);
|
|
176
|
+
}
|
|
177
|
+
if (!Array.isArray(value))
|
|
178
|
+
throw new Error("frozen_view_publications_invalid");
|
|
179
|
+
const captures = [];
|
|
180
|
+
const seen = new Set();
|
|
181
|
+
for (const entry of value) {
|
|
182
|
+
const object = plainObject(entry, "frozen_view_publication_invalid");
|
|
183
|
+
exactKeys(object, [
|
|
184
|
+
"device", "relative_path", "media_type", "encoding", "size_bytes", "sha256", "bytes",
|
|
185
|
+
], "frozen_view_publication");
|
|
186
|
+
if (object.device !== "mobile" && object.device !== "pc") {
|
|
187
|
+
throw new Error("frozen_view_publication_device_invalid");
|
|
188
|
+
}
|
|
189
|
+
const device = object.device;
|
|
190
|
+
if (seen.has(device))
|
|
191
|
+
throw new Error(`frozen_view_publication_device_duplicate:${device}`);
|
|
192
|
+
seen.add(device);
|
|
193
|
+
if (object.relative_path !== FROZEN_VIEW_PUBLICATION_SPECS[device].relative_path) {
|
|
194
|
+
throw new Error(`frozen_view_publication_path_mismatch:${device}`);
|
|
195
|
+
}
|
|
196
|
+
if (object.media_type !== FROZEN_VIEW_PUBLICATION_MEDIA_TYPE) {
|
|
197
|
+
throw new Error(`frozen_view_publication_media_type_mismatch:${device}`);
|
|
198
|
+
}
|
|
199
|
+
if (object.encoding !== "bytes") {
|
|
200
|
+
throw new Error(`frozen_view_publication_encoding_invalid:${device}`);
|
|
201
|
+
}
|
|
202
|
+
const capture = frozenViewPublication(device, requireBytes(object.bytes, `frozen_view_publication_${device}_bytes_invalid`));
|
|
203
|
+
try {
|
|
204
|
+
strictDecoder.decode(capture.bytes);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
throw new Error(`frozen_view_publication_utf8_invalid:${device}`);
|
|
208
|
+
}
|
|
209
|
+
if (object.size_bytes !== capture.size_bytes || object.sha256 !== capture.sha256) {
|
|
210
|
+
throw new Error(`frozen_view_publication_readback_mismatch:${device}`);
|
|
211
|
+
}
|
|
212
|
+
const identity = expected[device];
|
|
213
|
+
if (capture.size_bytes !== identity.size_bytes || capture.sha256 !== identity.sha256) {
|
|
214
|
+
throw new Error(`frozen_view_publication_artifact_mismatch:${device}`);
|
|
215
|
+
}
|
|
216
|
+
if (capture.size_bytes === 0) {
|
|
217
|
+
throw new Error(`frozen_view_publication_empty_must_be_omitted:${device}`);
|
|
218
|
+
}
|
|
219
|
+
captures.push(capture);
|
|
220
|
+
}
|
|
221
|
+
for (const device of ["mobile", "pc"]) {
|
|
222
|
+
const hasCapture = seen.has(device);
|
|
223
|
+
const isRequired = expected[device].size_bytes > 0;
|
|
224
|
+
if (hasCapture !== isRequired) {
|
|
225
|
+
throw new Error(`frozen_view_publication_capture_count_mismatch:${device}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
captures.sort((left, right) => left.device === right.device ? 0 : left.device === "mobile" ? -1 : 1);
|
|
229
|
+
return Object.freeze(captures);
|
|
127
230
|
}
|
|
128
231
|
function canonicalArtifactV5CoreBytes(core) {
|
|
129
232
|
if (core.package_format_version === LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5) {
|
|
@@ -805,8 +908,17 @@ function assertCanonicalBundlePayloads(artifact, objects) {
|
|
|
805
908
|
const viewInteractionMobile = assertCanonicalJsonObject(one("view_interaction_mobile"));
|
|
806
909
|
const viewInteractionPc = assertCanonicalJsonObject(one("view_interaction_pc"));
|
|
807
910
|
const musicCandidateSource = assertCanonicalJsonObject(one("music_candidate_source"));
|
|
808
|
-
|
|
809
|
-
|
|
911
|
+
const sourceManifest = one("source_manifest");
|
|
912
|
+
const targetProfile = one("compile_target_profile");
|
|
913
|
+
const compilerReceipt = one("compiler_receipt");
|
|
914
|
+
assertCanonicalJsonObject(targetProfile);
|
|
915
|
+
assertCanonicalJsonObject(compilerReceipt);
|
|
916
|
+
verifyArtifactCompilerWitness({
|
|
917
|
+
scenario_id: artifact.scenario_id,
|
|
918
|
+
source_manifest_sha256: sourceManifest.sha256,
|
|
919
|
+
target_profile: { bytes: targetProfile.bytes, sha256: targetProfile.sha256 },
|
|
920
|
+
compiler_receipt: { bytes: compilerReceipt.bytes, sha256: compilerReceipt.sha256 },
|
|
921
|
+
});
|
|
810
922
|
normalizeViewInteractionManifest(viewInteractionMobile, artifact.epoch, artifact.runtime_contract_hash, artifact.views.publish_mobile.sha256, "mobile");
|
|
811
923
|
normalizeViewInteractionManifest(viewInteractionPc, artifact.epoch, artifact.runtime_contract_hash, artifact.views.publish_pc.sha256, "pc");
|
|
812
924
|
if (parseMusicCandidateSourceV3(musicCandidateSource) === null) {
|
package/dist/artifact.d.ts
CHANGED
|
@@ -215,6 +215,17 @@ export interface FrozenArtifactObjectIdentity extends BinaryIdentity {
|
|
|
215
215
|
export interface FrozenArtifactObject extends FrozenArtifactObjectIdentity {
|
|
216
216
|
readonly bytes: Uint8Array;
|
|
217
217
|
}
|
|
218
|
+
export type FrozenViewPublicationDevice = "mobile" | "pc";
|
|
219
|
+
/**
|
|
220
|
+
* 审核提交随 Artifact 一次性携带的发布 HTML 原始字节。该对象不参与
|
|
221
|
+
* ArtifactV5 digest;其身份已由 `artifact.views.publish_*` 固定。
|
|
222
|
+
*/
|
|
223
|
+
export interface FrozenViewPublicationCapture extends BinaryIdentity {
|
|
224
|
+
readonly device: FrozenViewPublicationDevice;
|
|
225
|
+
readonly relative_path: "view-publish-mobile.html" | "view-publish-pc.html";
|
|
226
|
+
readonly media_type: "text/html; charset=utf-8";
|
|
227
|
+
readonly bytes: Uint8Array;
|
|
228
|
+
}
|
|
218
229
|
export interface ArtifactCompilerWitnessIdentity {
|
|
219
230
|
readonly source_manifest: FrozenArtifactObjectIdentity & {
|
|
220
231
|
readonly kind: "source_manifest";
|
|
@@ -236,6 +247,7 @@ export interface ArtifactCompilerWitnessIdentity {
|
|
|
236
247
|
export interface FrozenArtifactBundle {
|
|
237
248
|
readonly artifact: ArtifactV5;
|
|
238
249
|
readonly objects: readonly FrozenArtifactObject[];
|
|
250
|
+
readonly view_publications: readonly FrozenViewPublicationCapture[];
|
|
239
251
|
}
|
|
240
252
|
export interface PublishedArtifactHeadCore {
|
|
241
253
|
readonly head_schema_version: typeof PUBLISHED_ARTIFACT_HEAD_SCHEMA_VERSION;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export { appendJsonPointer, canonicalJson, compareUtf8, escapeJsonPointerToken, parseJsonPointer, sha256Hex, toJsonValue, unescapeJsonPointerToken, utf8ByteLength, } from "./canonical.js";
|
|
2
2
|
export { buildScenarioReviewSnapshot, DEFAULT_SCENARIO_REVIEW_SNAPSHOT_LIMITS, reconstructRecordPayload, stableRelationId, } from "./snapshot.js";
|
|
3
|
-
export { ARTIFACT_MANIFEST_VERSION_V5, ARTIFACT_PACKAGE_FORMAT_VERSION_V5, LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, EARLIER_LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, CAPTURED_REVISION_SCHEMA_VERSION, FROZEN_PUBLISH_INPUT_SCHEMA_VERSION, FROZEN_DATABASE_DELIVERY_CODEC, FROZEN_DATABASE_DELIVERY_MEDIA_TYPE, FROZEN_DATABASE_DELIVERY_MAGIC, FROZEN_DATABASE_DELIVERY_RELATIVE_PATH, PUBLISHED_ARTIFACT_HEAD_SCHEMA_VERSION, } from "./artifact.js";
|
|
4
|
-
export { artifactV5ManifestSha256, buildArtifactV5, buildFrozenArtifactBundle, buildPublishedArtifactHead, canonicalArtifactV5Bytes, canonicalBytes, canonicalPublishedArtifactHeadCoreBytes, canonicalSha256, digestPublishedArtifactHeadCore, parseArtifactV5, parseFrozenArtifactBundle, parseLegacyArtifactV5Package2, parsePublishedArtifactHead, } from "./artifact-digest.js";
|
|
3
|
+
export { ARTIFACT_MANIFEST_VERSION_V5, ARTIFACT_COMPILER_WITNESS_OBJECTS, ARTIFACT_PACKAGE_FORMAT_VERSION_V5, LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, EARLIER_LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, CAPTURED_REVISION_SCHEMA_VERSION, FROZEN_PUBLISH_INPUT_SCHEMA_VERSION, FROZEN_DATABASE_DELIVERY_CODEC, FROZEN_DATABASE_DELIVERY_MEDIA_TYPE, FROZEN_DATABASE_DELIVERY_MAGIC, FROZEN_DATABASE_DELIVERY_RELATIVE_PATH, PUBLISHED_ARTIFACT_HEAD_SCHEMA_VERSION, } from "./artifact.js";
|
|
4
|
+
export { artifactV5ManifestSha256, buildArtifactV5, buildFrozenArtifactBundle, buildPublishedArtifactHead, canonicalArtifactV5Bytes, canonicalBytes, canonicalPublishedArtifactHeadCoreBytes, canonicalSha256, digestPublishedArtifactHeadCore, parseArtifactV5, parseFrozenArtifactBundle, parseFrozenViewPublications, parseLegacyArtifactV5Package2, parsePublishedArtifactHead, } from "./artifact-digest.js";
|
|
5
|
+
export { verifyArtifactCompilerWitness } from "./artifact-compiler-witness.js";
|
|
6
|
+
export type { ArtifactCompilerWitnessBlob, ArtifactCompilerWitnessVerificationInput, VerifiedArtifactCompilerWitness, } from "./artifact-compiler-witness.js";
|
|
5
7
|
export type { CoverageManifest, CoverageManifestCore, CoverageRecordEntry, CoverageSectionEntry, JsonPrimitive, JsonValue, ReviewFragmentValueKind, ReviewRecordKind, ReviewSection, ScenarioReviewChunk, ScenarioReviewFragment, ScenarioReviewRecord, ScenarioReviewSnapshot, ScenarioReviewSnapshotCore, ScenarioReviewSnapshotInput, ScenarioReviewSnapshotLimits, ScenarioReviewSupplementalRecordInput, } from "./types.js";
|
|
6
|
-
export type { ArtifactV5, ArtifactV5Core, BinaryIdentity, CanonicalJsonIdentity, CapturedRevision, FrozenPublishInput, FrozenDatabaseDelivery, FrozenDatabaseDeliveryIdentity, FrozenArtifactBundle, FrozenArtifactObject, FrozenArtifactObjectIdentity, FrozenArtifactObjectKind, FrozenPublishResource, FrozenResourceIdentity, FrozenRuntimeIdentity, PublishedViewBuildReceipt, PublishedViewBuildHtmlReceipt, FrozenSystemPrompt, LegacyArtifactV5Package2Read, PublishedArtifactHead, PublishedArtifactHeadCore, Sha256Hex, } from "./artifact.js";
|
|
8
|
+
export type { ArtifactV5, ArtifactV5Core, BinaryIdentity, CanonicalJsonIdentity, CapturedRevision, FrozenPublishInput, FrozenDatabaseDelivery, FrozenDatabaseDeliveryIdentity, FrozenArtifactBundle, FrozenArtifactObject, FrozenArtifactObjectIdentity, FrozenArtifactObjectKind, FrozenViewPublicationCapture, FrozenViewPublicationDevice, FrozenPublishResource, FrozenResourceIdentity, FrozenRuntimeIdentity, PublishedViewBuildReceipt, PublishedViewBuildHtmlReceipt, FrozenSystemPrompt, LegacyArtifactV5Package2Read, PublishedArtifactHead, PublishedArtifactHeadCore, Sha256Hex, } from "./artifact.js";
|
|
7
9
|
export type { ArtifactObjectReadbackReceipt, FinalizeApprovedArtifactInput, FinalizeApprovedArtifactResult, PublishOperationKey, PublishAttemptState, PublishAttemptCoordinator, PublishedArtifactObjectReader, ResolvePublishedArtifactInput, ResolvedPublishedArtifact, SubmitFrozenArtifactInput, SubmittedPublishAttempt, WriteOnceArtifactObject, WriteOnceArtifactStore, WriteOncePutResult, } from "./publish-attempt.js";
|
|
8
10
|
export { buildFrozenArtifactObjectKey, publishOperationKey, } from "./publish-attempt.js";
|
|
9
11
|
export * from "./neutral-artifact-assembler.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { appendJsonPointer, canonicalJson, compareUtf8, escapeJsonPointerToken, parseJsonPointer, sha256Hex, toJsonValue, unescapeJsonPointerToken, utf8ByteLength, } from "./canonical.js";
|
|
2
2
|
export { buildScenarioReviewSnapshot, DEFAULT_SCENARIO_REVIEW_SNAPSHOT_LIMITS, reconstructRecordPayload, stableRelationId, } from "./snapshot.js";
|
|
3
|
-
export { ARTIFACT_MANIFEST_VERSION_V5, ARTIFACT_PACKAGE_FORMAT_VERSION_V5, LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, EARLIER_LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, CAPTURED_REVISION_SCHEMA_VERSION, FROZEN_PUBLISH_INPUT_SCHEMA_VERSION, FROZEN_DATABASE_DELIVERY_CODEC, FROZEN_DATABASE_DELIVERY_MEDIA_TYPE, FROZEN_DATABASE_DELIVERY_MAGIC, FROZEN_DATABASE_DELIVERY_RELATIVE_PATH, PUBLISHED_ARTIFACT_HEAD_SCHEMA_VERSION, } from "./artifact.js";
|
|
4
|
-
export { artifactV5ManifestSha256, buildArtifactV5, buildFrozenArtifactBundle, buildPublishedArtifactHead, canonicalArtifactV5Bytes, canonicalBytes, canonicalPublishedArtifactHeadCoreBytes, canonicalSha256, digestPublishedArtifactHeadCore, parseArtifactV5, parseFrozenArtifactBundle, parseLegacyArtifactV5Package2, parsePublishedArtifactHead, } from "./artifact-digest.js";
|
|
3
|
+
export { ARTIFACT_MANIFEST_VERSION_V5, ARTIFACT_COMPILER_WITNESS_OBJECTS, ARTIFACT_PACKAGE_FORMAT_VERSION_V5, LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, EARLIER_LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, CAPTURED_REVISION_SCHEMA_VERSION, FROZEN_PUBLISH_INPUT_SCHEMA_VERSION, FROZEN_DATABASE_DELIVERY_CODEC, FROZEN_DATABASE_DELIVERY_MEDIA_TYPE, FROZEN_DATABASE_DELIVERY_MAGIC, FROZEN_DATABASE_DELIVERY_RELATIVE_PATH, PUBLISHED_ARTIFACT_HEAD_SCHEMA_VERSION, } from "./artifact.js";
|
|
4
|
+
export { artifactV5ManifestSha256, buildArtifactV5, buildFrozenArtifactBundle, buildPublishedArtifactHead, canonicalArtifactV5Bytes, canonicalBytes, canonicalPublishedArtifactHeadCoreBytes, canonicalSha256, digestPublishedArtifactHeadCore, parseArtifactV5, parseFrozenArtifactBundle, parseFrozenViewPublications, parseLegacyArtifactV5Package2, parsePublishedArtifactHead, } from "./artifact-digest.js";
|
|
5
|
+
export { verifyArtifactCompilerWitness } from "./artifact-compiler-witness.js";
|
|
5
6
|
export { buildFrozenArtifactObjectKey, publishOperationKey, } from "./publish-attempt.js";
|
|
6
7
|
export * from "./neutral-artifact-assembler.js";
|
|
7
8
|
export * from "./bound-view-freeze.js";
|
|
@@ -93,6 +93,13 @@ export interface NeutralArtifactRuntimeDefinitions {
|
|
|
93
93
|
readonly achievement_defs: readonly JsonValue[];
|
|
94
94
|
readonly transaction_defs: readonly JsonValue[];
|
|
95
95
|
}
|
|
96
|
+
/** 发布时由 compiler 从 prompt_retrieval.retrieval.play_config 固化的生成合同。 */
|
|
97
|
+
export interface FrozenGenerationPolicyV1 {
|
|
98
|
+
readonly schema_version: 1;
|
|
99
|
+
readonly model: string;
|
|
100
|
+
readonly temperature: number;
|
|
101
|
+
readonly policy_sha256: string;
|
|
102
|
+
}
|
|
96
103
|
export interface NeutralArtifactSourceModel<TViewProject = unknown> {
|
|
97
104
|
readonly metadata: JsonValue;
|
|
98
105
|
readonly protagonist: JsonValue;
|
|
@@ -111,6 +118,7 @@ export interface NeutralArtifactSourceModel<TViewProject = unknown> {
|
|
|
111
118
|
readonly view_interaction_pc: ViewInteractionSourceV3 | null;
|
|
112
119
|
readonly music_candidate_source: MusicCandidateSourceV3;
|
|
113
120
|
readonly retrieval: JsonValue;
|
|
121
|
+
readonly generation_policy: FrozenGenerationPolicyV1 | null;
|
|
114
122
|
readonly runtime_definitions: NeutralArtifactRuntimeDefinitions;
|
|
115
123
|
readonly source_metadata: Readonly<Record<string, JsonValue>>;
|
|
116
124
|
readonly resources: readonly FrozenPublishResource[];
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { canonicalScenarioSourceManifestDigestInputBytes, canonicalScenarioSourceSectionBytes, } from "@world-engines/scenario-author-source";
|
|
2
|
-
import {
|
|
2
|
+
import { NEUTRAL_SPATIAL_WORLD_STAGE_ID, canonicalCompileTargetProfileBytes, canonicalNeutralSourceCompilerReceiptBytes, compileTargetProfileDigest, canonicalTmwBytes, digestViewInteractionManifestV3, materializeTmw3TriggerDocument, initialTmwSceneFromMetadata, parseMusicCandidateSourceV3, parseTmwBlueprintProgramV1, parseViewInteractionManifestV3, parseViewInteractionSourceV3, runtimeContractHashV3, } from "@world-engines/tmw";
|
|
3
3
|
import { decodeTrustedCompiledBlueprintArtifactV1 } from "@world-engines/tmw/compiled-blueprint-artifact";
|
|
4
|
+
import { TMW_RELATION_ID_ALIASES_METADATA_KEY, createTmwRelationIdAliasesMetadataValue, createTmwRelationIdAliasResolver } from "@world-engines/tmw/relation-id-aliases";
|
|
4
5
|
import { canonicalLadybugRelationId, createViewAccessPolicySubject, sealViewAccessPolicy, VIEW_ACCESS_POLICY_DENY_ROOT, VIEW_ACCESS_POLICY_ROOT_SUBJECT_ID, } from "@world-engines/ladybug-bridge";
|
|
5
6
|
import { validateRoadGeneration } from "@world-engines/spatial-authoring/road-generation";
|
|
6
7
|
import { validateSpatialTemplateConfiguration } from "@world-engines/spatial-authoring/spatial-templates";
|
|
7
8
|
import { validateGpsPosition, validateSpatialCoordinateSystem } from "@world-engines/spatial-authoring";
|
|
8
9
|
import { canonicalJson, sha256Hex, toJsonValue } from "./canonical.js";
|
|
9
10
|
import { ARTIFACT_COMPILER_WITNESS_PATHS, ARTIFACT_COMPILER_WITNESS_RESERVED_PREFIX, FROZEN_PUBLISH_INPUT_SCHEMA_VERSION, } from "./artifact.js";
|
|
10
|
-
import { buildArtifactV5 } from "./artifact-digest.js";
|
|
11
|
+
import { buildArtifactV5, canonicalSha256 } from "./artifact-digest.js";
|
|
11
12
|
export const NEUTRAL_ARTIFACT_EMBEDDING_INDEX_PATH = "__derived__/embedding-index-v1.json";
|
|
12
13
|
export const FRESH_RUNTIME3_PLAYER_TURN_POLICY_SCHEMA_VERSION = 1;
|
|
13
14
|
export const FROZEN_DATABASE_VIEW_ACCESS_POLICY_SCHEMA_VERSION = 3;
|
|
@@ -100,14 +101,37 @@ function embeddingSafeField(key) {
|
|
|
100
101
|
const compact = key.toLocaleLowerCase("en-US").replaceAll(/[^a-z0-9]/gu, "");
|
|
101
102
|
return !/(?:raw|original|archive|source|binary|blob).*(?:base64|bytes)|(?:base64|bytes).*(?:raw|original|archive|source|binary|blob)/u.test(compact);
|
|
102
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Review input historically used `kind`; the locked r14b source uses
|
|
106
|
+
* `relation_kind`. Source endpoints remain `from_id`/`to_id`; no caller may
|
|
107
|
+
* silently choose between conflicting kind fields.
|
|
108
|
+
*/
|
|
109
|
+
function normalizeAuthorRelation(relation) {
|
|
110
|
+
const from = text(relation.from_id, "neutral_artifact_embedding_relation_from_invalid");
|
|
111
|
+
const to = text(relation.to_id, "neutral_artifact_embedding_relation_to_invalid");
|
|
112
|
+
const sourceKind = relation.kind;
|
|
113
|
+
const r14bKind = relation.relation_kind;
|
|
114
|
+
const kind = sourceKind !== undefined && r14bKind !== undefined
|
|
115
|
+
? (() => {
|
|
116
|
+
const current = text(sourceKind, "neutral_artifact_embedding_relation_kind_invalid");
|
|
117
|
+
const blocked = text(r14bKind, "neutral_artifact_embedding_relation_kind_invalid");
|
|
118
|
+
if (current !== blocked)
|
|
119
|
+
throw new Error("neutral_artifact_embedding_relation_kind_conflict");
|
|
120
|
+
return current;
|
|
121
|
+
})()
|
|
122
|
+
: text(sourceKind ?? r14bKind, "neutral_artifact_embedding_relation_kind_invalid");
|
|
123
|
+
return Object.freeze({
|
|
124
|
+
from,
|
|
125
|
+
to,
|
|
126
|
+
kind,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
103
129
|
function relationSourceText(relation) {
|
|
104
130
|
const props = record(relation.props ?? {}, "neutral_artifact_embedding_relation_props_invalid");
|
|
105
131
|
const fields = relation.fields === undefined
|
|
106
132
|
? {}
|
|
107
133
|
: record(relation.fields, "neutral_artifact_embedding_relation_fields_invalid");
|
|
108
|
-
const from =
|
|
109
|
-
const to = text(relation.to_id, "neutral_artifact_embedding_relation_to_invalid");
|
|
110
|
-
const kind = text(relation.kind, "neutral_artifact_embedding_relation_kind_invalid");
|
|
134
|
+
const { from, to, kind } = normalizeAuthorRelation(relation);
|
|
111
135
|
const propText = Object.keys(props).filter(embeddingSafeField).sort().map((key) => {
|
|
112
136
|
const value = props[key];
|
|
113
137
|
const rendered = value === undefined ? "" : stableText(value);
|
|
@@ -143,11 +167,9 @@ function buildNeutralArtifactEmbeddingSourceTextIndex(graph) {
|
|
|
143
167
|
const relation = record(value, "neutral_artifact_embedding_relation_invalid");
|
|
144
168
|
if (relation.disabled === true)
|
|
145
169
|
continue;
|
|
146
|
-
const from = relation
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
append(from, relation);
|
|
150
|
-
if (typeof to === "string" && to !== from)
|
|
170
|
+
const { from, to } = normalizeAuthorRelation(relation);
|
|
171
|
+
append(from, relation);
|
|
172
|
+
if (to !== from)
|
|
151
173
|
append(to, relation);
|
|
152
174
|
}
|
|
153
175
|
const incidentByEntityId = new Map();
|
|
@@ -168,8 +190,9 @@ function deriveNeutralArtifactEntityEmbeddingSourceTextFromIndex(entityValue, in
|
|
|
168
190
|
}).filter(Boolean).join(" ");
|
|
169
191
|
const base = `names=${names(entity.name).join("|")} ${kind} ${entityId} ${baseProps}`.trim();
|
|
170
192
|
const incident = (index.incidentByEntityId.get(entityId) ?? []).map((relation) => {
|
|
171
|
-
const
|
|
172
|
-
const
|
|
193
|
+
const normalized = normalizeAuthorRelation(relation);
|
|
194
|
+
const outgoing = normalized.from === entityId;
|
|
195
|
+
const counterpartId = outgoing ? normalized.to : normalized.from;
|
|
173
196
|
const counterpart = index.entityById.get(counterpartId);
|
|
174
197
|
return [
|
|
175
198
|
`relation=${text(relation.relation_id, "neutral_artifact_embedding_relation_id_invalid")}`,
|
|
@@ -278,22 +301,36 @@ function base64Bytes(value, field) {
|
|
|
278
301
|
throw new Error(`neutral_artifact_${field}_base64_noncanonical`);
|
|
279
302
|
return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
|
|
280
303
|
}
|
|
281
|
-
function derivedObjectMap(objects) {
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
304
|
+
function derivedObjectMap(objects, profile) {
|
|
305
|
+
const expectedByStage = new Map();
|
|
306
|
+
for (const stage of profile.stage_versions) {
|
|
307
|
+
if (expectedByStage.has(stage.stage_id)) {
|
|
308
|
+
throw new Error(`neutral_artifact_target_profile_stage_duplicate:${stage.stage_id}`);
|
|
309
|
+
}
|
|
310
|
+
expectedByStage.set(stage.stage_id, stage.stage_version);
|
|
311
|
+
}
|
|
312
|
+
const byStage = new Map();
|
|
313
|
+
for (const object of objects) {
|
|
314
|
+
if (byStage.has(object.stage_id)) {
|
|
315
|
+
throw new Error(`neutral_artifact_derived_object_duplicate:${object.stage_id}`);
|
|
316
|
+
}
|
|
317
|
+
const expectedVersion = expectedByStage.get(object.stage_id);
|
|
318
|
+
if (expectedVersion === undefined) {
|
|
319
|
+
throw new Error(`neutral_artifact_derived_object_unexpected:${object.stage_id}`);
|
|
320
|
+
}
|
|
321
|
+
if (object.stage_version !== expectedVersion) {
|
|
322
|
+
throw new Error(`neutral_artifact_derived_object_version_mismatch:${object.stage_id}`);
|
|
323
|
+
}
|
|
324
|
+
byStage.set(object.stage_id, object);
|
|
325
|
+
}
|
|
326
|
+
if (objects.length !== profile.stage_versions.length) {
|
|
286
327
|
throw new Error("neutral_artifact_derived_object_set_mismatch");
|
|
287
328
|
}
|
|
288
|
-
for (const stage of
|
|
289
|
-
|
|
290
|
-
if (object === undefined || object.stage_version !== stage.stage_version) {
|
|
329
|
+
for (const stage of profile.stage_versions) {
|
|
330
|
+
if (!byStage.has(stage.stage_id)) {
|
|
291
331
|
throw new Error(`neutral_artifact_derived_object_missing:${stage.stage_id}`);
|
|
292
332
|
}
|
|
293
333
|
}
|
|
294
|
-
if (spatial !== undefined && spatial.stage_version !== NEUTRAL_SPATIAL_WORLD_STAGE_VERSION) {
|
|
295
|
-
throw new Error(`neutral_artifact_derived_object_missing:${NEUTRAL_SPATIAL_WORLD_STAGE_ID}`);
|
|
296
|
-
}
|
|
297
334
|
return byStage;
|
|
298
335
|
}
|
|
299
336
|
function requiredDerivedJson(byStage, stageId) {
|
|
@@ -307,6 +344,14 @@ function optionalSpatialBase(byStage) {
|
|
|
307
344
|
if (object === undefined)
|
|
308
345
|
return null;
|
|
309
346
|
const root = record(parseCanonicalJson(object.bytes, "neutral_spatial_world"), "neutral_artifact_spatial_world_object_required");
|
|
347
|
+
if (root.spatial_world_present !== undefined) {
|
|
348
|
+
exactKeys(root, ["spatial_world_present"], "spatial_world_absent");
|
|
349
|
+
if (object.stage_version !== "2"
|
|
350
|
+
|| root.spatial_world_present !== false) {
|
|
351
|
+
throw new Error("neutral_artifact_spatial_world_absence_invalid");
|
|
352
|
+
}
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
310
355
|
if (root.player_programmatic_supported === false) {
|
|
311
356
|
exactKeys(root, ["player_programmatic_supported", "reason", ...(root.playerMap === undefined ? [] : ["playerMap"]), ...(root.spatialAuthority === undefined ? [] : ["spatialAuthority"])], "spatial_world_unsupported");
|
|
312
357
|
if (root.reason !== "topology_missing"
|
|
@@ -327,6 +372,7 @@ function optionalSpatialBase(byStage) {
|
|
|
327
372
|
...(root.templates === undefined ? [] : ["templates"]),
|
|
328
373
|
...(root.controlledActorId === undefined ? [] : ["controlledActorId"]),
|
|
329
374
|
...(root.playerMap === undefined ? [] : ["playerMap"]),
|
|
375
|
+
...(root.spatialAuthority === undefined ? [] : ["spatialAuthority"]),
|
|
330
376
|
], "spatial_world");
|
|
331
377
|
const baseDigest = text(root.baseDigest, "neutral_artifact_spatial_world_base_digest_invalid");
|
|
332
378
|
if (!SHA256.test(baseDigest))
|
|
@@ -657,6 +703,27 @@ function parseLayoutModel(value) {
|
|
|
657
703
|
}
|
|
658
704
|
return Object.freeze({ view_interaction_mobile: mobile, view_interaction_pc: pc });
|
|
659
705
|
}
|
|
706
|
+
function parseGenerationPolicy(playConfig) {
|
|
707
|
+
if (playConfig === null)
|
|
708
|
+
return null;
|
|
709
|
+
const root = record(playConfig, "neutral_artifact_play_config_object_required");
|
|
710
|
+
const raw = root.generation_policy;
|
|
711
|
+
if (raw === undefined || raw === null)
|
|
712
|
+
return null;
|
|
713
|
+
const policy = record(raw, "neutral_artifact_generation_policy_object_required");
|
|
714
|
+
exactKeys(policy, ["schema_version", "model", "temperature"], "generation_policy");
|
|
715
|
+
const model = text(policy.model, "neutral_artifact_generation_policy_model_invalid");
|
|
716
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/u.test(model)) {
|
|
717
|
+
throw new Error("neutral_artifact_generation_policy_model_invalid");
|
|
718
|
+
}
|
|
719
|
+
const temperature = policy.temperature;
|
|
720
|
+
if (typeof temperature !== "number" || !Number.isFinite(temperature)
|
|
721
|
+
|| temperature < 0 || temperature > 2) {
|
|
722
|
+
throw new Error("neutral_artifact_generation_policy_temperature_invalid");
|
|
723
|
+
}
|
|
724
|
+
const core = toJsonValue({ schema_version: 1, model, temperature });
|
|
725
|
+
return Object.freeze({ schema_version: 1, model, temperature, policy_sha256: canonicalSha256(core) });
|
|
726
|
+
}
|
|
660
727
|
function parsePromptRetrievalModel(value) {
|
|
661
728
|
const root = record(value, "neutral_artifact_prompt_retrieval_object_required");
|
|
662
729
|
exactKeys(root, ["retrieval", "music_candidates", "runtime_definitions"], "prompt_retrieval");
|
|
@@ -673,6 +740,7 @@ function parsePromptRetrievalModel(value) {
|
|
|
673
740
|
throw new Error("neutral_artifact_music_candidate_source_invalid");
|
|
674
741
|
return Object.freeze({
|
|
675
742
|
retrieval: retrieval.retrieval_config ?? null,
|
|
743
|
+
generation_policy: parseGenerationPolicy(retrieval.play_config ?? null),
|
|
676
744
|
music_candidate_source: music,
|
|
677
745
|
runtime_definitions: Object.freeze({
|
|
678
746
|
triggers: array(runtime.triggers, "runtime_triggers"),
|
|
@@ -704,7 +772,7 @@ function parseResourcesModel(value) {
|
|
|
704
772
|
* gzip-tar 解包是唯一外部 seam;调用方不能重写 compiler derived-object 解释规则。
|
|
705
773
|
*/
|
|
706
774
|
export async function materializeNeutralArtifactCompilation(source, profile, outcome, unpackViewProject) {
|
|
707
|
-
const byStage = derivedObjectMap(outcome.derived_objects);
|
|
775
|
+
const byStage = derivedObjectMap(outcome.derived_objects, profile);
|
|
708
776
|
const metadata = parseMetadataModel(requiredDerivedJson(byStage, "neutral.metadata"));
|
|
709
777
|
const graph = parseGraphModel(requiredDerivedJson(byStage, "neutral.logical-graph"));
|
|
710
778
|
const layout = parseLayoutModel(requiredDerivedJson(byStage, "neutral.layout"));
|
|
@@ -934,7 +1002,8 @@ function snapshotEntities(values) {
|
|
|
934
1002
|
if (ids.has(id))
|
|
935
1003
|
throw new Error(`neutral_artifact_entity_id_duplicate:${id}`);
|
|
936
1004
|
ids.add(id);
|
|
937
|
-
|
|
1005
|
+
// 中性作者源允许单名称字符串;仅在数据库投影时转成名称数组,原源不变。
|
|
1006
|
+
const rawNames = typeof entity.name === "string" ? [entity.name] : Array.isArray(entity.name) ? entity.name : [];
|
|
938
1007
|
if (rawNames.length === 0 || rawNames.some((name) => typeof name !== "string"
|
|
939
1008
|
|| name.length === 0 || name !== name.trim().normalize("NFC"))) {
|
|
940
1009
|
throw new Error(`neutral_artifact_entity_name_invalid:${id}`);
|
|
@@ -954,17 +1023,22 @@ function snapshotEntities(values) {
|
|
|
954
1023
|
}
|
|
955
1024
|
function snapshotRelations(values) {
|
|
956
1025
|
const ids = new Set();
|
|
1026
|
+
const sourceIds = new Set();
|
|
957
1027
|
return Object.freeze(values.map((value, index) => {
|
|
958
1028
|
const relation = record(value, `neutral_artifact_relation_${index}_invalid`);
|
|
959
1029
|
if (relation.disabled === true)
|
|
960
1030
|
return null;
|
|
961
|
-
const
|
|
962
|
-
const
|
|
963
|
-
const
|
|
1031
|
+
const normalized = normalizeAuthorRelation(relation);
|
|
1032
|
+
const src = normalized.from;
|
|
1033
|
+
const dst = normalized.to;
|
|
1034
|
+
const kind = normalized.kind;
|
|
1035
|
+
const sourceId = text(relation.relation_id, `neutral_artifact_relation_id_invalid:${index}`);
|
|
1036
|
+
// 作者 ID 属于中性源;数据库关系按两端与类型生成 identity,不能要求作者预先使用物理 ID。
|
|
964
1037
|
const relationId = canonicalLadybugRelationId(src, dst, kind);
|
|
965
|
-
if (
|
|
1038
|
+
if (sourceIds.has(sourceId) || ids.has(relationId)) {
|
|
966
1039
|
throw new Error(`neutral_artifact_relation_id_invalid:${index}`);
|
|
967
1040
|
}
|
|
1041
|
+
sourceIds.add(sourceId);
|
|
968
1042
|
ids.add(relationId);
|
|
969
1043
|
const mode = viewAccess(relation.view_access, `relation_view_access_${relationId}`);
|
|
970
1044
|
return Object.freeze({
|
|
@@ -1018,9 +1092,10 @@ function buildViewAccessPolicy(graphEntities, entities, graphRelations, relation
|
|
|
1018
1092
|
const input = record(value, "neutral_artifact_policy_relation_invalid");
|
|
1019
1093
|
if (input.disabled === true)
|
|
1020
1094
|
continue;
|
|
1021
|
-
const
|
|
1022
|
-
const
|
|
1023
|
-
const
|
|
1095
|
+
const normalized = normalizeAuthorRelation(input);
|
|
1096
|
+
const src = normalized.from;
|
|
1097
|
+
const dst = normalized.to;
|
|
1098
|
+
const kind = normalized.kind;
|
|
1024
1099
|
if (!entityById.has(src) || !entityById.has(dst))
|
|
1025
1100
|
throw new Error("neutral_artifact_policy_relation_endpoint_missing");
|
|
1026
1101
|
const id = canonicalLadybugRelationId(src, dst, kind);
|
|
@@ -1169,6 +1244,22 @@ export async function encodeNeutralReviewDatabaseSnapshot(input) {
|
|
|
1169
1244
|
const graphEntities = deliveryGraphEntities(model.graph.entities);
|
|
1170
1245
|
const entities = snapshotEntities(graphEntities);
|
|
1171
1246
|
const relations = snapshotRelations(model.graph.relations);
|
|
1247
|
+
if (Object.hasOwn(model.source_metadata, TMW_RELATION_ID_ALIASES_METADATA_KEY)) {
|
|
1248
|
+
throw new Error("neutral_artifact_relation_alias_metadata_reserved");
|
|
1249
|
+
}
|
|
1250
|
+
const relationAliases = createTmwRelationIdAliasesMetadataValue(model.graph.relations.flatMap(value => {
|
|
1251
|
+
const relation = record(value, "neutral_artifact_relation_alias_source_invalid");
|
|
1252
|
+
if (relation.disabled === true)
|
|
1253
|
+
return [];
|
|
1254
|
+
const normalized = normalizeAuthorRelation(relation);
|
|
1255
|
+
const sourceId = text(relation.relation_id, "neutral_artifact_relation_alias_source_id_invalid");
|
|
1256
|
+
const canonicalId = canonicalLadybugRelationId(normalized.from, normalized.to, normalized.kind);
|
|
1257
|
+
return sourceId === canonicalId ? [] : [{ source_relation_id: sourceId, canonical_relation_id: canonicalId }];
|
|
1258
|
+
}));
|
|
1259
|
+
const sourceMetadata = relationAliases.length === 0 ? model.source_metadata : {
|
|
1260
|
+
...model.source_metadata, [TMW_RELATION_ID_ALIASES_METADATA_KEY]: relationAliases,
|
|
1261
|
+
};
|
|
1262
|
+
createTmwRelationIdAliasResolver({ sourceMetadata, canonicalRelationIds: new Set(relations.map(relation => relation.relation_id)) });
|
|
1172
1263
|
const entityIds = new Set(entities.map((entity) => entity.id));
|
|
1173
1264
|
for (const relation of relations) {
|
|
1174
1265
|
if (!entityIds.has(relation.src) || !entityIds.has(relation.dst)) {
|
|
@@ -1184,6 +1275,7 @@ export async function encodeNeutralReviewDatabaseSnapshot(input) {
|
|
|
1184
1275
|
const sidecar = toJsonValue({
|
|
1185
1276
|
initial_scene: initialTmwSceneFromMetadata(model.metadata == null ? {} : record(model.metadata, "neutral_artifact_metadata_object_required")),
|
|
1186
1277
|
retrieval_config: model.retrieval,
|
|
1278
|
+
generation_policy: model.generation_policy,
|
|
1187
1279
|
triggers: model.runtime_definitions.triggers,
|
|
1188
1280
|
trigger_variables_schema: model.runtime_definitions.trigger_variables_schema,
|
|
1189
1281
|
cg_defs: model.runtime_definitions.cg_defs,
|
|
@@ -1218,7 +1310,7 @@ export async function encodeNeutralReviewDatabaseSnapshot(input) {
|
|
|
1218
1310
|
view_interaction_mobile: model.view_interaction_mobile,
|
|
1219
1311
|
view_interaction_pc: model.view_interaction_pc,
|
|
1220
1312
|
music_candidate_source: model.music_candidate_source,
|
|
1221
|
-
source_metadata:
|
|
1313
|
+
source_metadata: sourceMetadata,
|
|
1222
1314
|
},
|
|
1223
1315
|
view_access_policy: buildDerivedViewAccessPolicyV3(graphEntities, entities, model.graph.relations, relations),
|
|
1224
1316
|
});
|
|
@@ -1286,16 +1378,17 @@ export async function assembleNeutralFrozenPublishInput(input) {
|
|
|
1286
1378
|
database_bytes: input.database_bytes.slice(),
|
|
1287
1379
|
database_delivery: Object.freeze({ codec: "scnb-v2", bytes: input.database_delivery_bytes.slice() }),
|
|
1288
1380
|
trigger_document: toJsonValue(compilation.model.trigger_document),
|
|
1289
|
-
compiled_plan: compilation.model.
|
|
1290
|
-
? compilation.model.compiled_plan
|
|
1291
|
-
: attachFreshRuntime3PlayerTurnPolicy(compilation.model.compiled_plan, compilation.model.system_prompts, input.runtime_identity.runtime_contract_hash),
|
|
1381
|
+
compiled_plan: attachFreshRuntime3PlayerTurnPolicy(compilation.model.compiled_plan, compilation.model.system_prompts, input.runtime_identity.runtime_contract_hash),
|
|
1292
1382
|
view_source_bytes: compilation.model.view_source_bytes.slice(),
|
|
1293
1383
|
view_publish_mobile_bytes: input.view_publish_mobile_bytes.slice(),
|
|
1294
1384
|
view_publish_pc_bytes: input.view_publish_pc_bytes.slice(),
|
|
1295
1385
|
view_interaction_mobile: mobile,
|
|
1296
1386
|
view_interaction_pc: pc,
|
|
1297
1387
|
music_candidate_source: compilation.model.music_candidate_source,
|
|
1298
|
-
retrieval:
|
|
1388
|
+
retrieval: toJsonValue({
|
|
1389
|
+
retrieval_config: compilation.model.retrieval,
|
|
1390
|
+
generation_policy: compilation.model.generation_policy,
|
|
1391
|
+
}),
|
|
1299
1392
|
resources: mergeNeutralArtifactResources(compilation.model.resources, compilation.witness_resources, [embedding.index_resource]),
|
|
1300
1393
|
runtime_identity: Object.freeze({ ...input.runtime_identity }),
|
|
1301
1394
|
...(input.published_view === undefined ? {} : { published_view: input.published_view }),
|
|
@@ -7,7 +7,7 @@ import { materializeNeutralArtifactCompilation, } from "./neutral-artifact-assem
|
|
|
7
7
|
* 只能运行在 sandbox Node build worker,不能进入 Cloudflare Worker isolate。
|
|
8
8
|
*/
|
|
9
9
|
export function createSandboxNodeBoundViewCompiler(options) {
|
|
10
|
-
const stages = buildCurrentTmw3NeutralCompilerStageRegistry();
|
|
10
|
+
const stages = buildCurrentTmw3NeutralCompilerStageRegistry(options.target_profile);
|
|
11
11
|
const cache = new InMemoryCompilerStageCache();
|
|
12
12
|
const codec = Object.freeze({
|
|
13
13
|
decode_svp(bytes) {
|
package/dist/submit-wire.d.ts
CHANGED
|
@@ -36,6 +36,15 @@ export declare function buildFrozenArtifactLegacySubmitWire(frozen: FrozenPublis
|
|
|
36
36
|
sha256: string;
|
|
37
37
|
bytes_base64: string;
|
|
38
38
|
}>[];
|
|
39
|
+
view_publications: readonly Readonly<{
|
|
40
|
+
device: import("./artifact.js").FrozenViewPublicationDevice;
|
|
41
|
+
relative_path: "view-publish-mobile.html" | "view-publish-pc.html";
|
|
42
|
+
media_type: "text/html; charset=utf-8";
|
|
43
|
+
encoding: "bytes";
|
|
44
|
+
size_bytes: number;
|
|
45
|
+
sha256: string;
|
|
46
|
+
bytes_base64: string;
|
|
47
|
+
}>[];
|
|
39
48
|
resources: readonly Readonly<{
|
|
40
49
|
path: string;
|
|
41
50
|
media_type: string;
|
package/dist/submit-wire.js
CHANGED
|
@@ -38,6 +38,15 @@ export function buildFrozenArtifactLegacySubmitWire(frozen, operation_key, revie
|
|
|
38
38
|
encoding: object.encoding, size_bytes: object.size_bytes, sha256: object.sha256,
|
|
39
39
|
bytes_base64: base64(object.bytes),
|
|
40
40
|
}))),
|
|
41
|
+
view_publications: Object.freeze(artifactBundle.view_publications.map((publication) => Object.freeze({
|
|
42
|
+
device: publication.device,
|
|
43
|
+
relative_path: publication.relative_path,
|
|
44
|
+
media_type: publication.media_type,
|
|
45
|
+
encoding: publication.encoding,
|
|
46
|
+
size_bytes: publication.size_bytes,
|
|
47
|
+
sha256: publication.sha256,
|
|
48
|
+
bytes_base64: base64(publication.bytes),
|
|
49
|
+
}))),
|
|
41
50
|
resources: Object.freeze(frozen.resources.filter((resource) => !Object.values(ARTIFACT_COMPILER_WITNESS_OBJECTS)
|
|
42
51
|
.some((witness) => witness.resource_path === resource.path)).map((resource) => Object.freeze({
|
|
43
52
|
path: resource.path, media_type: resource.media_type, encoding: "bytes",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@world-engines/scenario-review-snapshot",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -26,24 +26,24 @@
|
|
|
26
26
|
"dist"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@world-engines/chatplay-vite-plugin": "0.
|
|
30
|
-
"@world-engines/ladybug-bridge": "0.
|
|
31
|
-
"@world-engines/protocol-ts": "0.
|
|
32
|
-
"@world-engines/scenario-author-source": "0.
|
|
33
|
-
"@world-engines/tmw": "0.
|
|
34
|
-
"@world-engines/spatial-authoring": "0.
|
|
29
|
+
"@world-engines/chatplay-vite-plugin": "1.0.0",
|
|
30
|
+
"@world-engines/ladybug-bridge": "1.0.0",
|
|
31
|
+
"@world-engines/protocol-ts": "1.0.0",
|
|
32
|
+
"@world-engines/scenario-author-source": "1.0.0",
|
|
33
|
+
"@world-engines/tmw": "1.0.0",
|
|
34
|
+
"@world-engines/spatial-authoring": "1.0.0"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public",
|
|
38
38
|
"registry": "https://registry.npmjs.org"
|
|
39
39
|
},
|
|
40
|
-
"worldengine_source_sha256": "
|
|
40
|
+
"worldengine_source_sha256": "1d697deb5b085043a98d155e563e16c1f034945cb40f0786b00d274c967e1ffc",
|
|
41
41
|
"worldengine_source_identity_provenance": {
|
|
42
42
|
"schema": "worldengine-public-namespace-projection/v1",
|
|
43
43
|
"kind": "composite-source-identity; namespace projection; not-full-source-recompile",
|
|
44
44
|
"original_name": "@chat/scenario-review-snapshot",
|
|
45
|
-
"original_archive_sha256": "
|
|
46
|
-
"original_source_sha256": "
|
|
45
|
+
"original_archive_sha256": "152fbcbd8a9f7ecb458600a333708db8b42fed71f8238d3ad6b8ad3b1b19613b",
|
|
46
|
+
"original_source_sha256": "6742170897a854c1c859d642e5ac3cc77c874ec13263928abead5c30572b01ec",
|
|
47
47
|
"projected_name": "@world-engines/scenario-review-snapshot",
|
|
48
48
|
"package_name_mapping": {
|
|
49
49
|
"@chat/blocks-editor": "@world-engines/blocks-editor",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@chat/scenario-review-snapshot": "@world-engines/scenario-review-snapshot",
|
|
56
56
|
"@chat/tmw": "@world-engines/tmw",
|
|
57
57
|
"@chat/view-exposure": "@world-engines/view-exposure",
|
|
58
|
-
"@world-engines/chatplay-vite-plugin": "
|
|
58
|
+
"@world-engines/chatplay-vite-plugin": "@world-engines/chatplay-vite-plugin",
|
|
59
59
|
"@worldengine/agent-kit": "@world-engines/agent-kit",
|
|
60
60
|
"@worldengine/authoring-bridge": "@world-engines/authoring-bridge",
|
|
61
61
|
"@worldengine/authoring-ui": "@world-engines/authoring-ui",
|