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,192 @@
1
+ import { defaultAppLocale, } from "../../protocol/appPreferences.js";
2
+ /**
3
+ * Runtime-owned prompt tool registry.
4
+ *
5
+ * The file-native play codec only stores names from this registry. Provider
6
+ * schemas and descriptions stay here so authors cannot fork the executable
7
+ * tool contract.
8
+ */
9
+ export const registeredRuntimeToolNames = [
10
+ "context_list",
11
+ "context_search",
12
+ "context_read",
13
+ "world_patch",
14
+ "world_create",
15
+ "artifact_emit",
16
+ "artifact_clear",
17
+ ];
18
+ /** Changes only when the executable Runtime tool universe or schemas change. */
19
+ export const runtimeToolRegistryRevision = "runtime-tools-v3";
20
+ export function defaultRuntimeToolDefinitionStrategy(provider) {
21
+ return provider === "openai_responses"
22
+ ? "native_allowed_subset"
23
+ : "runtime_gate";
24
+ }
25
+ /** Canonical Runtime-owned order for a set of registered names. */
26
+ export function canonicalRuntimeToolNames(names) {
27
+ const set = new Set(names);
28
+ return registeredRuntimeToolNames.filter((name) => set.has(name));
29
+ }
30
+ export function isRegisteredRuntimeToolName(value) {
31
+ return registeredRuntimeToolNames.includes(value);
32
+ }
33
+ /** Expand author names to the one Runtime schema/description registry. */
34
+ export function runtimeToolsForNames(names, locale = defaultAppLocale) {
35
+ const toolDefinitions = createToolDefinitions(locale);
36
+ return names.map((name) => ({
37
+ name,
38
+ description: toolDefinitions[name].description,
39
+ inputSchema: toolDefinitions[name].inputSchema,
40
+ }));
41
+ }
42
+ function createToolDefinitions(locale) {
43
+ const descriptions = toolDescriptions[locale];
44
+ const object = (properties, required) => ({
45
+ type: "object",
46
+ additionalProperties: false,
47
+ required,
48
+ properties,
49
+ });
50
+ const string = { type: "string", minLength: 1 };
51
+ const yamlLocator = object({
52
+ yaml: {
53
+ type: "array",
54
+ items: { type: ["string", "integer"] },
55
+ },
56
+ }, ["yaml"]);
57
+ const markdownLocator = object({ markdown: { type: "array", items: { type: "string" }, minItems: 1 } }, ["markdown"]);
58
+ const definitions = {
59
+ context_list: {
60
+ description: descriptions.context_list,
61
+ inputSchema: {
62
+ type: "object",
63
+ oneOf: [
64
+ object({
65
+ source: { const: "state" },
66
+ parent: string,
67
+ cursor: { type: ["string", "null"] },
68
+ limit: { type: "integer", minimum: 1, maximum: 100 },
69
+ }, ["source", "parent"]),
70
+ object({
71
+ source: { const: "history" },
72
+ order: { enum: ["newest_first", "oldest_first"] },
73
+ cursor: { type: ["string", "null"] },
74
+ limit: { type: "integer", minimum: 1, maximum: 100 },
75
+ }, ["source", "order"]),
76
+ ],
77
+ },
78
+ },
79
+ context_search: {
80
+ description: descriptions.context_search,
81
+ inputSchema: object({
82
+ source: { enum: ["state", "history"] },
83
+ query: { type: "string", minLength: 1, maxLength: 256 },
84
+ caseSensitive: { type: "boolean" },
85
+ within: string,
86
+ limit: { type: "integer", minimum: 1, maximum: 50 },
87
+ cursor: { type: ["string", "null"] },
88
+ }, ["source", "query"]),
89
+ },
90
+ context_read: {
91
+ description: descriptions.context_read,
92
+ inputSchema: object({
93
+ ref: string,
94
+ cursor: { type: ["string", "null"] },
95
+ maxBytes: { type: "integer", minimum: 4, maximum: 8192 },
96
+ }, ["ref"]),
97
+ },
98
+ world_patch: {
99
+ description: descriptions.world_patch,
100
+ inputSchema: object({
101
+ target: string,
102
+ edits: {
103
+ type: "array",
104
+ minItems: 1,
105
+ maxItems: 32,
106
+ items: {
107
+ oneOf: [
108
+ object({
109
+ op: { enum: ["add", "replace", "append"] },
110
+ locator: yamlLocator,
111
+ value: {},
112
+ }, ["op", "locator", "value"]),
113
+ object({ op: { const: "remove" }, locator: yamlLocator }, [
114
+ "op",
115
+ "locator",
116
+ ]),
117
+ object({
118
+ op: { enum: ["replace_section", "add_section"] },
119
+ locator: markdownLocator,
120
+ markdown: { type: "string" },
121
+ }, ["op", "locator", "markdown"]),
122
+ object({
123
+ op: { const: "rename_section" },
124
+ locator: markdownLocator,
125
+ title: string,
126
+ }, ["op", "locator", "title"]),
127
+ object({ op: { const: "remove_section" }, locator: markdownLocator }, ["op", "locator"]),
128
+ object({
129
+ op: { const: "set_metadata" },
130
+ title: string,
131
+ summary: string,
132
+ aliases: { type: "array", items: { type: "string" } },
133
+ }, ["op", "title", "summary", "aliases"]),
134
+ object({
135
+ op: { enum: ["replace_body", "replace_preamble"] },
136
+ markdown: { type: "string" },
137
+ }, ["op", "markdown"]),
138
+ ],
139
+ },
140
+ },
141
+ }, ["target", "edits"]),
142
+ },
143
+ world_create: {
144
+ description: descriptions.world_create,
145
+ inputSchema: object({
146
+ parent: string,
147
+ codec: { enum: ["yaml", "markdown"] },
148
+ refHint: { type: "string", pattern: "^[a-z][a-z0-9-]{1,31}$" },
149
+ title: { type: "string", minLength: 1, maxLength: 120 },
150
+ summary: { type: "string", minLength: 1, maxLength: 240 },
151
+ aliases: {
152
+ type: "array",
153
+ maxItems: 16,
154
+ items: { type: "string", minLength: 1, maxLength: 64 },
155
+ },
156
+ body: { type: "string", maxLength: 65536 },
157
+ }, ["parent", "codec", "refHint", "title", "summary", "aliases", "body"]),
158
+ },
159
+ artifact_emit: {
160
+ description: descriptions.artifact_emit,
161
+ inputSchema: object({
162
+ output: { type: "string", minLength: 1, maxLength: 128 },
163
+ payload: {},
164
+ }, ["output", "payload"]),
165
+ },
166
+ artifact_clear: {
167
+ description: descriptions.artifact_clear,
168
+ inputSchema: object({ output: { type: "string", minLength: 1, maxLength: 128 } }, ["output"]),
169
+ },
170
+ };
171
+ return definitions;
172
+ }
173
+ const toolDescriptions = {
174
+ en: {
175
+ context_list: 'List handles known to Runtime. State-directory requests have the fixed shape {source:"state", parent:"@dir-/"}; when descending, parent must be an @dir-* handle returned by a prior result. History requests have the fixed shape {source:"history", order:"newest_first"}. The two shapes are mutually exclusive: never send parent and order together.',
176
+ context_search: "Search literal source text in state or history. Zero matches means only that the supplied text had no literal match; it does not prove that a world fact does not exist. Player-visible narrative must not recount the search process. within accepts only an @document or @dir-* handle returned by Runtime.",
177
+ context_read: "Precisely read an @document, @node, or @history-message-* handle previously returned by Runtime. Read headings, page markers, metadata boundaries, and body boundaries in a whole-document result are not source text. Update metadata only as one set_metadata operation and update the writable body between the boundaries with locators. YAML bodies omit the $document technical header; Markdown bodies begin at the document's level-one heading. Never pass file paths, natural-language names, or an invented world/... value.",
178
+ world_patch: 'Update a document that has been read precisely. target must be an @short-ref. When title, summary, or aliases are stale, update all three with {op:"set_metadata",title,summary,aliases}, copying unchanged values from the read result. A YAML edit uses locator:{yaml:["field","child"]}, for example {op:"replace",locator:{yaml:["status"]},value:"new value"}. A YAML value may reference a whole document only as {$ref:"@short-ref"}; the short reference must come from a Runtime list, read, or create result. Never invent a document id. If no handle is available, use ordinary text when semantically correct or call world_create first and use its returned @short-ref. To add an item to the end of an existing sequence, use append with the locator pointing to that sequence; add creates only a map key or list index that does not exist. A Markdown locator excludes the document level-one heading: {markdown:["Responsibilities"]} points exactly to ## Responsibilities, and replace_section.markdown must begin with the same heading at the same level. Use replace_preamble for text after the level-one heading and before the first level-two heading; send only that text. Use replace_body to replace the whole Markdown body and retain the original level-one heading. Success reports only whether the document changed and does not echo the body. Call context_read again only when a later decision depends on Runtime\'s exact serialized body. Do not use path, JSON Pointer, set, or a file name.',
179
+ world_create: 'Create a document inside a state directory returned by Runtime. parent must be an @dir-* handle returned by context_list, such as @dir-/characters; use @dir-/ for the root. refHint is a lowercase ASCII short-reference hint, not a world/ path. A YAML body may reference a whole document only as {$ref:"@short-ref"}, and the short reference must come from a Runtime list, read, or create result. Never invent a document id.',
180
+ artifact_emit: "Submit an artifact declared in advance for this follow-up request. Supply only the output name and payload; Runtime fixes the channel, key, content type, renderer, retention policy, and authority meaning in the contract.",
181
+ artifact_clear: "Clear the projection of an artifact that this follow-up declared in advance and explicitly allows to be cleared. The call cannot choose a channel, key, or any other output contract.",
182
+ },
183
+ "zh-CN": {
184
+ context_list: '列出 Runtime 已知句柄。状态目录形状固定为 {source:"state", parent:"@dir-/"},继续下级时 parent 只能使用结果返回的 @dir-*;历史形状固定为 {source:"history", order:"newest_first"}。两种形状互斥,不要同时传 parent 和 order。',
185
+ context_search: "在 state 或 history 的原文字面中搜索。0 命中只表示给定文本没有字面命中,不证明世界事实不存在;玩家可见叙事不得复述搜索过程。within 只能使用 Runtime 已返回的 @文档或 @dir-* 句柄。",
186
+ context_read: "精确读取 Runtime 先前返回的 @文档、@节点或 @history-message-* 句柄。整文档结果中的读取标题、分页线、元信息边界和正文边界不属于源文;元信息只能用 set_metadata 整组更新,中间的可写正文用 locator 更新。YAML 正文不含 $document 技术头,Markdown 正文从文档 # 一级标题开始。不要传文件路径、自然语言名称或自行拼造的 world/...。",
187
+ world_patch: '更新已精确读取的文档。target 必须使用 @短引用;文档 title、summary 或 aliases 过时时用 {op:"set_metadata",title,summary,aliases} 整组更新,未改项照抄读取结果。YAML edit 使用 locator:{yaml:["字段","子字段"]},例如 {op:"replace",locator:{yaml:["情况"]},value:"新值"}。YAML value 需要引用整份文档时只能写 {$ref:"@短引用"},短引用必须来自 Runtime 的 list、read 或 create 结果;不得自行编造文档 id。没有可用句柄时,根据语义使用普通文本,或先 world_create 后使用其返回的 @短引用。向已存在的 sequence 末尾加一项必须使用 append 并把 locator 指向该 sequence;add 只创建尚不存在的 map key 或 list index。Markdown locator 不包含文档 # 一级标题:{markdown:["职责"]} 精确指向 ## 职责,replace_section.markdown 必须从同级同名标题开始。修改 # 标题下、第一个 ## 前的文字用 replace_preamble(只传该段文字);替换整个 Markdown 正文用 replace_body,且 markdown 必须保留原 # 一级标题。成功结果只报告文档是否发生变化,不回显正文;只有后续决策依赖 Runtime 序列化后的精确正文时才重新 context_read。不要使用 path、JSON Pointer、set 或文件名。',
188
+ world_create: '在 Runtime 返回的状态目录中创建文档。parent 必须是 context_list 返回的 @dir-*,例如 @dir-/characters;根目录使用 @dir-/。refHint 是小写 ASCII 短引用,不要传 world/ 路径。YAML body 需要引用整份文档时只能写 {$ref:"@短引用"},且短引用必须来自 Runtime 的 list、read 或 create 结果;不得自行编造文档 id。',
189
+ artifact_emit: "提交本次后置请求预先声明的产物。只能传 output name 与 payload;频道、key、内容类型、renderer、保存策略和权威含义由 Runtime contract 固定。",
190
+ artifact_clear: "清除本次后置请求预先声明且允许 clear 的产物投影;不能选择频道、key 或其他输出契约。",
191
+ },
192
+ };
@@ -0,0 +1,95 @@
1
+ import { Ajv } from "ajv";
2
+ const nonEmptyString = { type: "string", minLength: 1 };
3
+ const nonEmptyStringPath = {
4
+ type: "array",
5
+ minItems: 1,
6
+ items: nonEmptyString,
7
+ };
8
+ const documentMaterialSchema = {
9
+ type: "object",
10
+ additionalProperties: false,
11
+ required: ["kind", "document"],
12
+ properties: {
13
+ kind: { const: "document" },
14
+ document: nonEmptyString,
15
+ },
16
+ };
17
+ const nodeMaterialSchema = {
18
+ type: "object",
19
+ additionalProperties: false,
20
+ required: ["kind", "document", "locator"],
21
+ properties: {
22
+ kind: { const: "node" },
23
+ document: nonEmptyString,
24
+ locator: {
25
+ oneOf: [
26
+ {
27
+ type: "object",
28
+ additionalProperties: false,
29
+ required: ["yaml"],
30
+ properties: { yaml: nonEmptyStringPath },
31
+ },
32
+ {
33
+ type: "object",
34
+ additionalProperties: false,
35
+ required: ["markdown"],
36
+ properties: { markdown: nonEmptyStringPath },
37
+ },
38
+ ],
39
+ },
40
+ },
41
+ };
42
+ const historyMessageMaterialSchema = {
43
+ type: "object",
44
+ additionalProperties: false,
45
+ required: ["kind", "message"],
46
+ properties: {
47
+ kind: { const: "history_message" },
48
+ message: nonEmptyString,
49
+ },
50
+ };
51
+ const historyCommitMaterialSchema = {
52
+ type: "object",
53
+ additionalProperties: false,
54
+ required: ["kind", "commit"],
55
+ properties: {
56
+ kind: { const: "history_commit" },
57
+ commit: nonEmptyString,
58
+ },
59
+ };
60
+ export const materialListSchema = {
61
+ type: "array",
62
+ maxItems: 32,
63
+ items: {
64
+ oneOf: [
65
+ documentMaterialSchema,
66
+ nodeMaterialSchema,
67
+ historyMessageMaterialSchema,
68
+ historyCommitMaterialSchema,
69
+ ],
70
+ },
71
+ };
72
+ export class MaterialSelectionValidationError extends Error {
73
+ constructor(message) {
74
+ super(message);
75
+ this.name = "MaterialSelectionValidationError";
76
+ }
77
+ }
78
+ const ajv = new Ajv({ allErrors: true, strict: true });
79
+ const validateListSchema = ajv.compile(materialListSchema);
80
+ export function validateMaterialList(value) {
81
+ assertSchema(validateListSchema, value, "additional-materials list schema");
82
+ return structuredClone(value);
83
+ }
84
+ function assertSchema(validate, value, contract) {
85
+ if (validate(value))
86
+ return;
87
+ throw new MaterialSelectionValidationError(`${contract} rejected the input: ${renderErrors(validate.errors)}`);
88
+ }
89
+ function renderErrors(errors) {
90
+ if (errors === null || errors === undefined || errors.length === 0)
91
+ return "invalid shape";
92
+ return errors
93
+ .map(({ instancePath, message }) => `${instancePath || "/"} ${message ?? "is invalid"}`)
94
+ .join(";");
95
+ }