@world-engines/project-host 0.1.0-alpha.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 (218) hide show
  1. package/LICENSE +46 -0
  2. package/assets/gui/WELogo-title.svg +11 -0
  3. package/assets/gui/app.js +1326 -0
  4. package/assets/gui/index.html +118 -0
  5. package/assets/gui/local-preview-asset-urls.js +61 -0
  6. package/assets/gui/local-preview-bridge.js +393 -0
  7. package/assets/gui/spatial-template-panel.js +233 -0
  8. package/assets/gui/styles.css +115 -0
  9. package/assets/gui/terrain-render.js +120 -0
  10. package/assets/gui/workspace-status.css +17 -0
  11. package/assets/gui/workspace-status.js +255 -0
  12. package/assets/gui/world-map-globe.js +187 -0
  13. package/assets/gui/world-map-worker.js +62 -0
  14. package/assets/gui/world-map.css +30 -0
  15. package/assets/gui/world-map.js +2466 -0
  16. package/dist/account-model-proxy.d.ts +21 -0
  17. package/dist/account-model-proxy.js +470 -0
  18. package/dist/authoring-browser-runtime.d.ts +7 -0
  19. package/dist/authoring-browser-runtime.js +110 -0
  20. package/dist/authoring-ingestion.d.ts +38 -0
  21. package/dist/authoring-ingestion.js +410 -0
  22. package/dist/authoring-model-connection.d.ts +166 -0
  23. package/dist/authoring-model-connection.js +757 -0
  24. package/dist/chinese-lexical.d.ts +152 -0
  25. package/dist/chinese-lexical.js +555 -0
  26. package/dist/chromium-preview-browser-driver.d.ts +43 -0
  27. package/dist/chromium-preview-browser-driver.js +575 -0
  28. package/dist/cli/download.d.ts +12 -0
  29. package/dist/cli/download.js +50 -0
  30. package/dist/cli/dump.d.ts +32 -0
  31. package/dist/cli/dump.js +91 -0
  32. package/dist/cli/ingest.d.ts +49 -0
  33. package/dist/cli/ingest.js +225 -0
  34. package/dist/cli/review.d.ts +10 -0
  35. package/dist/cli/review.js +44 -0
  36. package/dist/cli/upload.d.ts +57 -0
  37. package/dist/cli/upload.js +112 -0
  38. package/dist/cli-command.d.ts +60 -0
  39. package/dist/cli-command.js +613 -0
  40. package/dist/cli.d.ts +2 -0
  41. package/dist/cli.js +182 -0
  42. package/dist/desktop-authoring-runtime.d.ts +88 -0
  43. package/dist/desktop-authoring-runtime.js +1602 -0
  44. package/dist/desktop-sidecar.d.ts +11 -0
  45. package/dist/desktop-sidecar.js +143 -0
  46. package/dist/document-dump.d.ts +113 -0
  47. package/dist/document-dump.js +431 -0
  48. package/dist/document-graph-extractor.d.ts +125 -0
  49. package/dist/document-graph-extractor.js +588 -0
  50. package/dist/document-ingestion-journal.d.ts +68 -0
  51. package/dist/document-ingestion-journal.js +471 -0
  52. package/dist/document-ingestion.d.ts +183 -0
  53. package/dist/document-ingestion.js +976 -0
  54. package/dist/document-query-path-proof.d.ts +30 -0
  55. package/dist/document-query-path-proof.js +34 -0
  56. package/dist/document-query-path-renderer.d.ts +50 -0
  57. package/dist/document-query-path-renderer.js +75 -0
  58. package/dist/document-query-proof-paths.d.ts +22 -0
  59. package/dist/document-query-proof-paths.js +71 -0
  60. package/dist/document-query.d.ts +97 -0
  61. package/dist/document-query.js +1106 -0
  62. package/dist/document-relation-role.d.ts +14 -0
  63. package/dist/document-relation-role.js +86 -0
  64. package/dist/document-source.d.ts +107 -0
  65. package/dist/document-source.js +255 -0
  66. package/dist/embedding-cache.d.ts +35 -0
  67. package/dist/embedding-cache.js +317 -0
  68. package/dist/frozen-review-local-pipeline.d.ts +88 -0
  69. package/dist/frozen-review-local-pipeline.js +609 -0
  70. package/dist/frozen-review-source-capture.d.ts +15 -0
  71. package/dist/frozen-review-source-capture.js +551 -0
  72. package/dist/frozen-review-wasm-encryptor.d.ts +16 -0
  73. package/dist/frozen-review-wasm-encryptor.js +72 -0
  74. package/dist/frozen-review.d.ts +131 -0
  75. package/dist/frozen-review.js +802 -0
  76. package/dist/graph-traversal.d.ts +23 -0
  77. package/dist/graph-traversal.js +100 -0
  78. package/dist/gui/graph-layout-worker.js +1 -0
  79. package/dist/gui/graph-layout.wasm +0 -0
  80. package/dist/gui/graph.css +1 -0
  81. package/dist/gui/graph.js +76 -0
  82. package/dist/gui/plane-generation.js +112 -0
  83. package/dist/gui/plane-generator.js +310 -0
  84. package/dist/gui/plane-generator_bg.wasm +0 -0
  85. package/dist/gui/procedural-city.js +595 -0
  86. package/dist/gui/road-generation.js +129 -0
  87. package/dist/gui/road-parcels.js +1584 -0
  88. package/dist/gui/score.css +1 -0
  89. package/dist/gui/score.js +54 -0
  90. package/dist/gui/spatial-runtime.js +1231 -0
  91. package/dist/gui/spatial-templates.js +200 -0
  92. package/dist/gui/trigger.css +1 -0
  93. package/dist/gui/trigger.js +142 -0
  94. package/dist/harness-callback-transport.d.ts +17 -0
  95. package/dist/harness-callback-transport.js +128 -0
  96. package/dist/harness-document-ingestion.d.ts +77 -0
  97. package/dist/harness-document-ingestion.js +343 -0
  98. package/dist/index.d.ts +43 -0
  99. package/dist/index.js +43 -0
  100. package/dist/ingestion-source-reconciliation.d.ts +88 -0
  101. package/dist/ingestion-source-reconciliation.js +253 -0
  102. package/dist/ladybug-runtime.d.ts +117 -0
  103. package/dist/ladybug-runtime.js +886 -0
  104. package/dist/local-account-embedding.d.ts +34 -0
  105. package/dist/local-account-embedding.js +294 -0
  106. package/dist/local-gallery.d.ts +35 -0
  107. package/dist/local-gallery.js +227 -0
  108. package/dist/local-gui-auth.d.ts +96 -0
  109. package/dist/local-gui-auth.js +703 -0
  110. package/dist/local-gui-contract.d.ts +53 -0
  111. package/dist/local-gui-contract.js +5 -0
  112. package/dist/local-gui-image-assets.d.ts +17 -0
  113. package/dist/local-gui-image-assets.js +96 -0
  114. package/dist/local-gui-preview-client.d.ts +3 -0
  115. package/dist/local-gui-preview-client.js +111 -0
  116. package/dist/local-gui-score.d.ts +72 -0
  117. package/dist/local-gui-score.js +184 -0
  118. package/dist/local-gui-server.d.ts +10 -0
  119. package/dist/local-gui-server.js +987 -0
  120. package/dist/local-gui-version.d.ts +17 -0
  121. package/dist/local-gui-version.js +147 -0
  122. package/dist/local-gui-workspace.d.ts +14 -0
  123. package/dist/local-gui-workspace.js +171 -0
  124. package/dist/local-model-settings.d.ts +28 -0
  125. package/dist/local-model-settings.js +124 -0
  126. package/dist/local-preview-assets.d.ts +28 -0
  127. package/dist/local-preview-assets.js +124 -0
  128. package/dist/local-preview-bound-resources.d.ts +12 -0
  129. package/dist/local-preview-bound-resources.js +80 -0
  130. package/dist/local-preview-message-store.d.ts +63 -0
  131. package/dist/local-preview-message-store.js +214 -0
  132. package/dist/local-preview-player-read-models.d.ts +47 -0
  133. package/dist/local-preview-player-read-models.js +90 -0
  134. package/dist/local-preview-projections.d.ts +61 -0
  135. package/dist/local-preview-projections.js +117 -0
  136. package/dist/local-preview-runtime.d.ts +105 -0
  137. package/dist/local-preview-runtime.js +553 -0
  138. package/dist/local-preview-session.d.ts +73 -0
  139. package/dist/local-preview-session.js +395 -0
  140. package/dist/local-preview-source.d.ts +40 -0
  141. package/dist/local-preview-source.js +192 -0
  142. package/dist/local-preview-view-events.d.ts +101 -0
  143. package/dist/local-preview-view-events.js +179 -0
  144. package/dist/local-preview-view-state-sqlite.d.ts +24 -0
  145. package/dist/local-preview-view-state-sqlite.js +263 -0
  146. package/dist/local-preview-view-state.d.ts +93 -0
  147. package/dist/local-preview-view-state.js +108 -0
  148. package/dist/local-preview-vite-config.d.ts +6 -0
  149. package/dist/local-preview-vite-config.js +122 -0
  150. package/dist/local-preview-world-effects.d.ts +9 -0
  151. package/dist/local-preview-world-effects.js +55 -0
  152. package/dist/local-preview-worldline-store.d.ts +47 -0
  153. package/dist/local-preview-worldline-store.js +282 -0
  154. package/dist/local-preview-worldlines.d.ts +95 -0
  155. package/dist/local-preview-worldlines.js +136 -0
  156. package/dist/mcp-async-dispatcher.d.ts +3 -0
  157. package/dist/mcp-async-dispatcher.js +129 -0
  158. package/dist/mcp-line-queue.d.ts +14 -0
  159. package/dist/mcp-line-queue.js +184 -0
  160. package/dist/mcp-pipe-internal.d.ts +6 -0
  161. package/dist/mcp-pipe-internal.js +151 -0
  162. package/dist/mcp-pipe-response.d.ts +14 -0
  163. package/dist/mcp-pipe-response.js +50 -0
  164. package/dist/mcp-stdio-response.d.ts +12 -0
  165. package/dist/mcp-stdio-response.js +27 -0
  166. package/dist/mcp.d.ts +27 -0
  167. package/dist/mcp.js +194 -0
  168. package/dist/prepared-embedding-cache.d.ts +38 -0
  169. package/dist/prepared-embedding-cache.js +238 -0
  170. package/dist/preview-browser-adapter.d.ts +53 -0
  171. package/dist/preview-browser-adapter.js +171 -0
  172. package/dist/preview-runtime.d.ts +67 -0
  173. package/dist/preview-runtime.js +460 -0
  174. package/dist/project-auth-transport.d.ts +27 -0
  175. package/dist/project-auth-transport.js +121 -0
  176. package/dist/project-auth.d.ts +51 -0
  177. package/dist/project-auth.js +108 -0
  178. package/dist/project-git-capture-recovery.d.ts +16 -0
  179. package/dist/project-git-capture-recovery.js +347 -0
  180. package/dist/project-git-cli.d.ts +5 -0
  181. package/dist/project-git-cli.js +40 -0
  182. package/dist/project-git-scenario-source.d.ts +10 -0
  183. package/dist/project-git-scenario-source.js +240 -0
  184. package/dist/project-git-view-source.d.ts +13 -0
  185. package/dist/project-git-view-source.js +134 -0
  186. package/dist/project-git.d.ts +97 -0
  187. package/dist/project-git.js +656 -0
  188. package/dist/project-host.d.ts +59 -0
  189. package/dist/project-host.js +471 -0
  190. package/dist/project-writer-lease.d.ts +18 -0
  191. package/dist/project-writer-lease.js +430 -0
  192. package/dist/public-error.d.ts +7 -0
  193. package/dist/public-error.js +61 -0
  194. package/dist/retrieval-benchmark.d.ts +46 -0
  195. package/dist/retrieval-benchmark.js +58 -0
  196. package/dist/safe-project-path.d.ts +8 -0
  197. package/dist/safe-project-path.js +93 -0
  198. package/dist/scenario-writer.d.ts +32 -0
  199. package/dist/scenario-writer.js +191 -0
  200. package/dist/transfer/download.d.ts +159 -0
  201. package/dist/transfer/download.js +598 -0
  202. package/dist/transfer/transport.d.ts +26 -0
  203. package/dist/transfer/transport.js +1 -0
  204. package/dist/transfer/upload.d.ts +162 -0
  205. package/dist/transfer/upload.js +354 -0
  206. package/dist/transfer-types.d.ts +95 -0
  207. package/dist/transfer-types.js +1 -0
  208. package/dist/transfer.d.ts +18 -0
  209. package/dist/transfer.js +273 -0
  210. package/dist/trigger-runtime.d.ts +121 -0
  211. package/dist/trigger-runtime.js +153 -0
  212. package/dist/vendor/wasm-core/asset-hashes.json +5 -0
  213. package/dist/vendor/wasm-core/wasm_core.d.ts +490 -0
  214. package/dist/vendor/wasm-core/wasm_core.js +1 -0
  215. package/dist/vendor/wasm-core/wasm_core_bg.wasm +0 -0
  216. package/dist/view-watcher.d.ts +9 -0
  217. package/dist/view-watcher.js +34 -0
  218. package/package.json +110 -0
@@ -0,0 +1,471 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { open, readFile, rename, rm, stat } from "node:fs/promises";
3
+ import { basename, dirname, join } from "node:path";
4
+ import { assertSafeExistingProjectPath, assertSafeProjectParent, ensureSafeProjectDirectory, safeProjectPath, } from "./safe-project-path.js";
5
+ export class DocumentIngestionJournalError extends Error {
6
+ code;
7
+ name = "DocumentIngestionJournalError";
8
+ constructor(code) {
9
+ super(`document_ingestion_journal:${code}`);
10
+ this.code = code;
11
+ }
12
+ }
13
+ const SHA256 = /^[0-9a-f]{64}$/u;
14
+ const PHASE_ORDER = ["extracting", "ready_to_commit", "committing", "committed"];
15
+ function fail(code) {
16
+ throw new DocumentIngestionJournalError(code);
17
+ }
18
+ function text(value, maximum = 512) {
19
+ if (typeof value !== "string" || value.length === 0 || value.length > maximum || value.trim() !== value
20
+ || value.normalize("NFC") !== value || /[\u0000-\u001f\u007f]/u.test(value))
21
+ fail("invalid_input");
22
+ return value;
23
+ }
24
+ function sha256(value) {
25
+ return createHash("sha256").update(value).digest("hex");
26
+ }
27
+ function json(value) {
28
+ let encoded;
29
+ try {
30
+ encoded = JSON.stringify(value);
31
+ }
32
+ catch {
33
+ fail("invalid_input");
34
+ }
35
+ if (encoded === undefined)
36
+ fail("invalid_input");
37
+ return encoded;
38
+ }
39
+ function parseJson(value) {
40
+ try {
41
+ return JSON.parse(value);
42
+ }
43
+ catch {
44
+ fail("corrupt");
45
+ }
46
+ }
47
+ async function ensureDirectory(root, relative) {
48
+ try {
49
+ return await ensureSafeProjectDirectory(root, relative);
50
+ }
51
+ catch (error) {
52
+ if (error.code !== "EEXIST")
53
+ throw error;
54
+ return await assertSafeExistingProjectPath(root, join(root, ...relative.split("/")));
55
+ }
56
+ }
57
+ async function atomicWrite(root, path, bytes) {
58
+ await assertSafeProjectParent(root, path);
59
+ const temporary = join(dirname(path), `.${basename(path)}.${randomUUID()}.tmp`);
60
+ const handle = await open(temporary, "wx");
61
+ try {
62
+ await handle.writeFile(bytes, "utf8");
63
+ await handle.sync();
64
+ }
65
+ catch (error) {
66
+ await handle.close();
67
+ await rm(temporary, { force: true });
68
+ throw error;
69
+ }
70
+ await handle.close();
71
+ try {
72
+ await assertSafeProjectParent(root, path);
73
+ await rename(temporary, path);
74
+ }
75
+ catch (error) {
76
+ await rm(temporary, { force: true });
77
+ throw error;
78
+ }
79
+ }
80
+ function identityFrom(options) {
81
+ const method = options.method === undefined ? undefined : Object.freeze({
82
+ version: text(options.method.version),
83
+ sha256: options.method.sha256,
84
+ promptVersion: text(options.method.promptVersion),
85
+ schemaVersion: text(options.method.schemaVersion),
86
+ mergeVersion: text(options.method.mergeVersion),
87
+ embeddingPolicy: text(options.method.embeddingPolicy),
88
+ ...(options.method.validatorDiagnosticsVersion === undefined ? {} : { validatorDiagnosticsVersion: text(options.method.validatorDiagnosticsVersion) }),
89
+ ...(options.method.repairPolicyVersion === undefined ? {} : { repairPolicyVersion: text(options.method.repairPolicyVersion) }),
90
+ });
91
+ const identity = Object.freeze({
92
+ operationId: text(options.operationId),
93
+ sourceByteSha256: options.sourceByteSha256,
94
+ planSha256: options.planSha256,
95
+ modelId: text(options.modelId),
96
+ expectedProjectRevision: options.expectedProjectRevision,
97
+ ...(method === undefined ? {} : { method }),
98
+ });
99
+ if (!SHA256.test(identity.sourceByteSha256) || !SHA256.test(identity.planSha256)
100
+ || !Number.isSafeInteger(identity.expectedProjectRevision) || identity.expectedProjectRevision < 0
101
+ || (method !== undefined && !SHA256.test(method.sha256)))
102
+ fail("invalid_input");
103
+ return identity;
104
+ }
105
+ function newManifest(identity) {
106
+ return {
107
+ schema_version: 1,
108
+ identity: {
109
+ operation_id: identity.operationId,
110
+ source_byte_sha256: identity.sourceByteSha256,
111
+ plan_sha256: identity.planSha256,
112
+ model_id: identity.modelId,
113
+ expected_project_revision: identity.expectedProjectRevision,
114
+ ...(identity.method === undefined ? {} : { method: {
115
+ version: identity.method.version,
116
+ sha256: identity.method.sha256,
117
+ prompt_version: identity.method.promptVersion,
118
+ schema_version: identity.method.schemaVersion,
119
+ merge_version: identity.method.mergeVersion,
120
+ embedding_policy: identity.method.embeddingPolicy,
121
+ ...(identity.method.validatorDiagnosticsVersion === undefined ? {} : { validator_diagnostics_version: identity.method.validatorDiagnosticsVersion }),
122
+ ...(identity.method.repairPolicyVersion === undefined ? {} : { repair_policy_version: identity.method.repairPolicyVersion }),
123
+ } }),
124
+ },
125
+ phase: "extracting",
126
+ segments: {},
127
+ failures: {},
128
+ commit_receipt_json: null,
129
+ commit_receipt_sha256: null,
130
+ };
131
+ }
132
+ function validateManifest(value) {
133
+ if (typeof value !== "object" || value === null || Array.isArray(value))
134
+ fail("corrupt");
135
+ const manifest = value;
136
+ const identity = manifest.identity;
137
+ const method = identity?.method;
138
+ if (manifest.schema_version !== 1 || identity === undefined || typeof identity !== "object"
139
+ || typeof identity.operation_id !== "string" || !SHA256.test(identity.source_byte_sha256 ?? "")
140
+ || !SHA256.test(identity.plan_sha256 ?? "") || typeof identity.model_id !== "string"
141
+ || !Number.isSafeInteger(identity.expected_project_revision) || !PHASE_ORDER.includes(manifest.phase)
142
+ || (method !== undefined && (typeof method !== "object" || typeof method.version !== "string" || !SHA256.test(method.sha256 ?? "")
143
+ || typeof method.prompt_version !== "string" || typeof method.schema_version !== "string"
144
+ || typeof method.merge_version !== "string" || typeof method.embedding_policy !== "string"
145
+ || (method.validator_diagnostics_version !== undefined && typeof method.validator_diagnostics_version !== "string")
146
+ || (method.repair_policy_version !== undefined && typeof method.repair_policy_version !== "string")))
147
+ || typeof manifest.segments !== "object" || manifest.segments === null || Array.isArray(manifest.segments)
148
+ || typeof manifest.failures !== "object" || manifest.failures === null || Array.isArray(manifest.failures)
149
+ || !Object.values(manifest.segments).every((entry) => typeof entry === "object" && entry !== null && SHA256.test(entry.payload_sha256))
150
+ || !Object.values(manifest.failures).every((entry) => typeof entry === "object" && entry !== null
151
+ && Number.isSafeInteger(entry.attempts) && entry.attempts > 0 && typeof entry.code === "string" && typeof entry.retryable === "boolean")
152
+ || (manifest.commit_receipt_json !== null && typeof manifest.commit_receipt_json !== "string")
153
+ || (manifest.commit_receipt_sha256 !== null && !SHA256.test(manifest.commit_receipt_sha256 ?? "")))
154
+ fail("corrupt");
155
+ if ((manifest.commit_receipt_json === null) !== (manifest.commit_receipt_sha256 === null)
156
+ || (manifest.commit_receipt_json !== null && sha256(manifest.commit_receipt_json) !== manifest.commit_receipt_sha256))
157
+ fail("corrupt");
158
+ return manifest;
159
+ }
160
+ function assertIdentity(manifest, expected) {
161
+ const actual = manifest.identity;
162
+ if (actual.operation_id !== expected.operationId || actual.source_byte_sha256 !== expected.sourceByteSha256
163
+ || actual.plan_sha256 !== expected.planSha256 || actual.model_id !== expected.modelId
164
+ || actual.expected_project_revision !== expected.expectedProjectRevision)
165
+ fail("identity_mismatch");
166
+ if (expected.method === undefined) {
167
+ if (actual.method !== undefined)
168
+ fail("identity_mismatch");
169
+ return;
170
+ }
171
+ if (actual.method === undefined || actual.method.version !== expected.method.version || actual.method.sha256 !== expected.method.sha256
172
+ || actual.method.prompt_version !== expected.method.promptVersion || actual.method.schema_version !== expected.method.schemaVersion
173
+ || actual.method.merge_version !== expected.method.mergeVersion || actual.method.embedding_policy !== expected.method.embeddingPolicy
174
+ || actual.method.validator_diagnostics_version !== expected.method.validatorDiagnosticsVersion
175
+ || actual.method.repair_policy_version !== expected.method.repairPolicyVersion)
176
+ fail("identity_mismatch");
177
+ }
178
+ function methodFromManifest(manifest) {
179
+ const method = manifest.identity.method;
180
+ return method === undefined ? undefined : Object.freeze({
181
+ version: method.version,
182
+ sha256: method.sha256,
183
+ promptVersion: method.prompt_version,
184
+ schemaVersion: method.schema_version,
185
+ mergeVersion: method.merge_version,
186
+ embeddingPolicy: method.embedding_policy,
187
+ ...(method.validator_diagnostics_version === undefined ? {} : { validatorDiagnosticsVersion: method.validator_diagnostics_version }),
188
+ ...(method.repair_policy_version === undefined ? {} : { repairPolicyVersion: method.repair_policy_version }),
189
+ });
190
+ }
191
+ function pidAlive(pid) {
192
+ if (!Number.isSafeInteger(pid) || pid <= 0)
193
+ return false;
194
+ try {
195
+ process.kill(pid, 0);
196
+ return true;
197
+ }
198
+ catch (error) {
199
+ return error.code === "EPERM";
200
+ }
201
+ }
202
+ async function acquireLease(root, relativeDirectory, operationId) {
203
+ const path = await safeProjectPath(root, `${relativeDirectory}/active.lock`);
204
+ const token = randomUUID();
205
+ const body = `${JSON.stringify({ schema_version: 1, token, operation_id: operationId, pid: process.pid })}\n`;
206
+ for (let attempt = 0; attempt < 2; attempt += 1) {
207
+ try {
208
+ const handle = await open(path, "wx");
209
+ try {
210
+ await handle.writeFile(body, "utf8");
211
+ await handle.sync();
212
+ }
213
+ finally {
214
+ await handle.close();
215
+ }
216
+ return Object.freeze({ path, token });
217
+ }
218
+ catch (error) {
219
+ if (error.code !== "EEXIST")
220
+ throw error;
221
+ await assertSafeExistingProjectPath(root, path);
222
+ let existing;
223
+ try {
224
+ existing = JSON.parse(await readFile(path, "utf8"));
225
+ }
226
+ catch {
227
+ fail("corrupt");
228
+ }
229
+ if (existing.schema_version !== 1 || typeof existing.token !== "string" || existing.operation_id !== operationId
230
+ || !Number.isSafeInteger(existing.pid))
231
+ fail("corrupt");
232
+ if (pidAlive(existing.pid))
233
+ fail("busy");
234
+ const before = await stat(path);
235
+ const recoveredPath = await safeProjectPath(root, `${relativeDirectory}/recovered-lock-${existing.token}.json`);
236
+ const after = await stat(path);
237
+ if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs)
238
+ fail("lease_uncertain");
239
+ await assertSafeProjectParent(root, path);
240
+ await rename(path, recoveredPath);
241
+ }
242
+ }
243
+ fail("busy");
244
+ }
245
+ async function releaseLease(root, lease) {
246
+ await assertSafeExistingProjectPath(root, lease.path);
247
+ let value;
248
+ try {
249
+ value = JSON.parse(await readFile(lease.path, "utf8"));
250
+ }
251
+ catch {
252
+ fail("lease_uncertain");
253
+ }
254
+ if (value.token !== lease.token)
255
+ fail("lease_uncertain");
256
+ await rm(lease.path);
257
+ }
258
+ /** 只读 operational 状态;不获取 writer lease,也不修复或改写 manifest。 */
259
+ export async function readDocumentIngestionJournalStatus(projectRoot, operationId) {
260
+ const canonicalId = text(operationId);
261
+ const path = await safeProjectPath(projectRoot, `.worldengine/staging/document-ingestion/${sha256(canonicalId)}/manifest.json`);
262
+ await assertSafeExistingProjectPath(projectRoot, path);
263
+ const manifest = validateManifest(JSON.parse(await readFile(path, "utf8")));
264
+ if (manifest.identity.operation_id !== canonicalId)
265
+ fail("identity_mismatch");
266
+ const method = methodFromManifest(manifest);
267
+ return Object.freeze({
268
+ operationId: canonicalId, sourceByteSha256: manifest.identity.source_byte_sha256,
269
+ planSha256: manifest.identity.plan_sha256, modelId: manifest.identity.model_id,
270
+ expectedProjectRevision: manifest.identity.expected_project_revision, phase: manifest.phase,
271
+ ...(method === undefined ? {} : { method }),
272
+ completedSegmentIds: Object.freeze(Object.keys(manifest.segments).sort()),
273
+ failedSegments: Object.freeze(Object.entries(manifest.failures).sort(([left], [right]) => left.localeCompare(right))
274
+ .map(([id, failure]) => Object.freeze({ id, ...failure }))),
275
+ commitReceipt: manifest.commit_receipt_json === null ? null : parseJson(manifest.commit_receipt_json),
276
+ });
277
+ }
278
+ export async function openDocumentIngestionJournal(options) {
279
+ const identity = identityFrom(options);
280
+ const operationKey = sha256(identity.operationId);
281
+ const relativeDirectory = `.worldengine/staging/document-ingestion/${operationKey}`;
282
+ await ensureDirectory(options.projectRoot, relativeDirectory);
283
+ await ensureDirectory(options.projectRoot, `${relativeDirectory}/segments`);
284
+ const lease = await acquireLease(options.projectRoot, relativeDirectory, identity.operationId);
285
+ const manifestPath = await safeProjectPath(options.projectRoot, `${relativeDirectory}/manifest.json`);
286
+ let manifest;
287
+ try {
288
+ try {
289
+ await assertSafeExistingProjectPath(options.projectRoot, manifestPath);
290
+ manifest = validateManifest(JSON.parse(await readFile(manifestPath, "utf8")));
291
+ assertIdentity(manifest, identity);
292
+ }
293
+ catch (error) {
294
+ if (error.code !== "ENOENT")
295
+ throw error;
296
+ manifest = newManifest(identity);
297
+ await atomicWrite(options.projectRoot, manifestPath, `${JSON.stringify(manifest)}\n`);
298
+ }
299
+ }
300
+ catch (error) {
301
+ await releaseLease(options.projectRoot, lease);
302
+ throw error;
303
+ }
304
+ let closed = false;
305
+ let closing = false;
306
+ let closePromise;
307
+ let queue = Promise.resolve();
308
+ const requireOpen = () => { if (closed || closing)
309
+ fail("invalid_transition"); };
310
+ const persist = async () => atomicWrite(options.projectRoot, manifestPath, `${JSON.stringify(manifest)}\n`);
311
+ const exclusive = async (action) => {
312
+ requireOpen();
313
+ const previous = queue;
314
+ let release;
315
+ queue = new Promise((resolve) => { release = resolve; });
316
+ await previous;
317
+ try {
318
+ return await action();
319
+ }
320
+ finally {
321
+ release();
322
+ }
323
+ };
324
+ const segmentPath = async (id) => safeProjectPath(options.projectRoot, `${relativeDirectory}/segments/${sha256(text(id))}.json`);
325
+ return Object.freeze({
326
+ async getStatus() {
327
+ return exclusive(async () => Object.freeze({
328
+ ...identity,
329
+ phase: manifest.phase,
330
+ completedSegmentIds: Object.freeze(Object.keys(manifest.segments).sort()),
331
+ failedSegments: Object.freeze(Object.entries(manifest.failures).sort(([left], [right]) => left.localeCompare(right))
332
+ .map(([id, failure]) => Object.freeze({ id, ...failure }))),
333
+ commitReceipt: manifest.commit_receipt_json === null ? null : parseJson(manifest.commit_receipt_json),
334
+ }));
335
+ },
336
+ async readSegment(id) {
337
+ return exclusive(async () => {
338
+ const canonicalId = text(id);
339
+ const path = await segmentPath(canonicalId);
340
+ try {
341
+ await assertSafeExistingProjectPath(options.projectRoot, path);
342
+ }
343
+ catch (error) {
344
+ if (error.code === "ENOENT")
345
+ return null;
346
+ throw error;
347
+ }
348
+ let envelope;
349
+ try {
350
+ envelope = JSON.parse(await readFile(path, "utf8"));
351
+ }
352
+ catch {
353
+ fail("corrupt");
354
+ }
355
+ if (envelope.schema_version !== 1 || envelope.id !== canonicalId || typeof envelope.payload_json !== "string"
356
+ || typeof envelope.payload_sha256 !== "string" || sha256(envelope.payload_json) !== envelope.payload_sha256)
357
+ fail("corrupt");
358
+ const listed = manifest.segments[canonicalId];
359
+ if (listed !== undefined && listed.payload_sha256 !== envelope.payload_sha256)
360
+ fail("corrupt");
361
+ if (listed === undefined) {
362
+ manifest.segments[canonicalId] = { payload_sha256: envelope.payload_sha256 };
363
+ delete manifest.failures[canonicalId];
364
+ await persist();
365
+ }
366
+ return parseJson(envelope.payload_json);
367
+ });
368
+ },
369
+ async writeSegment(id, payload) {
370
+ return exclusive(async () => {
371
+ if (manifest.phase !== "extracting")
372
+ fail("invalid_transition");
373
+ const canonicalId = text(id);
374
+ const payloadJson = json(payload);
375
+ const payloadSha256 = sha256(payloadJson);
376
+ const path = await segmentPath(canonicalId);
377
+ try {
378
+ await assertSafeExistingProjectPath(options.projectRoot, path);
379
+ let existing;
380
+ try {
381
+ existing = JSON.parse(await readFile(path, "utf8"));
382
+ }
383
+ catch {
384
+ fail("corrupt");
385
+ }
386
+ if (existing.schema_version !== 1 || existing.id !== canonicalId || typeof existing.payload_json !== "string"
387
+ || typeof existing.payload_sha256 !== "string" || sha256(existing.payload_json) !== existing.payload_sha256)
388
+ fail("corrupt");
389
+ if (existing.payload_sha256 !== payloadSha256)
390
+ fail("identity_mismatch");
391
+ }
392
+ catch (error) {
393
+ if (error.code !== "ENOENT")
394
+ throw error;
395
+ await atomicWrite(options.projectRoot, path, `${JSON.stringify({ schema_version: 1, id: canonicalId, payload_json: payloadJson, payload_sha256: payloadSha256 })}\n`);
396
+ }
397
+ manifest.segments[canonicalId] = { payload_sha256: payloadSha256 };
398
+ delete manifest.failures[canonicalId];
399
+ await persist();
400
+ return Object.freeze({ payloadSha256 });
401
+ });
402
+ },
403
+ async clearSegmentFailure(id) {
404
+ await exclusive(async () => {
405
+ if (manifest.phase !== "extracting")
406
+ fail("invalid_transition");
407
+ const canonicalId = text(id);
408
+ delete manifest.failures[canonicalId];
409
+ await persist();
410
+ });
411
+ },
412
+ async recordSegmentFailure(id, failure) {
413
+ await exclusive(async () => {
414
+ if (manifest.phase !== "extracting")
415
+ fail("invalid_transition");
416
+ const canonicalId = text(id);
417
+ const code = text(failure.code, 128);
418
+ const previousAttempts = manifest.failures[canonicalId]?.attempts ?? 0;
419
+ const attempts = failure.attempts ?? previousAttempts + 1;
420
+ if (!Number.isSafeInteger(attempts) || attempts < previousAttempts || attempts < 1)
421
+ fail("invalid_input");
422
+ manifest.failures[canonicalId] = { attempts, code, retryable: failure.retryable === true };
423
+ await persist();
424
+ });
425
+ },
426
+ async markPhase(phase) {
427
+ await exclusive(async () => {
428
+ const current = PHASE_ORDER.indexOf(manifest.phase);
429
+ const next = PHASE_ORDER.indexOf(phase);
430
+ if (next < current || next > current + 1)
431
+ fail("invalid_transition");
432
+ if (phase === "ready_to_commit" && Object.keys(manifest.failures).length > 0)
433
+ fail("invalid_transition");
434
+ manifest.phase = phase;
435
+ await persist();
436
+ });
437
+ },
438
+ async recordCommitReceipt(receipt) {
439
+ await exclusive(async () => {
440
+ if (manifest.phase !== "committing")
441
+ fail("invalid_transition");
442
+ const receiptJson = json(receipt);
443
+ manifest.commit_receipt_json = receiptJson;
444
+ manifest.commit_receipt_sha256 = sha256(receiptJson);
445
+ manifest.phase = "committed";
446
+ await persist();
447
+ });
448
+ },
449
+ async close() {
450
+ if (closed)
451
+ return;
452
+ if (closePromise !== undefined)
453
+ return closePromise;
454
+ closing = true;
455
+ closePromise = (async () => {
456
+ await queue;
457
+ await releaseLease(options.projectRoot, lease);
458
+ closed = true;
459
+ closing = false;
460
+ })();
461
+ try {
462
+ await closePromise;
463
+ }
464
+ catch (error) {
465
+ closing = false;
466
+ closePromise = undefined;
467
+ throw error;
468
+ }
469
+ },
470
+ });
471
+ }
@@ -0,0 +1,183 @@
1
+ import { type AuthoringToolCall } from "@world-engines/authoring-bridge";
2
+ import type { AuthoringModelConnection } from "./authoring-model-connection.js";
3
+ import { extractDocumentGraphWindow, type GraphSegment, type GraphSegmentationIssue, type KnownGraphEntity } from "./document-graph-extractor.js";
4
+ import { openDocumentIngestionJournal, type DocumentIngestionJournalStatus } from "./document-ingestion-journal.js";
5
+ import { type DocumentSource, type DocumentSourceOptions } from "./document-source.js";
6
+ import { createCachedEmbeddingPort } from "./embedding-cache.js";
7
+ type JsonValue = null | boolean | number | string | JsonValue[] | {
8
+ [key: string]: JsonValue;
9
+ };
10
+ export interface LosslessDocumentIngestionHost {
11
+ inspect(): Promise<Readonly<{
12
+ project_revision: number;
13
+ }>>;
14
+ dispatchTool(call: AuthoringToolCall): Promise<unknown>;
15
+ }
16
+ export type LosslessDocumentInput = Readonly<{
17
+ bytes: Uint8Array;
18
+ scopeId: string;
19
+ sourceId: string;
20
+ sourceRevision: string;
21
+ sourcePath?: string;
22
+ sourceStoragePath?: string;
23
+ }>;
24
+ export type StagedDocumentInputDescriptor = Readonly<{
25
+ schemaVersion: 1 | 2;
26
+ operationId: string;
27
+ input: LosslessDocumentInput;
28
+ sourceOptions?: DocumentSourceOptions;
29
+ planSha256: string;
30
+ method: DocumentIngestionMethodIdentity;
31
+ }>;
32
+ export type DocumentIngestionMethodVersion = "legacy-v1" | "deferred-v2" | "adaptive-repair-v3" | "semantic-negation-v4" | "harness-v1";
33
+ export type DocumentIngestionMethodIdentity = Readonly<{
34
+ version: DocumentIngestionMethodVersion;
35
+ sha256: string;
36
+ promptVersion: string;
37
+ schemaVersion: string;
38
+ mergeVersion: string;
39
+ embeddingPolicy: "immediate" | "deferred_review";
40
+ validatorDiagnosticsVersion?: string;
41
+ repairPolicyVersion?: string;
42
+ }>;
43
+ export type DocumentEmbeddingStatus = Readonly<{
44
+ status: "pending" | "complete";
45
+ providerId: string;
46
+ modelId: string;
47
+ dimensions: number;
48
+ hits: number;
49
+ misses: number;
50
+ remoteTexts: number;
51
+ writes: number;
52
+ }>;
53
+ export type LosslessDocumentIngestionReceipt = Readonly<{
54
+ schemaVersion: 1;
55
+ operationId: string;
56
+ documentId?: string;
57
+ status: "staged" | "committed" | "already_committed";
58
+ rawByteSha256: string;
59
+ rawByteLength: number;
60
+ sourcePath?: string;
61
+ sourceStoragePath?: string;
62
+ roundtripVerified: boolean;
63
+ sourceCoverage: Readonly<{
64
+ status: "complete";
65
+ utf16Length: number;
66
+ unitCount: number;
67
+ coveredUnitCount: number;
68
+ }>;
69
+ graphStatus: "pending" | "partial" | "complete";
70
+ processedWindows: number;
71
+ pendingWindows: readonly string[];
72
+ failedWindows: readonly Readonly<{
73
+ id: string;
74
+ code: string;
75
+ retryable: boolean;
76
+ attempts: number;
77
+ }>[];
78
+ nodeCount: number;
79
+ edgeCount: number;
80
+ embeddingStatus: DocumentEmbeddingStatus;
81
+ previousProjectRevision?: number;
82
+ nextProjectRevision?: number;
83
+ }>;
84
+ export type DocumentGraphEmbeddingText = Readonly<{
85
+ id: string;
86
+ kind: "chunk" | "entity" | "statement" | "edge_assertion";
87
+ text: string;
88
+ }>;
89
+ /** 提审 consumer 的只读输入;这里只冻结文本与 identity,不执行 embedding 或写图。 */
90
+ export type DocumentGraphEmbeddingPreparation = Readonly<{
91
+ schemaVersion: 1;
92
+ operationId: string;
93
+ method: DocumentIngestionMethodIdentity;
94
+ sourceIdentity: Readonly<{
95
+ scopeId: string;
96
+ sourceId: string;
97
+ sourceRevision: string;
98
+ sourceSha256: string;
99
+ }>;
100
+ planSha256: string;
101
+ graphSha256: string;
102
+ graphStatus: "complete";
103
+ texts: readonly DocumentGraphEmbeddingText[];
104
+ }>;
105
+ type Dependencies = Readonly<{
106
+ extractWindow: (input: Parameters<typeof extractDocumentGraphWindow>[0] & Readonly<{
107
+ ingestionMethod: DocumentIngestionMethodIdentity;
108
+ adaptiveRepair?: AdaptiveRepairPreviousAttempt;
109
+ }>) => ReturnType<typeof extractDocumentGraphWindow>;
110
+ openJournal: typeof openDocumentIngestionJournal;
111
+ createEmbeddingCache: typeof createCachedEmbeddingPort;
112
+ }>;
113
+ export type AdaptiveRepairPreviousAttempt = Readonly<{
114
+ attempt: number;
115
+ diagnosticFingerprint: string;
116
+ repeatedDiagnosticFingerprint: boolean;
117
+ reasons: readonly string[];
118
+ validLocalNodeIds: readonly string[];
119
+ allowedEvidenceUnitIds: readonly string[];
120
+ segmentationIssues?: readonly GraphSegmentationIssue[];
121
+ }>;
122
+ /** 仅发布已持久化进度;不含正文、路径、凭据或 provider 原始响应。 */
123
+ export type LosslessDocumentIngestionProgress = Readonly<{
124
+ schemaVersion: 1;
125
+ operationId: string;
126
+ event: "source_staged" | "window_completed" | "window_failed" | "phase_changed";
127
+ phase: DocumentIngestionJournalStatus["phase"];
128
+ totalWindows: number;
129
+ processedWindows: number;
130
+ windowId?: string;
131
+ failure?: Readonly<{
132
+ code: string;
133
+ retryable: boolean;
134
+ }>;
135
+ }>;
136
+ export type LosslessDocumentIngestionStatus = DocumentIngestionJournalStatus & Readonly<{
137
+ sourcePath?: string;
138
+ sourceStoragePath?: string;
139
+ }>;
140
+ export type LosslessDocumentIngestionCapability = Readonly<{
141
+ projectRoot: string;
142
+ modelConnection: AuthoringModelConnection;
143
+ embeddingOrigin?: string;
144
+ signal?: AbortSignal;
145
+ concurrency?: number;
146
+ sourceOptions?: DocumentSourceOptions;
147
+ /** 仅用于显式创建 legacy operation;恢复时 descriptor 是唯一方法 authority。 */
148
+ methodVersion?: DocumentIngestionMethodVersion;
149
+ /** 等待 consumer 完成投递;consumer 失败会在安全 checkpoint 边界终止本次调用。 */
150
+ onProgress?: (progress: LosslessDocumentIngestionProgress) => void | Promise<void>;
151
+ /** 仅供模块测试替换真实外部端口;产品调用必须省略。 */
152
+ dependencies?: Partial<Dependencies>;
153
+ }>;
154
+ export declare const DOCUMENT_INGESTION_CHECKPOINT_IDS: Readonly<{
155
+ sourceDescriptorId: "__document_source_descriptor__";
156
+ partialAttemptPrefix: "__document_partial_attempt__:";
157
+ retryCompletePrefix: "__document_retry_complete__:";
158
+ harnessAcceptancePrefix: "__document_harness_acceptance__:";
159
+ maxPartialAttempts: 3;
160
+ }>;
161
+ export declare function readDocumentIngestionCheckpoint(projectRoot: string, operationId: string, segmentId: string): Promise<JsonValue>;
162
+ /** descriptor 是输入元数据,不能算作一个已完成的模型窗口。 */
163
+ export declare function readLosslessDocumentStatus(projectRoot: string, operationId: string): Promise<LosslessDocumentIngestionStatus>;
164
+ export declare function resumeLosslessDocument(host: LosslessDocumentIngestionHost, operationId: string, capability: LosslessDocumentIngestionCapability): Promise<LosslessDocumentIngestionReceipt>;
165
+ /** 上传流或原文件已释放时,以 operationId 只读恢复 journal 中已校验的不可变原始输入。 */
166
+ export declare function readStagedDocumentInput(projectRoot: string, operationId: string): Promise<StagedDocumentInputDescriptor>;
167
+ export declare function graphSegment(value: JsonValue, windowId: string): GraphSegment;
168
+ export declare function knownDocumentGraphEntities(source: DocumentSource, segments: readonly GraphSegment[]): readonly KnownGraphEntity[];
169
+ export declare function prepareDocumentGraphEmbedding(projectRoot: string, operationId: string): Promise<DocumentGraphEmbeddingPreparation>;
170
+ /**
171
+ * 全文导入的唯一编排入口。模型与 embedding 结果先进入 journal/cache;只有所有窗口
172
+ * 都有已校验 checkpoint 时,才以最初捕获的 revision 做一次原子 world_apply。
173
+ */
174
+ export declare function ingestLosslessDocument(host: LosslessDocumentIngestionHost, input: LosslessDocumentInput, capability: LosslessDocumentIngestionCapability): Promise<LosslessDocumentIngestionReceipt>;
175
+ /** harness 工作流的稳定 adapter:复用唯一 ingestion journal、合并与 CAS commit authority。 */
176
+ export declare function ingestHarnessDocument(host: LosslessDocumentIngestionHost, input: LosslessDocumentInput, capability: Omit<LosslessDocumentIngestionCapability, "methodVersion" | "modelConnection"> & Readonly<{
177
+ modelConnection: AuthoringModelConnection;
178
+ }>): Promise<LosslessDocumentIngestionReceipt>;
179
+ /** operation descriptor 是恢复时的唯一 method authority;不得由 CLI 参数原地升级。 */
180
+ export declare function resumeHarnessDocument(host: LosslessDocumentIngestionHost, operationId: string, capability: Omit<LosslessDocumentIngestionCapability, "methodVersion" | "modelConnection"> & Readonly<{
181
+ modelConnection: AuthoringModelConnection;
182
+ }>): Promise<LosslessDocumentIngestionReceipt>;
183
+ export {};