narraeon 0.4.0 → 0.5.1
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/node/protocol/appPreferences.js +25 -0
- package/dist/node/protocol/conversationObservation.js +67 -0
- package/dist/node/protocol/v1.js +186 -34
- package/dist/node/protocol/worldExtensions.js +16 -0
- package/dist/node/protocol/worldMaintenance.js +77 -0
- package/dist/node/protocol/worldPromptDiagnostics.js +1 -0
- package/dist/node/runtime/ConversationChanges.js +24 -0
- package/dist/node/runtime/V1Runtime.js +445 -121
- package/dist/node/runtime/artifact/FileNativeArtifactStore.js +326 -12
- package/dist/node/runtime/authoring/AuthoringRequestSnapshot.js +63 -0
- package/dist/node/runtime/authoring/ModelToolConversation.js +218 -0
- package/dist/node/runtime/config/AppPreferencesStore.js +46 -17
- package/dist/node/runtime/content/ContentTreeFingerprint.js +13 -0
- package/dist/node/runtime/content/ContentWorkspace.js +43 -13
- package/dist/node/runtime/content/CurrentTreeContentLibrary.js +128 -46
- package/dist/node/runtime/content/FileNativeContentTree.js +28 -10
- package/dist/node/runtime/content/PackageFollowups.js +80 -0
- package/dist/node/runtime/extension/FrontendExtensionBundle.js +19 -14
- package/dist/node/runtime/extension/PackageScriptPermissions.js +70 -0
- package/dist/node/runtime/extension/WorldExtensionControls.js +205 -0
- package/dist/node/runtime/extension/WorldExtensionRequests.js +64 -0
- package/dist/node/runtime/model/FileNativeModelAdapters.js +21 -466
- package/dist/node/runtime/model/ModelConnectionStore.js +13 -0
- package/dist/node/runtime/model/ModelHost.js +31 -1
- package/dist/node/runtime/model/ModelHostStream.js +51 -5
- package/dist/node/runtime/play/FileNativeContinuityCorrection.js +55 -45
- package/dist/node/runtime/play/FileNativePlayPresetStore.js +256 -238
- package/dist/node/runtime/play/FileNativePlayTimelineStore.js +118 -9
- package/dist/node/runtime/play/OrderedFollowups.js +71 -0
- package/dist/node/runtime/play/OrderedPlayPrompts.js +180 -0
- package/dist/node/runtime/play/PlayCallChain.js +814 -61
- package/dist/node/runtime/play/PlayContinuity.js +88 -0
- package/dist/node/runtime/play/PlayDocumentTools.js +205 -287
- package/dist/node/runtime/play/PlayFollowupRequests.js +34 -3
- package/dist/node/runtime/play/PlayPresetWorkbench.js +45 -54
- package/dist/node/runtime/play/PlayPromptRun.js +86 -0
- package/dist/node/runtime/prompt/FileNativePromptCompiler.js +670 -93
- package/dist/node/runtime/prompt/FileNativeToolRegistry.js +37 -21
- package/dist/node/runtime/prompt/PromptCompilationCodec.js +322 -0
- package/dist/node/runtime/prompt/WorldMaintenanceReport.js +251 -0
- package/dist/node/runtime/prompt/WorldMaterialCoverage.js +83 -0
- package/dist/node/runtime/prompt/WorldPromptDiagnostics.js +185 -0
- package/dist/node/runtime/setting/FileNativeSettingImprovementStore.js +1027 -0
- package/dist/node/runtime/setting/SettingAuthoringTransaction.js +1339 -0
- package/dist/node/runtime/setting/SettingImprovementSession.js +714 -0
- package/dist/node/runtime/world/FileNativeAuthorityV3.js +88 -1
- package/dist/node/runtime/world/FileNativeWorldStore.js +292 -27
- package/dist/node/runtime/world/PlayerViewBindings.js +30 -0
- package/dist/node/runtime/world/PlayerViewRenderer.js +27 -34
- package/dist/node/runtime/world/WorldDocumentMaintenance.js +174 -0
- package/dist/node/runtime/world/WorldDocumentStore.js +166 -96
- package/dist/node/runtime/world/WorldOperationCoordinator.js +103 -2
- package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +696 -0
- package/dist/node/runtime/world-revision/WorldRevisionAuthoringTransaction.js +380 -0
- package/dist/node/runtime/world-revision/WorldRevisionSession.js +709 -0
- package/dist/node/runtime/world-revision/WorldRevisionWorkspace.js +494 -0
- package/dist/node/server/ConversationStream.js +109 -0
- package/dist/node/server/createServer.js +5 -1
- package/dist/node/shared/artifact-payload-contract.js +123 -0
- package/dist/node/shared/default-play-prompts.js +25 -45
- package/dist/node/shared/default-preset-host.js +114 -138
- package/dist/node/shared/default-setting-improvement-prompt.js +52 -38
- package/dist/node/shared/display-regex.js +114 -0
- package/dist/node/shared/first-party-action-choices.js +1 -6
- package/dist/node/shared/first-party-player-view.js +1 -6
- package/dist/node/shared/ordered-author-prompts.js +64 -0
- package/dist/node/shared/ordered-followups.js +42 -0
- package/dist/node/shared/ordered-play-prompts.js +73 -0
- package/dist/web/assets/index-DG85hpWC.css +1 -0
- package/dist/web/assets/index-DfxvCjb6.js +1207 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/node/runtime/model/SettingAuthorStream.js +0 -21
- package/dist/node/runtime/setting/DocumentCandidateSettingImprovement.js +0 -1731
- package/dist/web/assets/index-D8YKfmNG.js +0 -1192
- package/dist/web/assets/index-Xa0s35CJ.css +0 -1
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
export const appLocales = ["en", "zh-CN"];
|
|
2
|
+
export const readingDensities = ["compact", "standard", "relaxed"];
|
|
2
3
|
export const defaultAppLocale = "en";
|
|
4
|
+
export const defaultAppReadingPreferences = {
|
|
5
|
+
density: "compact",
|
|
6
|
+
fontSize: 17,
|
|
7
|
+
lineHeight: 1.7,
|
|
8
|
+
letterSpacing: 0.01,
|
|
9
|
+
measure: 48,
|
|
10
|
+
};
|
|
3
11
|
export function isAppLocale(value) {
|
|
4
12
|
return appLocales.includes(value);
|
|
5
13
|
}
|
|
14
|
+
export function isAppReadingPreferences(value) {
|
|
15
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
16
|
+
return false;
|
|
17
|
+
const candidate = value;
|
|
18
|
+
return (Object.keys(candidate).length === 5 &&
|
|
19
|
+
readingDensities.includes(candidate.density) &&
|
|
20
|
+
isNumberInRange(candidate.fontSize, 15, 24) &&
|
|
21
|
+
isNumberInRange(candidate.lineHeight, 1.4, 2.4) &&
|
|
22
|
+
isNumberInRange(candidate.letterSpacing, 0, 0.12) &&
|
|
23
|
+
isNumberInRange(candidate.measure, 32, 72));
|
|
24
|
+
}
|
|
25
|
+
function isNumberInRange(value, minimum, maximum) {
|
|
26
|
+
return (typeof value === "number" &&
|
|
27
|
+
Number.isFinite(value) &&
|
|
28
|
+
value >= minimum &&
|
|
29
|
+
value <= maximum);
|
|
30
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Only append-only live text uses deltas. Every other transition resets from
|
|
2
|
+
* the Runtime projection, including settlement, context changes and recovery. */
|
|
3
|
+
export function conversationUpdate(previous, next) {
|
|
4
|
+
if (previous?.kind === "play" &&
|
|
5
|
+
next.kind === "play" &&
|
|
6
|
+
previous.extensionsRevision === next.extensionsRevision &&
|
|
7
|
+
previous.value !== null &&
|
|
8
|
+
next.value !== null &&
|
|
9
|
+
previous.value.chainId === next.value.chainId) {
|
|
10
|
+
const candidate = structuredClone(previous.value);
|
|
11
|
+
const deltas = [];
|
|
12
|
+
for (const event of next.value.events) {
|
|
13
|
+
const old = candidate.events.find((item) => item.id === event.id);
|
|
14
|
+
if (event.kind !== "assistant" ||
|
|
15
|
+
event.status !== "streaming" ||
|
|
16
|
+
old?.kind !== "assistant")
|
|
17
|
+
continue;
|
|
18
|
+
for (const field of ["text", "reasoning", "toolFragment"]) {
|
|
19
|
+
const before = old[field] ?? "";
|
|
20
|
+
const after = event[field];
|
|
21
|
+
if (!after?.startsWith(before))
|
|
22
|
+
continue;
|
|
23
|
+
if (after !== before)
|
|
24
|
+
deltas.push({
|
|
25
|
+
eventId: event.id,
|
|
26
|
+
field,
|
|
27
|
+
text: after.slice(before.length),
|
|
28
|
+
});
|
|
29
|
+
old[field] = after;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
candidate.updatedAt = next.value.updatedAt;
|
|
33
|
+
if (next.value.activeInvocation === undefined)
|
|
34
|
+
delete candidate.activeInvocation;
|
|
35
|
+
else
|
|
36
|
+
candidate.activeInvocation = next.value.activeInvocation;
|
|
37
|
+
if (JSON.stringify(candidate) === JSON.stringify(next.value)) {
|
|
38
|
+
return {
|
|
39
|
+
kind: "play_delta",
|
|
40
|
+
chainId: candidate.chainId,
|
|
41
|
+
updatedAt: candidate.updatedAt,
|
|
42
|
+
progress: candidate.activeInvocation ?? null,
|
|
43
|
+
deltas,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { kind: "snapshot", value: next };
|
|
48
|
+
}
|
|
49
|
+
export function applyConversationUpdate(previous, update) {
|
|
50
|
+
if (update.kind === "snapshot")
|
|
51
|
+
return update.value;
|
|
52
|
+
if (previous?.kind !== "play" || previous.value?.chainId !== update.chainId)
|
|
53
|
+
throw new Error("Conversation observation needs a fresh snapshot");
|
|
54
|
+
const value = structuredClone(previous.value);
|
|
55
|
+
for (const delta of update.deltas) {
|
|
56
|
+
const event = value.events.find((item) => item.id === delta.eventId);
|
|
57
|
+
if (event?.kind !== "assistant" || event.status !== "streaming")
|
|
58
|
+
throw new Error("Conversation observation has an unknown live event");
|
|
59
|
+
event[delta.field] = `${event[delta.field] ?? ""}${delta.text}`;
|
|
60
|
+
}
|
|
61
|
+
value.updatedAt = update.updatedAt;
|
|
62
|
+
if (update.progress === null)
|
|
63
|
+
delete value.activeInvocation;
|
|
64
|
+
else
|
|
65
|
+
value.activeInvocation = update.progress;
|
|
66
|
+
return { ...previous, kind: "play", value };
|
|
67
|
+
}
|
package/dist/node/protocol/v1.js
CHANGED
|
@@ -19,12 +19,16 @@ export function parseV1Envelope(value) {
|
|
|
19
19
|
return value;
|
|
20
20
|
}
|
|
21
21
|
const requiredFields = {
|
|
22
|
-
"preferences.save": {
|
|
22
|
+
"preferences.save": {},
|
|
23
23
|
"model.save": { connection: "object" },
|
|
24
24
|
"model.copy": { connectionId: "string", name: "string" },
|
|
25
25
|
"model.select": { connectionId: "string" },
|
|
26
26
|
"model.delete": { connectionId: "string" },
|
|
27
27
|
"model.models": { provider: "string", baseUrl: "string" },
|
|
28
|
+
"content.scripts.read": { packageId: "string" },
|
|
29
|
+
"content.scripts.set": { packageId: "string", enabled: "boolean" },
|
|
30
|
+
"world.package-scripts.read": { worldId: "string" },
|
|
31
|
+
"world.package-scripts.set": { worldId: "string", enabled: "boolean" },
|
|
28
32
|
"content.read": { packageId: "string" },
|
|
29
33
|
"content.replace": { packageId: "string", files: "array" },
|
|
30
34
|
"content.copy": { packageId: "string" },
|
|
@@ -32,25 +36,31 @@ const requiredFields = {
|
|
|
32
36
|
"content.rename": { packageId: "string", name: "string" },
|
|
33
37
|
"content.import": { archiveBase64: "string" },
|
|
34
38
|
"content.export": { packageId: "string" },
|
|
35
|
-
"setting-improvement.
|
|
36
|
-
|
|
39
|
+
"setting-improvement.read": { packageId: "string" },
|
|
40
|
+
"setting-improvement.preview": { packageId: "string" },
|
|
41
|
+
"setting-improvement.status": { packageId: "string" },
|
|
42
|
+
"setting-improvement.overview": { packageId: "string" },
|
|
43
|
+
"setting-improvement.session.read": {
|
|
37
44
|
packageId: "string",
|
|
38
|
-
|
|
39
|
-
mode: "string",
|
|
40
|
-
contextPaths: "array",
|
|
45
|
+
sessionId: "string",
|
|
41
46
|
},
|
|
42
|
-
"setting-improvement.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
"setting-improvement.session.delete": {
|
|
48
|
+
packageId: "string",
|
|
49
|
+
sessionId: "string",
|
|
50
|
+
},
|
|
51
|
+
"setting-improvement.rollback": {
|
|
52
|
+
packageId: "string",
|
|
53
|
+
sessionId: "string",
|
|
54
|
+
changeSetId: "string",
|
|
55
|
+
path: "string",
|
|
47
56
|
},
|
|
48
|
-
"setting-improvement.
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
"setting-improvement.message": {
|
|
58
|
+
packageId: "string",
|
|
59
|
+
requestId: "string",
|
|
60
|
+
message: "string",
|
|
61
|
+
continuation: "object",
|
|
51
62
|
},
|
|
52
|
-
"setting-improvement.
|
|
53
|
-
"setting-improvement.discard": { improvementId: "string" },
|
|
63
|
+
"setting-improvement.cancel": { sessionId: "string" },
|
|
54
64
|
"play.create": { name: "string" },
|
|
55
65
|
"play.copy": { presetId: "string" },
|
|
56
66
|
"play.save": { presetId: "string", name: "string", files: "object" },
|
|
@@ -82,6 +92,43 @@ const requiredFields = {
|
|
|
82
92
|
"world.control-draft.save": { worldId: "string", files: "array" },
|
|
83
93
|
"world.control-draft.preview": { worldId: "string" },
|
|
84
94
|
"world.control-draft.apply": { worldId: "string" },
|
|
95
|
+
"world.revision.open": { worldId: "string" },
|
|
96
|
+
"world.revision.overview": { worldId: "string" },
|
|
97
|
+
"world.revision.preview": { worldId: "string" },
|
|
98
|
+
"world.revision.status": { worldId: "string" },
|
|
99
|
+
"world.revision.session.read": {
|
|
100
|
+
worldId: "string",
|
|
101
|
+
sessionId: "string",
|
|
102
|
+
},
|
|
103
|
+
"world.revision.session.delete": {
|
|
104
|
+
worldId: "string",
|
|
105
|
+
sessionId: "string",
|
|
106
|
+
},
|
|
107
|
+
"world.revision.message": {
|
|
108
|
+
worldId: "string",
|
|
109
|
+
requestId: "string",
|
|
110
|
+
message: "string",
|
|
111
|
+
continuation: "object",
|
|
112
|
+
},
|
|
113
|
+
"world.revision.cancel": { sessionId: "string" },
|
|
114
|
+
"world.revision.files.replace": {
|
|
115
|
+
worldId: "string",
|
|
116
|
+
epochId: "string",
|
|
117
|
+
expectedRevision: "string",
|
|
118
|
+
files: "array",
|
|
119
|
+
},
|
|
120
|
+
"world.revision.rollback": {
|
|
121
|
+
worldId: "string",
|
|
122
|
+
epochId: "string",
|
|
123
|
+
changeSetId: "string",
|
|
124
|
+
path: "string",
|
|
125
|
+
},
|
|
126
|
+
"world.revision.apply": {
|
|
127
|
+
worldId: "string",
|
|
128
|
+
epochId: "string",
|
|
129
|
+
expectedRevision: "string",
|
|
130
|
+
},
|
|
131
|
+
"world.revision.discard": { worldId: "string", epochId: "string" },
|
|
85
132
|
"world.derive": {
|
|
86
133
|
operationId: "string",
|
|
87
134
|
sourceWorldId: "string",
|
|
@@ -94,6 +141,7 @@ const requiredFields = {
|
|
|
94
141
|
eventId: "number",
|
|
95
142
|
replacementExchangeId: "string",
|
|
96
143
|
replacementText: "string",
|
|
144
|
+
continuation: "string",
|
|
97
145
|
},
|
|
98
146
|
"play.chain.start": {
|
|
99
147
|
worldId: "string",
|
|
@@ -119,7 +167,10 @@ const requiredFields = {
|
|
|
119
167
|
chainId: "string",
|
|
120
168
|
eventId: "number",
|
|
121
169
|
},
|
|
170
|
+
"world.play-context.read": { worldId: "string" },
|
|
122
171
|
"world.surface.read": { worldId: "string", surface: "string" },
|
|
172
|
+
"world.extensions.read": { worldId: "string" },
|
|
173
|
+
"world.extensions.set": { worldId: "string", key: "string", value: "string" },
|
|
123
174
|
"world.play-decorations.read": { worldId: "string" },
|
|
124
175
|
"correction.begin": { worldId: "string", operationId: "string" },
|
|
125
176
|
"correction.read": { candidateId: "string", document: "string" },
|
|
@@ -153,17 +204,33 @@ function validateRequestFields(request) {
|
|
|
153
204
|
if (!valid || (kind === "string" && value === "" && !allowsEmptyString))
|
|
154
205
|
throw new V1ProtocolError("invalid_request", `${String(request.type)}.${field} is invalid`);
|
|
155
206
|
}
|
|
156
|
-
if (request.type === "
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
207
|
+
if (request.type === "world.extensions.set" &&
|
|
208
|
+
!["on", "off", "default"].includes(String(request.value)))
|
|
209
|
+
throw new V1ProtocolError("invalid_request", "Invalid world extension choice");
|
|
210
|
+
if (request.type === "preferences.save") {
|
|
211
|
+
if ((request.locale === undefined && request.reading === undefined) ||
|
|
212
|
+
Object.keys(request).some((key) => key !== "type" && key !== "locale" && key !== "reading"))
|
|
213
|
+
throw new V1ProtocolError("invalid_request", "preferences.save must update locale or reading preferences");
|
|
214
|
+
if (request.locale !== undefined &&
|
|
215
|
+
request.locale !== "en" &&
|
|
216
|
+
request.locale !== "zh-CN")
|
|
217
|
+
throw new V1ProtocolError("invalid_request", "preferences.save.locale must be en or zh-CN");
|
|
218
|
+
if (request.reading !== undefined &&
|
|
219
|
+
!isValidReadingPreferences(request.reading))
|
|
220
|
+
throw new V1ProtocolError("invalid_request", "preferences.save.reading is invalid");
|
|
221
|
+
}
|
|
160
222
|
if (request.type === "content.import") {
|
|
161
223
|
const archiveBase64 = request.archiveBase64;
|
|
162
224
|
if (archiveBase64.length > maxPortableContentArchiveBase64Characters ||
|
|
163
225
|
archiveBase64.length % 4 !== 0 ||
|
|
164
226
|
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(archiveBase64))
|
|
165
227
|
throw new V1ProtocolError("invalid_request", "content.import.archiveBase64 is not a supported content-package ZIP");
|
|
166
|
-
|
|
228
|
+
if (request.title !== undefined &&
|
|
229
|
+
(typeof request.title !== "string" ||
|
|
230
|
+
request.title.trim().length === 0 ||
|
|
231
|
+
!/^[^\r\n]{1,160}$/u.test(request.title.trim())))
|
|
232
|
+
throw new V1ProtocolError("invalid_request", "content.import.title is invalid");
|
|
233
|
+
const unexpectedFields = Object.keys(request).filter((field) => field !== "type" && field !== "archiveBase64" && field !== "title");
|
|
167
234
|
if (unexpectedFields.length > 0)
|
|
168
235
|
throw new V1ProtocolError("invalid_request", `content.import contains unsupported fields: ${unexpectedFields.join(", ")}`);
|
|
169
236
|
return;
|
|
@@ -194,6 +261,17 @@ function validateRequestFields(request) {
|
|
|
194
261
|
!isRecord(request.structure))
|
|
195
262
|
throw new V1ProtocolError("invalid_request", "play.save.structure must be a structured map");
|
|
196
263
|
if (request.type === "play.workbench.read") {
|
|
264
|
+
if (request.draft !== undefined &&
|
|
265
|
+
(!isRecord(request.draft) ||
|
|
266
|
+
!isRecord(request.draft.files) ||
|
|
267
|
+
!Object.values(request.draft.files).every((value) => typeof value === "string") ||
|
|
268
|
+
(request.draft.structure !== undefined &&
|
|
269
|
+
!isRecord(request.draft.structure)) ||
|
|
270
|
+
request.presetId === undefined))
|
|
271
|
+
throw new V1ProtocolError("invalid_request", "play.workbench.read.draft requires presetId, text files and an optional structured map");
|
|
272
|
+
if (request.worldId !== undefined &&
|
|
273
|
+
(typeof request.worldId !== "string" || request.worldId === ""))
|
|
274
|
+
throw new V1ProtocolError("invalid_request", "play.workbench.read.worldId is invalid");
|
|
197
275
|
if (request.presetId !== undefined &&
|
|
198
276
|
(typeof request.presetId !== "string" || request.presetId === ""))
|
|
199
277
|
throw new V1ProtocolError("invalid_request", "play.workbench.read.presetId is invalid");
|
|
@@ -230,18 +308,53 @@ function validateRequestFields(request) {
|
|
|
230
308
|
if (request.type === "play.timeline.detail" &&
|
|
231
309
|
(!Number.isSafeInteger(request.eventId) || Number(request.eventId) < 1))
|
|
232
310
|
throw new V1ProtocolError("invalid_request", "play.timeline.detail.eventId is invalid");
|
|
311
|
+
if (request.type === "play.chain.revise-player" &&
|
|
312
|
+
request.continuation !== "continue_context" &&
|
|
313
|
+
request.continuation !== "fresh_context")
|
|
314
|
+
throw new V1ProtocolError("invalid_request", "play.chain.revise-player.continuation is invalid");
|
|
233
315
|
if (request.type === "world.surface.read" &&
|
|
234
316
|
request.surface !== "state" &&
|
|
235
317
|
request.surface !== "control" &&
|
|
236
318
|
request.surface !== "history" &&
|
|
237
319
|
request.surface !== "runtime")
|
|
238
320
|
throw new V1ProtocolError("invalid_request", "world.surface.read.surface is invalid");
|
|
239
|
-
if (request.type
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
321
|
+
if ((request.type === "setting-improvement.preview" ||
|
|
322
|
+
request.type === "world.revision.preview") &&
|
|
323
|
+
request.sessionId !== undefined &&
|
|
324
|
+
(typeof request.sessionId !== "string" || request.sessionId.length === 0))
|
|
325
|
+
throw new V1ProtocolError("invalid_request", "Authoring preview sessionId is invalid");
|
|
326
|
+
if (request.type === "world.revision.status" &&
|
|
327
|
+
request.sessionId !== undefined &&
|
|
328
|
+
(typeof request.sessionId !== "string" || request.sessionId.length === 0))
|
|
329
|
+
throw new V1ProtocolError("invalid_request", "world.revision.status.sessionId is invalid");
|
|
330
|
+
if (request.type === "setting-improvement.message" ||
|
|
331
|
+
request.type === "world.revision.message") {
|
|
332
|
+
const continuation = request.continuation;
|
|
333
|
+
if (!isRecord(continuation) ||
|
|
334
|
+
(continuation.kind !== "fresh_context" &&
|
|
335
|
+
continuation.kind !== "continue_context") ||
|
|
336
|
+
(continuation.kind === "continue_context" &&
|
|
337
|
+
typeof continuation.sessionId !== "string") ||
|
|
338
|
+
(continuation.kind === "fresh_context" && "sessionId" in continuation))
|
|
339
|
+
throw new V1ProtocolError("invalid_request", `${request.type}.continuation is invalid`);
|
|
340
|
+
}
|
|
341
|
+
if (request.type === "setting-improvement.rollback" &&
|
|
342
|
+
(typeof request.changeSetId !== "string" ||
|
|
343
|
+
!/^change-set:(0|[1-9][0-9]*)$/u.test(request.changeSetId)))
|
|
344
|
+
throw new V1ProtocolError("invalid_request", "setting-improvement.rollback.changeSetId is invalid");
|
|
345
|
+
if ((request.type === "world.revision.files.replace" ||
|
|
346
|
+
request.type === "world.revision.rollback" ||
|
|
347
|
+
request.type === "world.revision.apply" ||
|
|
348
|
+
request.type === "world.revision.discard") &&
|
|
349
|
+
(typeof request.epochId !== "string" ||
|
|
350
|
+
!/^revision-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(request.epochId)))
|
|
351
|
+
throw new V1ProtocolError("invalid_request", `${request.type}.epochId is invalid`);
|
|
352
|
+
if (request.type === "world.revision.rollback" &&
|
|
353
|
+
(typeof request.changeSetId !== "string" ||
|
|
354
|
+
!/^change-set:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(request.changeSetId) ||
|
|
355
|
+
typeof request.path !== "string" ||
|
|
356
|
+
request.path.length === 0))
|
|
357
|
+
throw new V1ProtocolError("invalid_request", "world.revision.rollback target is invalid");
|
|
245
358
|
}
|
|
246
359
|
const requestTypes = new Set([
|
|
247
360
|
"workspace.read",
|
|
@@ -254,6 +367,10 @@ const requestTypes = new Set([
|
|
|
254
367
|
"model.delete",
|
|
255
368
|
"model.models",
|
|
256
369
|
"content.create",
|
|
370
|
+
"content.scripts.read",
|
|
371
|
+
"content.scripts.set",
|
|
372
|
+
"world.package-scripts.read",
|
|
373
|
+
"world.package-scripts.set",
|
|
257
374
|
"content.read",
|
|
258
375
|
"content.replace",
|
|
259
376
|
"content.copy",
|
|
@@ -261,13 +378,15 @@ const requestTypes = new Set([
|
|
|
261
378
|
"content.rename",
|
|
262
379
|
"content.import",
|
|
263
380
|
"content.export",
|
|
264
|
-
"setting-improvement.
|
|
265
|
-
"setting-improvement.
|
|
266
|
-
"setting-improvement.
|
|
267
|
-
"setting-improvement.
|
|
268
|
-
"setting-improvement.
|
|
269
|
-
"setting-improvement.
|
|
270
|
-
"setting-improvement.
|
|
381
|
+
"setting-improvement.read",
|
|
382
|
+
"setting-improvement.preview",
|
|
383
|
+
"setting-improvement.status",
|
|
384
|
+
"setting-improvement.overview",
|
|
385
|
+
"setting-improvement.session.read",
|
|
386
|
+
"setting-improvement.session.delete",
|
|
387
|
+
"setting-improvement.rollback",
|
|
388
|
+
"setting-improvement.message",
|
|
389
|
+
"setting-improvement.cancel",
|
|
271
390
|
"play.read",
|
|
272
391
|
"play.create",
|
|
273
392
|
"play.copy",
|
|
@@ -292,6 +411,18 @@ const requestTypes = new Set([
|
|
|
292
411
|
"world.control-draft.save",
|
|
293
412
|
"world.control-draft.preview",
|
|
294
413
|
"world.control-draft.apply",
|
|
414
|
+
"world.revision.open",
|
|
415
|
+
"world.revision.overview",
|
|
416
|
+
"world.revision.preview",
|
|
417
|
+
"world.revision.status",
|
|
418
|
+
"world.revision.session.read",
|
|
419
|
+
"world.revision.session.delete",
|
|
420
|
+
"world.revision.message",
|
|
421
|
+
"world.revision.cancel",
|
|
422
|
+
"world.revision.files.replace",
|
|
423
|
+
"world.revision.rollback",
|
|
424
|
+
"world.revision.apply",
|
|
425
|
+
"world.revision.discard",
|
|
295
426
|
"world.derive",
|
|
296
427
|
"play.chain.revise-player",
|
|
297
428
|
"play.chain.start",
|
|
@@ -300,7 +431,10 @@ const requestTypes = new Set([
|
|
|
300
431
|
"play.chain.inspect",
|
|
301
432
|
"play.timeline.page",
|
|
302
433
|
"play.timeline.detail",
|
|
434
|
+
"world.play-context.read",
|
|
303
435
|
"world.surface.read",
|
|
436
|
+
"world.extensions.read",
|
|
437
|
+
"world.extensions.set",
|
|
304
438
|
"world.play-decorations.read",
|
|
305
439
|
"correction.begin",
|
|
306
440
|
"correction.read",
|
|
@@ -313,3 +447,21 @@ const requestTypes = new Set([
|
|
|
313
447
|
function isRecord(value) {
|
|
314
448
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
315
449
|
}
|
|
450
|
+
function isValidReadingPreferences(value) {
|
|
451
|
+
if (!isRecord(value))
|
|
452
|
+
return false;
|
|
453
|
+
return (Object.keys(value).length === 5 &&
|
|
454
|
+
(value.density === "compact" ||
|
|
455
|
+
value.density === "standard" ||
|
|
456
|
+
value.density === "relaxed") &&
|
|
457
|
+
isFiniteRange(value.fontSize, 15, 24) &&
|
|
458
|
+
isFiniteRange(value.lineHeight, 1.4, 2.4) &&
|
|
459
|
+
isFiniteRange(value.letterSpacing, 0, 0.12) &&
|
|
460
|
+
isFiniteRange(value.measure, 32, 72));
|
|
461
|
+
}
|
|
462
|
+
function isFiniteRange(value, minimum, maximum) {
|
|
463
|
+
return (typeof value === "number" &&
|
|
464
|
+
Number.isFinite(value) &&
|
|
465
|
+
value >= minimum &&
|
|
466
|
+
value <= maximum);
|
|
467
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function isWorldExtensionControl(value) {
|
|
2
|
+
return (typeof value === "object" &&
|
|
3
|
+
value !== null &&
|
|
4
|
+
"key" in value &&
|
|
5
|
+
typeof value.key === "string" &&
|
|
6
|
+
value.key.length > 0 &&
|
|
7
|
+
"generation" in value &&
|
|
8
|
+
Number.isSafeInteger(value.generation) &&
|
|
9
|
+
Number(value.generation) >= 0 &&
|
|
10
|
+
Object.keys(value).length === 2);
|
|
11
|
+
}
|
|
12
|
+
export function requestControlKey(presetId, requestId) {
|
|
13
|
+
return requestId.startsWith("package:")
|
|
14
|
+
? requestId
|
|
15
|
+
: `preset:${presetId}:request:${requestId}`;
|
|
16
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export function isWorldPromptMaintenance(value) {
|
|
2
|
+
if (!record(value) ||
|
|
3
|
+
value.unit !== "utf8_bytes" ||
|
|
4
|
+
(value.unavailableReason !== undefined &&
|
|
5
|
+
typeof value.unavailableReason !== "string") ||
|
|
6
|
+
![
|
|
7
|
+
"worldMaterialsBytes",
|
|
8
|
+
"checkpointHistoryBytes",
|
|
9
|
+
"authorInstructionBytes",
|
|
10
|
+
"runtimeInstructionBytes",
|
|
11
|
+
"toolDefinitionBytes",
|
|
12
|
+
].every((key) => count(value[key])) ||
|
|
13
|
+
!Array.isArray(value.documents) ||
|
|
14
|
+
!Array.isArray(value.slots))
|
|
15
|
+
return false;
|
|
16
|
+
return (value.documents.every((item) => record(item) &&
|
|
17
|
+
typeof item.shortRef === "string" &&
|
|
18
|
+
typeof item.title === "string" &&
|
|
19
|
+
count(item.bodyBytes) &&
|
|
20
|
+
count(item.injectedBytes) &&
|
|
21
|
+
["world_origin", "document_creation", "unavailable"].includes(String(item.baselineKind)) &&
|
|
22
|
+
nullableCount(item.baselineBytes) &&
|
|
23
|
+
nullableNumber(item.growthBytes) &&
|
|
24
|
+
nullableNumber(item.growthRatio) &&
|
|
25
|
+
nullableCount(item.advisoryBytes) &&
|
|
26
|
+
typeof item.overAdvisory === "boolean" &&
|
|
27
|
+
position(item.lastBodyWrite) &&
|
|
28
|
+
position(item.lastMetadataWrite) &&
|
|
29
|
+
(item.observedContexts === undefined || count(item.observedContexts)) &&
|
|
30
|
+
count(item.bodyChangedContexts) &&
|
|
31
|
+
count(item.metadataChangedContexts)) &&
|
|
32
|
+
value.slots.every((slot) => record(slot) &&
|
|
33
|
+
typeof slot.id === "string" &&
|
|
34
|
+
count(slot.bytes) &&
|
|
35
|
+
nullableCount(slot.advisoryBytes) &&
|
|
36
|
+
typeof slot.overAdvisory === "boolean" &&
|
|
37
|
+
Array.isArray(slot.documents) &&
|
|
38
|
+
slot.documents.every((ref) => typeof ref === "string")));
|
|
39
|
+
}
|
|
40
|
+
function position(value) {
|
|
41
|
+
return (value === null ||
|
|
42
|
+
(record(value) &&
|
|
43
|
+
["world_origin", "play", "correction", "history"].includes(String(value.kind)) &&
|
|
44
|
+
nullableCount(value.context) &&
|
|
45
|
+
count(value.round) &&
|
|
46
|
+
(value.worldTime === null || typeof value.worldTime === "string")));
|
|
47
|
+
}
|
|
48
|
+
function record(value) {
|
|
49
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
function count(value) {
|
|
52
|
+
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
53
|
+
}
|
|
54
|
+
function nullableCount(value) {
|
|
55
|
+
return value === null || count(value);
|
|
56
|
+
}
|
|
57
|
+
function nullableNumber(value) {
|
|
58
|
+
return (value === null || (typeof value === "number" && Number.isFinite(value)));
|
|
59
|
+
}
|
|
60
|
+
/** Display only recorded provenance; a write position is not character knowledge. */
|
|
61
|
+
export function worldWritePositionText(position, locale) {
|
|
62
|
+
const zh = locale === "zh-CN";
|
|
63
|
+
if (position === null)
|
|
64
|
+
return zh ? "未记录" : "Not recorded";
|
|
65
|
+
const place = position.kind === "world_origin"
|
|
66
|
+
? zh
|
|
67
|
+
? "世界创建时"
|
|
68
|
+
: "World creation"
|
|
69
|
+
: position.kind === "play" && position.context !== null
|
|
70
|
+
? zh
|
|
71
|
+
? `上下文 ${position.context} · 第 ${position.round} 回合`
|
|
72
|
+
: `Context ${position.context} · round ${position.round}`
|
|
73
|
+
: zh
|
|
74
|
+
? `${position.kind === "correction" ? "连续性修订" : "历史提交"} · 玩家输入位置 ${position.round}`
|
|
75
|
+
: `${position.kind === "correction" ? "Continuity correction" : "History commit"} · player input position ${position.round}`;
|
|
76
|
+
return `${place} · ${zh ? "写入时世界时间" : "World time at write"}: ${position.worldTime ?? (zh ? "未记录" : "not recorded")}`;
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Observation failure never participates in a Runtime mutation. */
|
|
2
|
+
export class ConversationChanges {
|
|
3
|
+
#listeners = new Map();
|
|
4
|
+
subscribe(scope, listener) {
|
|
5
|
+
const listeners = this.#listeners.get(scope) ?? new Set();
|
|
6
|
+
listeners.add(listener);
|
|
7
|
+
this.#listeners.set(scope, listeners);
|
|
8
|
+
return () => {
|
|
9
|
+
listeners.delete(listener);
|
|
10
|
+
if (listeners.size === 0)
|
|
11
|
+
this.#listeners.delete(scope);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
publish(scope, durable = true) {
|
|
15
|
+
for (const listener of this.#listeners.get(scope) ?? []) {
|
|
16
|
+
try {
|
|
17
|
+
listener(durable);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
/* Observers cannot affect settlement. */
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|