narraeon 0.1.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.
Files changed (60) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +164 -0
  3. package/README.zh-CN.md +164 -0
  4. package/dist/node/cli/NarraeonCli.js +163 -0
  5. package/dist/node/cli/main.js +38 -0
  6. package/dist/node/protocol/appPreferences.js +5 -0
  7. package/dist/node/protocol/contentTree.js +14 -0
  8. package/dist/node/protocol/modelConnections.js +110 -0
  9. package/dist/node/protocol/playerViews.js +1 -0
  10. package/dist/node/protocol/v1.js +278 -0
  11. package/dist/node/runtime/V1Runtime.js +625 -0
  12. package/dist/node/runtime/artifact/FileNativeArtifactStore.js +1914 -0
  13. package/dist/node/runtime/config/AppPreferencesStore.js +92 -0
  14. package/dist/node/runtime/config/appPaths.js +19 -0
  15. package/dist/node/runtime/content/ContentLimits.js +1 -0
  16. package/dist/node/runtime/content/ContentTreeFile.js +1 -0
  17. package/dist/node/runtime/content/ContentWorkspace.js +601 -0
  18. package/dist/node/runtime/content/CurrentTreeContentLibrary.js +721 -0
  19. package/dist/node/runtime/content/FileNativeContentTree.js +512 -0
  20. package/dist/node/runtime/extension/FrontendExtensionBundle.js +229 -0
  21. package/dist/node/runtime/extension/PlayerViewPanelProjector.js +121 -0
  22. package/dist/node/runtime/model/AiFailureLog.js +332 -0
  23. package/dist/node/runtime/model/FileNativeModelAdapters.js +1246 -0
  24. package/dist/node/runtime/model/ModelConnectionStore.js +378 -0
  25. package/dist/node/runtime/model/ModelHost.js +89 -0
  26. package/dist/node/runtime/model/ModelHostStream.js +367 -0
  27. package/dist/node/runtime/model/ProviderExchangeState.js +1 -0
  28. package/dist/node/runtime/model/ProviderStream.js +90 -0
  29. package/dist/node/runtime/model/SettingAuthorStream.js +118 -0
  30. package/dist/node/runtime/play/FileNativeContinuityCorrection.js +304 -0
  31. package/dist/node/runtime/play/FileNativePlayPresetStore.js +1856 -0
  32. package/dist/node/runtime/play/PlayCallChain.js +1280 -0
  33. package/dist/node/runtime/play/PlayDocumentTools.js +1121 -0
  34. package/dist/node/runtime/play/PlayFollowupRequests.js +183 -0
  35. package/dist/node/runtime/play/PlayPresetWorkbench.js +185 -0
  36. package/dist/node/runtime/prompt/FileNativePromptCompiler.js +1425 -0
  37. package/dist/node/runtime/prompt/FileNativeToolRegistry.js +192 -0
  38. package/dist/node/runtime/prompt/MaterialSelection.js +95 -0
  39. package/dist/node/runtime/setting/DocumentCandidateSettingImprovement.js +1726 -0
  40. package/dist/node/runtime/world/FileNativeWorldStore.js +2028 -0
  41. package/dist/node/runtime/world/PlayerViewRenderer.js +322 -0
  42. package/dist/node/runtime/world/WorldDocumentStore.js +2808 -0
  43. package/dist/node/runtime/world/WorldOperationCoordinator.js +490 -0
  44. package/dist/node/runtime/world/WorldStateRevision.js +75 -0
  45. package/dist/node/server/createRuntime.js +6 -0
  46. package/dist/node/server/createServer.js +218 -0
  47. package/dist/node/server/main.js +25 -0
  48. package/dist/node/server/startWebServer.js +28 -0
  49. package/dist/node/shared/default-play-prompts.js +85 -0
  50. package/dist/node/shared/default-preset-host.js +538 -0
  51. package/dist/node/shared/default-setting-improvement-prompt.js +77 -0
  52. package/dist/node/shared/first-party-action-choices.js +164 -0
  53. package/dist/node/shared/first-party-generic-panels.js +105 -0
  54. package/dist/node/shared/first-party-play-preset-templates.js +41 -0
  55. package/dist/node/shared/first-party-player-view.js +179 -0
  56. package/dist/node/shared/play-preset-prompt-roles.js +4 -0
  57. package/dist/web/assets/index-CRGTZ9Ak.css +1 -0
  58. package/dist/web/assets/index-DDx1itzT.js +1192 -0
  59. package/dist/web/index.html +14 -0
  60. package/package.json +86 -0
@@ -0,0 +1,322 @@
1
+ import { parseDocument } from "yaml";
2
+ const maxItemsPerView = 128;
3
+ const maxRenderedBytesPerView = 64 * 1024;
4
+ const maxRenderedDepth = 16;
5
+ const maxDocumentReadBytes = 65_536;
6
+ export class PlayerViewRenderer {
7
+ render(input) {
8
+ const diagnostics = [];
9
+ const control = parseControlYaml(input.control);
10
+ if (!isRecord(control) ||
11
+ control.format !== "narraeon.player-views/v1" ||
12
+ !Array.isArray(control.views)) {
13
+ return {
14
+ views: [],
15
+ diagnostics: [
16
+ {
17
+ code: "invalid_control",
18
+ message: "The player-view control file has an invalid format",
19
+ },
20
+ ],
21
+ };
22
+ }
23
+ const views = [];
24
+ for (const candidate of control.views) {
25
+ if (!isRecord(candidate) ||
26
+ typeof candidate.id !== "string" ||
27
+ typeof candidate.title !== "string" ||
28
+ !Array.isArray(candidate.items)) {
29
+ diagnostics.push({
30
+ code: "invalid_control",
31
+ message: "The player-view declaration is invalid",
32
+ });
33
+ continue;
34
+ }
35
+ const view = {
36
+ id: candidate.id,
37
+ title: candidate.title,
38
+ items: [],
39
+ };
40
+ let renderedBytes = 0;
41
+ for (const [index, item] of candidate.items.entries()) {
42
+ if (index >= maxItemsPerView) {
43
+ diagnostics.push({
44
+ code: "capacity_exceeded",
45
+ viewId: view.id,
46
+ message: `UI capacity reached: a view may contain at most ${maxItemsPerView} items`,
47
+ });
48
+ break;
49
+ }
50
+ const resolved = resolveItem(item, input.snapshot);
51
+ if (!resolved.ok) {
52
+ diagnostics.push({
53
+ code: resolved.code,
54
+ viewId: view.id,
55
+ ...(resolved.itemId === undefined
56
+ ? {}
57
+ : { itemId: resolved.itemId }),
58
+ message: resolved.message,
59
+ });
60
+ if (resolved.code === "capacity_exceeded")
61
+ break;
62
+ continue;
63
+ }
64
+ const itemBytes = Buffer.byteLength(JSON.stringify(resolved.item), "utf8");
65
+ if (renderedBytes + itemBytes > maxRenderedBytesPerView) {
66
+ diagnostics.push({
67
+ code: "capacity_exceeded",
68
+ viewId: view.id,
69
+ itemId: resolved.item.id,
70
+ message: "UI capacity reached: rendering stopped at a complete node boundary",
71
+ });
72
+ break;
73
+ }
74
+ renderedBytes += itemBytes;
75
+ view.items.push(resolved.item);
76
+ }
77
+ views.push(view);
78
+ }
79
+ return { views, diagnostics };
80
+ }
81
+ }
82
+ function resolveItem(item, snapshot) {
83
+ if (!isRecord(item) ||
84
+ typeof item.id !== "string" ||
85
+ typeof item.label !== "string" ||
86
+ !isRecord(item.select) ||
87
+ typeof item.select.document !== "string")
88
+ return {
89
+ ok: false,
90
+ code: "invalid_control",
91
+ message: "The player-view item has an invalid format",
92
+ };
93
+ const itemId = item.id;
94
+ const handle = item.select.document;
95
+ const selector = playerViewDocumentSelector(handle);
96
+ if (item.select.locator === undefined ||
97
+ isWholeMarkdownLocator(item.select.locator)) {
98
+ const wholeDocument = resolveWholeDocument(snapshot, selector, handle, item.select.locator === undefined ? undefined : "markdown");
99
+ return wholeDocument.ok
100
+ ? {
101
+ ok: true,
102
+ item: {
103
+ id: itemId,
104
+ label: item.label,
105
+ value: wholeDocument.value,
106
+ },
107
+ }
108
+ : { ...wholeDocument, itemId };
109
+ }
110
+ const locator = playerViewLocator(item.select.locator);
111
+ if (locator === null)
112
+ return {
113
+ ok: false,
114
+ code: "invalid_control",
115
+ itemId,
116
+ message: "The locator has an invalid format",
117
+ };
118
+ const selected = snapshot.query({
119
+ kind: "select_node",
120
+ document: selector,
121
+ locator,
122
+ });
123
+ if (selected.kind === "error")
124
+ return { ...selectionFailure(selected, handle), itemId };
125
+ if (selected.kind !== "select_node")
126
+ return {
127
+ ok: false,
128
+ code: "unresolved_selector",
129
+ itemId,
130
+ message: "The exact selector cannot currently be resolved",
131
+ };
132
+ return {
133
+ ok: true,
134
+ item: {
135
+ id: itemId,
136
+ label: item.label,
137
+ value: selectedNodeValue(selected),
138
+ },
139
+ };
140
+ }
141
+ /**
142
+ * Control files address documents by the same handle the model sees. The
143
+ * prompt compiler already resolves `@shortRef`; a view that silently rendered
144
+ * nothing for it was the only place the two disagreed.
145
+ */
146
+ function playerViewDocumentSelector(handle) {
147
+ return handle.startsWith("@")
148
+ ? { shortRef: handle.slice(1) }
149
+ : { documentId: handle };
150
+ }
151
+ function resolveWholeDocument(snapshot, selector, handle, expectedCodec) {
152
+ const read = snapshot.query({
153
+ kind: "read_document",
154
+ document: selector,
155
+ maxBytes: maxDocumentReadBytes,
156
+ });
157
+ if (read.kind === "error")
158
+ return selectionFailure(read, handle);
159
+ if (read.kind !== "read_document")
160
+ return {
161
+ ok: false,
162
+ code: "unresolved_selector",
163
+ message: "The exact selector cannot currently be resolved",
164
+ };
165
+ if (expectedCodec !== undefined && read.codec !== expectedCodec)
166
+ return {
167
+ ok: false,
168
+ code: "unresolved_selector",
169
+ message: "The selector codec does not match the target document",
170
+ };
171
+ if (read.codec === "markdown")
172
+ return readWholeMarkdown(snapshot, selector, handle, read);
173
+ return selectYamlRoot(snapshot, selector, handle);
174
+ }
175
+ function readWholeMarkdown(snapshot, selector, handle, firstPage) {
176
+ let page = firstPage;
177
+ const chunks = [page.body];
178
+ while (!page.page.complete) {
179
+ if (page.page.nextCursor === null)
180
+ return {
181
+ ok: false,
182
+ code: "unresolved_selector",
183
+ message: "The exact selector cannot currently be resolved",
184
+ };
185
+ const nextPage = snapshot.query({
186
+ kind: "read_document",
187
+ document: selector,
188
+ maxBytes: maxDocumentReadBytes,
189
+ cursor: page.page.nextCursor,
190
+ });
191
+ if (nextPage.kind === "error")
192
+ return selectionFailure(nextPage, handle);
193
+ if (nextPage.kind !== "read_document" || nextPage.codec !== "markdown")
194
+ return {
195
+ ok: false,
196
+ code: "unresolved_selector",
197
+ message: "The exact selector cannot currently be resolved",
198
+ };
199
+ chunks.push(nextPage.body);
200
+ page = nextPage;
201
+ }
202
+ return { ok: true, value: chunks.join("").trim() };
203
+ }
204
+ function selectYamlRoot(snapshot, selector, handle) {
205
+ const selected = snapshot.query({
206
+ kind: "select_node",
207
+ document: selector,
208
+ locator: { yaml: [] },
209
+ });
210
+ if (selected.kind === "error")
211
+ return selectionFailure(selected, handle);
212
+ if (selected.kind !== "select_node" || selected.node.codec !== "yaml")
213
+ return {
214
+ ok: false,
215
+ code: "unresolved_selector",
216
+ message: "The exact selector cannot currently be resolved",
217
+ };
218
+ return { ok: true, value: selectedNodeValue(selected) };
219
+ }
220
+ function selectedNodeValue(selected) {
221
+ return selected.node.codec === "markdown"
222
+ ? selected.node.markdown
223
+ : projectPlayerValue(selected.node.value);
224
+ }
225
+ function selectionFailure(failure, handle) {
226
+ const codes = new Set(failure.diagnostics.map(({ code }) => code));
227
+ if (codes.has("capacity_exceeded"))
228
+ return {
229
+ ok: false,
230
+ code: "capacity_exceeded",
231
+ message: "UI capacity reached: selected nodes exceed query capacity",
232
+ };
233
+ if (codes.has("document_not_found"))
234
+ return {
235
+ ok: false,
236
+ code: "unresolved_selector",
237
+ message: `Document not found: ${handle}`,
238
+ };
239
+ if (codes.has("document_reference_invalid"))
240
+ return {
241
+ ok: false,
242
+ code: "unresolved_selector",
243
+ message: "The explicit document reference cannot currently be resolved",
244
+ };
245
+ if (codes.has("locator_invalid"))
246
+ return {
247
+ ok: false,
248
+ code: "unresolved_selector",
249
+ message: "The selector codec does not match the target document",
250
+ };
251
+ return {
252
+ ok: false,
253
+ code: "unresolved_selector",
254
+ message: "The exact selector cannot currently be resolved",
255
+ };
256
+ }
257
+ function playerViewLocator(value) {
258
+ if (!isRecord(value) || Object.keys(value).length !== 1)
259
+ return null;
260
+ if (isStringArray(value.yaml))
261
+ return { yaml: [...value.yaml] };
262
+ if (isNonEmptyStringArray(value.markdown))
263
+ return { markdown: [...value.markdown] };
264
+ return null;
265
+ }
266
+ function isWholeMarkdownLocator(value) {
267
+ return (isRecord(value) &&
268
+ Object.keys(value).length === 1 &&
269
+ isStringArray(value.markdown) &&
270
+ value.markdown.length === 0);
271
+ }
272
+ function projectPlayerValue(value, depth = 0) {
273
+ if (depth > maxRenderedDepth)
274
+ return "[UI depth limit reached]";
275
+ if (isWorldDocumentValueArray(value))
276
+ return value.map((entry) => projectPlayerValue(entry, depth + 1));
277
+ if (!isRecord(value))
278
+ return value;
279
+ if (isProjectedReference(value))
280
+ return {
281
+ $ref: value.$ref,
282
+ title: value.target.title,
283
+ ref: value.target.shortRef,
284
+ };
285
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [
286
+ key,
287
+ projectPlayerValue(child, depth + 1),
288
+ ]));
289
+ }
290
+ function isStringArray(value) {
291
+ return (Array.isArray(value) &&
292
+ value.every((part) => typeof part === "string"));
293
+ }
294
+ function isNonEmptyStringArray(value) {
295
+ return (isStringArray(value) &&
296
+ value.length > 0 &&
297
+ value.every((part) => part.length > 0));
298
+ }
299
+ function isWorldDocumentValueArray(value) {
300
+ return Array.isArray(value);
301
+ }
302
+ function isProjectedReference(value) {
303
+ return (Object.keys(value).length === 2 &&
304
+ typeof value.$ref === "string" &&
305
+ isRecord(value.target) &&
306
+ typeof value.target.documentId === "string" &&
307
+ typeof value.target.shortRef === "string" &&
308
+ typeof value.target.title === "string");
309
+ }
310
+ function parseControlYaml(source) {
311
+ const document = parseDocument(source, {
312
+ schema: "core",
313
+ uniqueKeys: true,
314
+ strict: true,
315
+ });
316
+ if (document.errors.length > 0 || document.warnings.length > 0)
317
+ return undefined;
318
+ return document.toJS({ maxAliasCount: 0 });
319
+ }
320
+ function isRecord(value) {
321
+ return typeof value === "object" && value !== null && !Array.isArray(value);
322
+ }