narraeon 0.3.1 → 0.5.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/node/protocol/appPreferences.js +25 -0
- package/dist/node/protocol/conversationObservation.js +67 -0
- package/dist/node/protocol/v1.js +188 -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 +447 -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 +44 -10
- 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-BXfelCi9.js +1207 -0
- package/dist/web/assets/index-BYnhUxlk.css +1 -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-D5VqDvjY.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,11 +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
|
+
"model.copy": { connectionId: "string", name: "string" },
|
|
24
25
|
"model.select": { connectionId: "string" },
|
|
25
26
|
"model.delete": { connectionId: "string" },
|
|
26
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" },
|
|
27
32
|
"content.read": { packageId: "string" },
|
|
28
33
|
"content.replace": { packageId: "string", files: "array" },
|
|
29
34
|
"content.copy": { packageId: "string" },
|
|
@@ -31,25 +36,31 @@ const requiredFields = {
|
|
|
31
36
|
"content.rename": { packageId: "string", name: "string" },
|
|
32
37
|
"content.import": { archiveBase64: "string" },
|
|
33
38
|
"content.export": { packageId: "string" },
|
|
34
|
-
"setting-improvement.
|
|
35
|
-
|
|
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": {
|
|
36
44
|
packageId: "string",
|
|
37
|
-
|
|
38
|
-
mode: "string",
|
|
39
|
-
contextPaths: "array",
|
|
45
|
+
sessionId: "string",
|
|
40
46
|
},
|
|
41
|
-
"setting-improvement.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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",
|
|
46
56
|
},
|
|
47
|
-
"setting-improvement.
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
"setting-improvement.message": {
|
|
58
|
+
packageId: "string",
|
|
59
|
+
requestId: "string",
|
|
60
|
+
message: "string",
|
|
61
|
+
continuation: "object",
|
|
50
62
|
},
|
|
51
|
-
"setting-improvement.
|
|
52
|
-
"setting-improvement.discard": { improvementId: "string" },
|
|
63
|
+
"setting-improvement.cancel": { sessionId: "string" },
|
|
53
64
|
"play.create": { name: "string" },
|
|
54
65
|
"play.copy": { presetId: "string" },
|
|
55
66
|
"play.save": { presetId: "string", name: "string", files: "object" },
|
|
@@ -81,6 +92,43 @@ const requiredFields = {
|
|
|
81
92
|
"world.control-draft.save": { worldId: "string", files: "array" },
|
|
82
93
|
"world.control-draft.preview": { worldId: "string" },
|
|
83
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" },
|
|
84
132
|
"world.derive": {
|
|
85
133
|
operationId: "string",
|
|
86
134
|
sourceWorldId: "string",
|
|
@@ -93,6 +141,7 @@ const requiredFields = {
|
|
|
93
141
|
eventId: "number",
|
|
94
142
|
replacementExchangeId: "string",
|
|
95
143
|
replacementText: "string",
|
|
144
|
+
continuation: "string",
|
|
96
145
|
},
|
|
97
146
|
"play.chain.start": {
|
|
98
147
|
worldId: "string",
|
|
@@ -118,7 +167,10 @@ const requiredFields = {
|
|
|
118
167
|
chainId: "string",
|
|
119
168
|
eventId: "number",
|
|
120
169
|
},
|
|
170
|
+
"world.play-context.read": { worldId: "string" },
|
|
121
171
|
"world.surface.read": { worldId: "string", surface: "string" },
|
|
172
|
+
"world.extensions.read": { worldId: "string" },
|
|
173
|
+
"world.extensions.set": { worldId: "string", key: "string", value: "string" },
|
|
122
174
|
"world.play-decorations.read": { worldId: "string" },
|
|
123
175
|
"correction.begin": { worldId: "string", operationId: "string" },
|
|
124
176
|
"correction.read": { candidateId: "string", document: "string" },
|
|
@@ -152,17 +204,33 @@ function validateRequestFields(request) {
|
|
|
152
204
|
if (!valid || (kind === "string" && value === "" && !allowsEmptyString))
|
|
153
205
|
throw new V1ProtocolError("invalid_request", `${String(request.type)}.${field} is invalid`);
|
|
154
206
|
}
|
|
155
|
-
if (request.type === "
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
+
}
|
|
159
222
|
if (request.type === "content.import") {
|
|
160
223
|
const archiveBase64 = request.archiveBase64;
|
|
161
224
|
if (archiveBase64.length > maxPortableContentArchiveBase64Characters ||
|
|
162
225
|
archiveBase64.length % 4 !== 0 ||
|
|
163
226
|
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(archiveBase64))
|
|
164
227
|
throw new V1ProtocolError("invalid_request", "content.import.archiveBase64 is not a supported content-package ZIP");
|
|
165
|
-
|
|
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");
|
|
166
234
|
if (unexpectedFields.length > 0)
|
|
167
235
|
throw new V1ProtocolError("invalid_request", `content.import contains unsupported fields: ${unexpectedFields.join(", ")}`);
|
|
168
236
|
return;
|
|
@@ -193,6 +261,17 @@ function validateRequestFields(request) {
|
|
|
193
261
|
!isRecord(request.structure))
|
|
194
262
|
throw new V1ProtocolError("invalid_request", "play.save.structure must be a structured map");
|
|
195
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");
|
|
196
275
|
if (request.presetId !== undefined &&
|
|
197
276
|
(typeof request.presetId !== "string" || request.presetId === ""))
|
|
198
277
|
throw new V1ProtocolError("invalid_request", "play.workbench.read.presetId is invalid");
|
|
@@ -229,18 +308,53 @@ function validateRequestFields(request) {
|
|
|
229
308
|
if (request.type === "play.timeline.detail" &&
|
|
230
309
|
(!Number.isSafeInteger(request.eventId) || Number(request.eventId) < 1))
|
|
231
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");
|
|
232
315
|
if (request.type === "world.surface.read" &&
|
|
233
316
|
request.surface !== "state" &&
|
|
234
317
|
request.surface !== "control" &&
|
|
235
318
|
request.surface !== "history" &&
|
|
236
319
|
request.surface !== "runtime")
|
|
237
320
|
throw new V1ProtocolError("invalid_request", "world.surface.read.surface is invalid");
|
|
238
|
-
if (request.type
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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");
|
|
244
358
|
}
|
|
245
359
|
const requestTypes = new Set([
|
|
246
360
|
"workspace.read",
|
|
@@ -248,10 +362,15 @@ const requestTypes = new Set([
|
|
|
248
362
|
"preferences.save",
|
|
249
363
|
"model.read",
|
|
250
364
|
"model.save",
|
|
365
|
+
"model.copy",
|
|
251
366
|
"model.select",
|
|
252
367
|
"model.delete",
|
|
253
368
|
"model.models",
|
|
254
369
|
"content.create",
|
|
370
|
+
"content.scripts.read",
|
|
371
|
+
"content.scripts.set",
|
|
372
|
+
"world.package-scripts.read",
|
|
373
|
+
"world.package-scripts.set",
|
|
255
374
|
"content.read",
|
|
256
375
|
"content.replace",
|
|
257
376
|
"content.copy",
|
|
@@ -259,13 +378,15 @@ const requestTypes = new Set([
|
|
|
259
378
|
"content.rename",
|
|
260
379
|
"content.import",
|
|
261
380
|
"content.export",
|
|
262
|
-
"setting-improvement.
|
|
263
|
-
"setting-improvement.
|
|
264
|
-
"setting-improvement.
|
|
265
|
-
"setting-improvement.
|
|
266
|
-
"setting-improvement.
|
|
267
|
-
"setting-improvement.
|
|
268
|
-
"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",
|
|
269
390
|
"play.read",
|
|
270
391
|
"play.create",
|
|
271
392
|
"play.copy",
|
|
@@ -290,6 +411,18 @@ const requestTypes = new Set([
|
|
|
290
411
|
"world.control-draft.save",
|
|
291
412
|
"world.control-draft.preview",
|
|
292
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",
|
|
293
426
|
"world.derive",
|
|
294
427
|
"play.chain.revise-player",
|
|
295
428
|
"play.chain.start",
|
|
@@ -298,7 +431,10 @@ const requestTypes = new Set([
|
|
|
298
431
|
"play.chain.inspect",
|
|
299
432
|
"play.timeline.page",
|
|
300
433
|
"play.timeline.detail",
|
|
434
|
+
"world.play-context.read",
|
|
301
435
|
"world.surface.read",
|
|
436
|
+
"world.extensions.read",
|
|
437
|
+
"world.extensions.set",
|
|
302
438
|
"world.play-decorations.read",
|
|
303
439
|
"correction.begin",
|
|
304
440
|
"correction.read",
|
|
@@ -311,3 +447,21 @@ const requestTypes = new Set([
|
|
|
311
447
|
function isRecord(value) {
|
|
312
448
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
313
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
|
+
}
|