@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,14 @@
1
+ export type DocumentRelationRole = "structural" | "semantic" | "unknown";
2
+ export interface DocumentRelationRoleInput {
3
+ readonly relation: Readonly<{
4
+ kind: string;
5
+ props: Readonly<Record<string, unknown>>;
6
+ }>;
7
+ readonly endpoints: Readonly<{
8
+ srcKind?: string | undefined;
9
+ dstKind?: string | undefined;
10
+ }>;
11
+ }
12
+ export declare function classifyDocumentRelationRole(input: DocumentRelationRoleInput): DocumentRelationRole;
13
+ /** `undefined` 表示端点尚未回读或结构形状不完整;调用方不得把它当成语义边。 */
14
+ export declare function isStructuralRelation(input: DocumentRelationRoleInput): boolean | undefined;
@@ -0,0 +1,86 @@
1
+ const RESERVED_RELATION_KINDS = new Set([
2
+ "part_of",
3
+ "next_chunk",
4
+ "mentions",
5
+ "supports",
6
+ "describes",
7
+ "describes_source",
8
+ "describes_target",
9
+ ]);
10
+ const SEMANTIC_ENTITY_KINDS = new Set([
11
+ "world_document_concept",
12
+ "world_document_event",
13
+ "world_document_object",
14
+ "world_document_organization",
15
+ "world_document_person",
16
+ "world_document_place",
17
+ ]);
18
+ const DIRECT_EVIDENCE_KEYS = ["end_offset", "offset_semantics", "quote", "spans", "start_offset", "unit_ids"];
19
+ function hasSemanticAssertion(props) {
20
+ return typeof props.assertion === "string" && props.assertion.trim().length > 0
21
+ && props.evidence !== null && typeof props.evidence === "object" && !Array.isArray(props.evidence)
22
+ && Array.isArray(props.source_chunk_ids) && props.source_chunk_ids.length > 0
23
+ && props.source_chunk_ids.every((id) => typeof id === "string" && id.length > 0);
24
+ }
25
+ function hasExactOrdinal(props) {
26
+ return Object.keys(props).length === 1 && Number.isSafeInteger(props.ordinal) && props.ordinal >= 0;
27
+ }
28
+ function isEmptyProps(props) {
29
+ return Object.keys(props).length === 0;
30
+ }
31
+ function hasExactDirectEvidence(props) {
32
+ const keys = Object.keys(props).sort();
33
+ if (keys.length !== DIRECT_EVIDENCE_KEYS.length || keys.some((key, index) => key !== [...DIRECT_EVIDENCE_KEYS].sort()[index]))
34
+ return false;
35
+ if (typeof props.quote !== "string" || !Number.isSafeInteger(props.start_offset) || !Number.isSafeInteger(props.end_offset)
36
+ || props.start_offset < 0 || props.end_offset <= props.start_offset
37
+ || props.offset_semantics !== "envelope_only")
38
+ return false;
39
+ if (!Array.isArray(props.unit_ids) || props.unit_ids.length < 1 || !props.unit_ids.every((id) => typeof id === "string" && id.length > 0))
40
+ return false;
41
+ return Array.isArray(props.spans) && props.spans.length > 0 && props.spans.every((value) => {
42
+ if (value === null || typeof value !== "object" || Array.isArray(value))
43
+ return false;
44
+ const span = value;
45
+ return Object.keys(span).sort().join("\0") === "end_offset\0quote\0start_offset\0unit_id"
46
+ && typeof span.unit_id === "string" && typeof span.quote === "string"
47
+ && Number.isSafeInteger(span.start_offset) && Number.isSafeInteger(span.end_offset)
48
+ && span.start_offset >= 0 && span.end_offset > span.start_offset;
49
+ });
50
+ }
51
+ export function classifyDocumentRelationRole(input) {
52
+ const kind = input.relation.kind.toLowerCase();
53
+ if (!RESERVED_RELATION_KINDS.has(kind))
54
+ return "semantic";
55
+ const { srcKind, dstKind } = input.endpoints;
56
+ if (srcKind === undefined || dstKind === undefined)
57
+ return "unknown";
58
+ if (hasSemanticAssertion(input.relation.props))
59
+ return "semantic";
60
+ if (kind === "part_of" && srcKind === "world_source_chunk" && dstKind === "world_source_document") {
61
+ return hasExactOrdinal(input.relation.props) ? "structural" : "unknown";
62
+ }
63
+ if (kind === "mentions" && srcKind === "world_source_chunk" && SEMANTIC_ENTITY_KINDS.has(dstKind)) {
64
+ return hasExactDirectEvidence(input.relation.props) ? "structural" : "unknown";
65
+ }
66
+ if (kind === "next_chunk" && srcKind === "world_source_chunk" && dstKind === "world_source_chunk") {
67
+ return isEmptyProps(input.relation.props) ? "structural" : "unknown";
68
+ }
69
+ if (kind === "supports" && srcKind === "world_source_chunk"
70
+ && (dstKind === "world_document_statement" || dstKind === "world_document_edge_assertion")) {
71
+ return hasExactDirectEvidence(input.relation.props) ? "structural" : "unknown";
72
+ }
73
+ if (kind === "describes" && srcKind === "world_document_statement" && SEMANTIC_ENTITY_KINDS.has(dstKind)) {
74
+ return isEmptyProps(input.relation.props) ? "structural" : "unknown";
75
+ }
76
+ if ((kind === "describes_source" || kind === "describes_target")
77
+ && srcKind === "world_document_edge_assertion" && SEMANTIC_ENTITY_KINDS.has(dstKind)) {
78
+ return isEmptyProps(input.relation.props) ? "structural" : "unknown";
79
+ }
80
+ return "semantic";
81
+ }
82
+ /** `undefined` 表示端点尚未回读或结构形状不完整;调用方不得把它当成语义边。 */
83
+ export function isStructuralRelation(input) {
84
+ const role = classifyDocumentRelationRole(input);
85
+ return role === "unknown" ? undefined : role === "structural";
86
+ }
@@ -0,0 +1,107 @@
1
+ export declare const DOCUMENT_SOURCE_SCHEMA_VERSION: 1;
2
+ export declare const DOCUMENT_PLAN_SCHEMA_VERSION: 1;
3
+ export type DocumentUnitKind = "control" | "chapter_header" | "sentence" | "layout";
4
+ export type DocumentUnit = Readonly<{
5
+ id: string;
6
+ kind: DocumentUnitKind;
7
+ startOffset: number;
8
+ endOffset: number;
9
+ text: string;
10
+ chapterId?: string;
11
+ }>;
12
+ export type DocumentWindow = Readonly<{
13
+ id: string;
14
+ coreUnitIds: readonly string[];
15
+ contextUnitIds: readonly string[];
16
+ chapterId?: string;
17
+ startOffset: number;
18
+ coreStartOffset: number;
19
+ coreEndOffset: number;
20
+ endOffset: number;
21
+ text: string;
22
+ }>;
23
+ export type DocumentSource = Readonly<{
24
+ schemaVersion: typeof DOCUMENT_SOURCE_SCHEMA_VERSION;
25
+ documentId: string;
26
+ sourceId: string;
27
+ sourceRevision: string;
28
+ scopeId: string;
29
+ sourceSha256: string;
30
+ byteLength: number;
31
+ utf16Length: number;
32
+ originalBytesBase64: string;
33
+ utf8Bom: "present" | "absent";
34
+ /** 所有 offset 均以包含 U+FEFF BOM(若存在)的 decoded UTF-16 code unit 为域。 */
35
+ offsetDomain: "decoded_utf16_including_bom";
36
+ units: readonly DocumentUnit[];
37
+ windows: readonly DocumentWindow[];
38
+ }>;
39
+ export type DocumentSourceOptions = Readonly<{
40
+ targetWindowUtf16?: number;
41
+ maximumWindowUtf16?: number;
42
+ maximumAtomicUtf16?: number;
43
+ includePreviousSentenceContext?: boolean;
44
+ }>;
45
+ export type BuildDocumentSourceInput = Readonly<{
46
+ bytes: Uint8Array;
47
+ sourceId: string;
48
+ sourceRevision: string;
49
+ scopeId: string;
50
+ options?: DocumentSourceOptions;
51
+ }>;
52
+ export type DocumentPlan = Readonly<{
53
+ schemaVersion: typeof DOCUMENT_PLAN_SCHEMA_VERSION;
54
+ documentId: string;
55
+ segments: readonly Readonly<{
56
+ startUnitId: string;
57
+ endUnitId: string;
58
+ title: string;
59
+ }>[];
60
+ }>;
61
+ export type ValidatedDocumentSegment = Readonly<{
62
+ id: string;
63
+ startUnitId: string;
64
+ endUnitId: string;
65
+ title: string;
66
+ chapterId?: string;
67
+ startOffset: number;
68
+ endOffset: number;
69
+ indexText: string;
70
+ }>;
71
+ export type ValidatedDocumentPlan = Readonly<{
72
+ schemaVersion: typeof DOCUMENT_PLAN_SCHEMA_VERSION;
73
+ documentId: string;
74
+ segments: readonly ValidatedDocumentSegment[];
75
+ }>;
76
+ export type WorldSourceDocumentNode = Readonly<{
77
+ kind: "world_source_document";
78
+ id: string;
79
+ props: Readonly<{
80
+ scopeId: string;
81
+ sourceId: string;
82
+ sourceRevision: string;
83
+ sourceSha256: string;
84
+ byteLength: number;
85
+ utf16Length: number;
86
+ utf8Bom: "present" | "absent";
87
+ offsetDomain: "decoded_utf16_including_bom";
88
+ }>;
89
+ }>;
90
+ export type WorldSourceChunkNode = Readonly<{
91
+ kind: "world_source_chunk";
92
+ id: string;
93
+ props: Readonly<{
94
+ documentId: string;
95
+ ordinal: number;
96
+ title: string;
97
+ startUnitId: string;
98
+ endUnitId: string;
99
+ startOffset: number;
100
+ endOffset: number;
101
+ index_text: string;
102
+ }>;
103
+ }>;
104
+ /** 深 Module 的唯一构建 Interface:fatal decode、无损校验、结构切分与窗口化一次完成。 */
105
+ export declare function buildDocumentSource(input: BuildDocumentSourceInput): DocumentSource;
106
+ export declare function documentSourceBytes(document: DocumentSource): Uint8Array;
107
+ export declare function validateDocumentPlan(document: DocumentSource, plan: DocumentPlan): ValidatedDocumentPlan;
@@ -0,0 +1,255 @@
1
+ import { createHash } from "node:crypto";
2
+ export const DOCUMENT_SOURCE_SCHEMA_VERSION = 1;
3
+ export const DOCUMENT_PLAN_SCHEMA_VERSION = 1;
4
+ // 章节标记必须位于行首,或紧随任意标题与既有斜线分隔符;不依赖文章/书籍名称。
5
+ const HEADER = /^(?:[^\//\r\n]+[\//]\s*)?第[0-9〇○零一二三四五六七八九十百千两]+[回章节](?:\s|$)/u;
6
+ // 单个 CRLF 是一个 line break;禁止 regexp 回溯把它误拆成 CR + LF 两个空行。
7
+ const BLANK_SEPARATOR = /(?:\r\n|\r(?!\n)|(?<!\r)\n)[\t \u3000]*(?:\r\n|\r(?!\n)|(?<!\r)\n)+/gu;
8
+ const SENTENCE_SEGMENTER = new Intl.Segmenter("zh-Hans", { granularity: "sentence" });
9
+ const GRAPHEME_SEGMENTER = new Intl.Segmenter("zh-Hans", { granularity: "grapheme" });
10
+ function invariant(condition, code) {
11
+ if (!condition)
12
+ throw new Error(code);
13
+ }
14
+ function identifier(value, code) {
15
+ invariant(typeof value === "string" && value.length > 0 && value.length <= 512, code);
16
+ invariant(value.trim() === value && value.normalize("NFC") === value && !/[\0\r\n]/u.test(value), code);
17
+ return value;
18
+ }
19
+ function sha256(bytes) {
20
+ return createHash("sha256").update(bytes).digest("hex");
21
+ }
22
+ function equalBytes(left, right) {
23
+ if (left.byteLength !== right.byteLength)
24
+ return false;
25
+ return left.every((value, index) => value === right[index]);
26
+ }
27
+ function chapterHeaders(text) {
28
+ const headers = [];
29
+ let start = text.charCodeAt(0) === 0xfeff ? 1 : 0;
30
+ while (start < text.length) {
31
+ const newline = text.indexOf("\n", start);
32
+ let end = newline < 0 ? text.length : newline;
33
+ if (end > start && text.charCodeAt(end - 1) === 0x0d)
34
+ end -= 1;
35
+ const line = text.slice(start, end);
36
+ if (HEADER.test(line.trimStart()))
37
+ headers.push({ start, end });
38
+ if (newline < 0)
39
+ break;
40
+ start = newline + 1;
41
+ }
42
+ return headers;
43
+ }
44
+ function splitGraphemes(text, start, end, maximum) {
45
+ if (end - start <= maximum)
46
+ return [end];
47
+ const ends = [];
48
+ let cursor = start;
49
+ let candidate = start;
50
+ for (const segment of GRAPHEME_SEGMENTER.segment(text.slice(start, end))) {
51
+ const absoluteEnd = start + segment.index + segment.segment.length;
52
+ if (absoluteEnd - cursor > maximum) {
53
+ invariant(candidate > cursor, "document_grapheme_exceeds_atomic_limit");
54
+ ends.push(candidate);
55
+ cursor = candidate;
56
+ }
57
+ candidate = absoluteEnd;
58
+ }
59
+ if (cursor < end)
60
+ ends.push(end);
61
+ return ends;
62
+ }
63
+ function sentenceEnds(text, start, end, maximumAtomic) {
64
+ if (start === end)
65
+ return [];
66
+ const normalized = text.slice(start, end).replace(/[\r\n]/gu, " ");
67
+ const rawEnds = [...SENTENCE_SEGMENTER.segment(normalized)].map((segment) => start + segment.index + segment.segment.length);
68
+ const result = [];
69
+ let cursor = start;
70
+ for (const rawEnd of rawEnds.length > 0 ? rawEnds : [end]) {
71
+ for (const safeEnd of splitGraphemes(text, cursor, rawEnd, maximumAtomic))
72
+ result.push(safeEnd);
73
+ cursor = rawEnd;
74
+ }
75
+ if (result.at(-1) !== end)
76
+ result.push(...splitGraphemes(text, cursor, end, maximumAtomic));
77
+ return result;
78
+ }
79
+ function buildUnits(text, maximumAtomic) {
80
+ const headers = chapterHeaders(text);
81
+ const units = [];
82
+ let chapterIndex = 0;
83
+ let chapterId;
84
+ let cursor = 0;
85
+ const push = (kind, start, end) => {
86
+ if (end <= start)
87
+ return;
88
+ units.push(Object.freeze({
89
+ id: `unit-${String(units.length + 1).padStart(7, "0")}`,
90
+ kind,
91
+ startOffset: start,
92
+ endOffset: end,
93
+ text: text.slice(start, end),
94
+ ...(chapterId === undefined ? {} : { chapterId }),
95
+ }));
96
+ };
97
+ if (text.charCodeAt(0) === 0xfeff) {
98
+ if (headers[0]?.start === 1)
99
+ chapterId = "chapter-001";
100
+ push("control", 0, 1);
101
+ cursor = 1;
102
+ }
103
+ const boundaries = [...headers, { start: text.length, end: text.length }];
104
+ for (const header of boundaries) {
105
+ if (cursor < header.start) {
106
+ let bodyCursor = cursor;
107
+ BLANK_SEPARATOR.lastIndex = cursor;
108
+ let match;
109
+ while ((match = BLANK_SEPARATOR.exec(text)) !== null && match.index < header.start) {
110
+ const separatorEnd = Math.min(match.index + match[0].length, header.start);
111
+ for (const end of sentenceEnds(text, bodyCursor, match.index, maximumAtomic)) {
112
+ push("sentence", bodyCursor, end);
113
+ bodyCursor = end;
114
+ }
115
+ push("layout", match.index, separatorEnd);
116
+ bodyCursor = separatorEnd;
117
+ if (separatorEnd < match.index + match[0].length)
118
+ break;
119
+ }
120
+ for (const end of sentenceEnds(text, bodyCursor, header.start, maximumAtomic)) {
121
+ push("sentence", bodyCursor, end);
122
+ bodyCursor = end;
123
+ }
124
+ cursor = header.start;
125
+ }
126
+ if (header.start === text.length)
127
+ break;
128
+ chapterIndex += 1;
129
+ chapterId = `chapter-${String(chapterIndex).padStart(3, "0")}`;
130
+ push("chapter_header", header.start, header.end);
131
+ cursor = header.end;
132
+ }
133
+ if (cursor < text.length) {
134
+ for (const end of sentenceEnds(text, cursor, text.length, maximumAtomic)) {
135
+ push("sentence", cursor, end);
136
+ cursor = end;
137
+ }
138
+ }
139
+ invariant(units.length > 0 && units[0].startOffset === 0 && units.at(-1).endOffset === text.length, "document_unit_coverage_incomplete");
140
+ for (let index = 1; index < units.length; index += 1)
141
+ invariant(units[index - 1].endOffset === units[index].startOffset, "document_unit_coverage_gap");
142
+ return Object.freeze(units);
143
+ }
144
+ function buildWindows(text, units, options) {
145
+ const windows = [];
146
+ let cursor = 0;
147
+ while (cursor < units.length) {
148
+ const chapterId = units[cursor].chapterId;
149
+ let end = cursor;
150
+ let size = 0;
151
+ while (end < units.length && units[end].chapterId === chapterId) {
152
+ const nextSize = units[end].endOffset - units[end].startOffset;
153
+ if (end > cursor && (size >= options.targetWindowUtf16 || size + nextSize > options.maximumWindowUtf16))
154
+ break;
155
+ size += nextSize;
156
+ end += 1;
157
+ }
158
+ invariant(end > cursor, "document_window_no_progress");
159
+ let contextStart = cursor;
160
+ if (options.includePreviousSentenceContext && cursor > 0 && units[cursor - 1].chapterId === chapterId) {
161
+ contextStart = cursor - 1;
162
+ while (contextStart > 0 && units[contextStart].kind === "layout" && units[contextStart - 1].chapterId === chapterId)
163
+ contextStart -= 1;
164
+ }
165
+ const core = units.slice(cursor, end);
166
+ const context = units.slice(contextStart, cursor);
167
+ windows.push(Object.freeze({
168
+ id: `window-${String(windows.length + 1).padStart(6, "0")}`,
169
+ coreUnitIds: Object.freeze(core.map((unit) => unit.id)),
170
+ contextUnitIds: Object.freeze(context.map((unit) => unit.id)),
171
+ ...(chapterId === undefined ? {} : { chapterId }),
172
+ startOffset: units[contextStart].startOffset,
173
+ coreStartOffset: core[0].startOffset,
174
+ coreEndOffset: core.at(-1).endOffset,
175
+ endOffset: core.at(-1).endOffset,
176
+ text: text.slice(units[contextStart].startOffset, core.at(-1).endOffset),
177
+ }));
178
+ cursor = end;
179
+ }
180
+ invariant(windows.flatMap((window) => window.coreUnitIds).join("\0") === units.map((unit) => unit.id).join("\0"), "document_window_core_coverage_invalid");
181
+ return Object.freeze(windows);
182
+ }
183
+ /** 深 Module 的唯一构建 Interface:fatal decode、无损校验、结构切分与窗口化一次完成。 */
184
+ export function buildDocumentSource(input) {
185
+ invariant(input.bytes instanceof Uint8Array && input.bytes.byteLength > 0, "document_bytes_required");
186
+ const sourceId = identifier(input.sourceId, "document_source_id_invalid");
187
+ const sourceRevision = identifier(input.sourceRevision, "document_source_revision_invalid");
188
+ const scopeId = identifier(input.scopeId, "document_scope_id_invalid");
189
+ let text;
190
+ try {
191
+ text = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(input.bytes);
192
+ }
193
+ catch {
194
+ throw new Error("document_utf8_invalid");
195
+ }
196
+ invariant(equalBytes(new TextEncoder().encode(text), input.bytes), "document_utf8_roundtrip_mismatch");
197
+ const targetWindowUtf16 = input.options?.targetWindowUtf16 ?? 6_000;
198
+ const maximumWindowUtf16 = input.options?.maximumWindowUtf16 ?? 8_000;
199
+ const maximumAtomicUtf16 = input.options?.maximumAtomicUtf16 ?? 2_000;
200
+ invariant(Number.isSafeInteger(targetWindowUtf16) && targetWindowUtf16 >= 4_000 && targetWindowUtf16 <= 8_000, "document_target_window_invalid");
201
+ invariant(Number.isSafeInteger(maximumWindowUtf16) && maximumWindowUtf16 >= targetWindowUtf16 && maximumWindowUtf16 <= 12_000, "document_maximum_window_invalid");
202
+ invariant(Number.isSafeInteger(maximumAtomicUtf16) && maximumAtomicUtf16 >= 256 && maximumAtomicUtf16 <= targetWindowUtf16, "document_atomic_limit_invalid");
203
+ const units = buildUnits(text, maximumAtomicUtf16);
204
+ const windows = buildWindows(text, units, { targetWindowUtf16, maximumWindowUtf16, maximumAtomicUtf16, includePreviousSentenceContext: input.options?.includePreviousSentenceContext ?? true });
205
+ const sourceSha256 = sha256(input.bytes);
206
+ return Object.freeze({
207
+ schemaVersion: DOCUMENT_SOURCE_SCHEMA_VERSION,
208
+ documentId: sha256(new TextEncoder().encode(JSON.stringify([scopeId, sourceId, sourceRevision, sourceSha256]))),
209
+ sourceId, sourceRevision, scopeId, sourceSha256,
210
+ byteLength: input.bytes.byteLength, utf16Length: text.length,
211
+ originalBytesBase64: Buffer.from(input.bytes).toString("base64"),
212
+ utf8Bom: input.bytes[0] === 0xef && input.bytes[1] === 0xbb && input.bytes[2] === 0xbf ? "present" : "absent",
213
+ offsetDomain: "decoded_utf16_including_bom",
214
+ units, windows,
215
+ });
216
+ }
217
+ export function documentSourceBytes(document) {
218
+ const bytes = Uint8Array.from(Buffer.from(document.originalBytesBase64, "base64"));
219
+ invariant(bytes.byteLength === document.byteLength && sha256(bytes) === document.sourceSha256, "document_original_bytes_invalid");
220
+ const text = document.units.map((unit) => unit.text).join("");
221
+ invariant(text.length === document.utf16Length && equalBytes(new TextEncoder().encode(text), bytes), "document_unit_roundtrip_mismatch");
222
+ return bytes;
223
+ }
224
+ export function validateDocumentPlan(document, plan) {
225
+ invariant(plan.schemaVersion === DOCUMENT_PLAN_SCHEMA_VERSION, "document_plan_schema_invalid");
226
+ invariant(plan.documentId === document.documentId, "document_plan_document_mismatch");
227
+ invariant(Array.isArray(plan.segments) && plan.segments.length > 0, "document_plan_segments_required");
228
+ const unitById = new Map(document.units.map((unit) => [unit.id, unit]));
229
+ let expectedStartIndex = 0;
230
+ const segments = plan.segments.map((segment, segmentIndex) => {
231
+ const title = identifier(segment.title, "document_plan_segment_title_invalid");
232
+ const startUnit = unitById.get(segment.startUnitId);
233
+ const endUnit = unitById.get(segment.endUnitId);
234
+ invariant(startUnit !== undefined && endUnit !== undefined, "document_plan_segment_unit_unknown");
235
+ const startIndex = document.units.indexOf(startUnit);
236
+ const endIndex = document.units.indexOf(endUnit);
237
+ invariant(startIndex === expectedStartIndex, "document_plan_unit_gap_or_overlap");
238
+ invariant(endIndex >= startIndex, "document_plan_segment_reverse");
239
+ const covered = document.units.slice(startIndex, endIndex + 1);
240
+ invariant(new Set(covered.map((unit) => unit.chapterId ?? null)).size === 1, "document_plan_segment_crosses_chapter");
241
+ expectedStartIndex = endIndex + 1;
242
+ return Object.freeze({
243
+ id: `source-chunk-${String(segmentIndex + 1).padStart(6, "0")}`,
244
+ startUnitId: startUnit.id,
245
+ endUnitId: endUnit.id,
246
+ title,
247
+ ...(startUnit.chapterId === undefined ? {} : { chapterId: startUnit.chapterId }),
248
+ startOffset: startUnit.startOffset,
249
+ endOffset: endUnit.endOffset,
250
+ indexText: covered.map((unit) => unit.text).join(""),
251
+ });
252
+ });
253
+ invariant(expectedStartIndex === document.units.length, "document_plan_unit_coverage_incomplete");
254
+ return Object.freeze({ schemaVersion: DOCUMENT_PLAN_SCHEMA_VERSION, documentId: plan.documentId, segments: Object.freeze(segments) });
255
+ }
@@ -0,0 +1,35 @@
1
+ import { type AuthoringEmbeddingPort } from "./authoring-model-connection.js";
2
+ export type EmbeddingCacheErrorCode = "invalid_identity" | "invalid_input" | "corrupt" | "provider_response_invalid" | "aborted";
3
+ export declare class EmbeddingCacheError extends Error {
4
+ readonly code: EmbeddingCacheErrorCode;
5
+ readonly name = "EmbeddingCacheError";
6
+ constructor(code: EmbeddingCacheErrorCode);
7
+ }
8
+ export interface EmbeddingCacheCounts {
9
+ readonly hits: number;
10
+ readonly misses: number;
11
+ readonly remoteTexts: number;
12
+ readonly writes: number;
13
+ }
14
+ export interface CachedEmbeddingPort extends AuthoringEmbeddingPort {
15
+ readonly origin: string;
16
+ getCounts(): EmbeddingCacheCounts;
17
+ }
18
+ export type CachedEmbeddingReadOptions = Readonly<{
19
+ projectRoot: string;
20
+ embedding: AuthoringEmbeddingPort & Readonly<{
21
+ origin?: string;
22
+ }>;
23
+ origin?: string;
24
+ inputKind?: "document" | "query";
25
+ }>;
26
+ /** 只读查询现有 corpus cache;任一 miss 返回 null,绝不调用 provider 或创建目录。 */
27
+ export declare function readCachedEmbeddingVectors(options: CachedEmbeddingReadOptions, textSha256s: readonly string[]): Promise<readonly (readonly number[])[] | null>;
28
+ export declare function createCachedEmbeddingPort(options: Readonly<{
29
+ projectRoot: string;
30
+ embedding: AuthoringEmbeddingPort & Readonly<{
31
+ origin?: string;
32
+ }>;
33
+ /** 旧 port 尚未暴露 origin 时由 capability 显式注入;不得从 providerId/modelId 推断。 */
34
+ origin?: string;
35
+ }>): Promise<CachedEmbeddingPort>;