prts-terrarchive 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GAME_ASSETS.md +26 -0
- package/LICENSE +21 -0
- package/README.en.md +274 -0
- package/README.md +423 -0
- package/THIRD_PARTY_NOTICES.md +61 -0
- package/bin/bake-rhine-details.mjs +34 -0
- package/bin/install.js +273 -0
- package/bin/pack-map-assets.mjs +110 -0
- package/bin/preview-rhine.mjs +71 -0
- package/bin/rhine-detail-baker.ts +84 -0
- package/contracts/cloud_tools_v1.schema.json +243 -0
- package/contracts/corpus_i18n_v1.schema.json +179 -0
- package/contracts/corpus_tools_v1.schema.json +1801 -0
- package/cordis.patch.yml +32 -0
- package/docs/endfield-official-i18n.md +103 -0
- package/lib/client.js +3912 -0
- package/lib/endfield-map/map.js.br +0 -0
- package/lib/endfield-map/map.js.gz +0 -0
- package/lib/endfield-map/resources/map-00b0d0744a1b4404.json.br +0 -0
- package/lib/endfield-map/resources/map-00b0d0744a1b4404.json.gz +0 -0
- package/lib/endfield-map/resources/map-01616f534aaaeca1.png +0 -0
- package/lib/endfield-map/resources/map-1562e0dff4be3d23.png +0 -0
- package/lib/endfield-map/resources/map-160fe040f080f6a3.png +0 -0
- package/lib/endfield-map/resources/map-17b9d8be25dc2b39.json.br +0 -0
- package/lib/endfield-map/resources/map-17b9d8be25dc2b39.json.gz +0 -0
- package/lib/endfield-map/resources/map-1c4245c4e8b80c43.json.br +0 -0
- package/lib/endfield-map/resources/map-1c4245c4e8b80c43.json.gz +0 -0
- package/lib/endfield-map/resources/map-1d30baaaef3e29cc.png +0 -0
- package/lib/endfield-map/resources/map-21a61386af79cd12.json.br +0 -0
- package/lib/endfield-map/resources/map-21a61386af79cd12.json.gz +0 -0
- package/lib/endfield-map/resources/map-24b920b8dab49b44.json.br +0 -0
- package/lib/endfield-map/resources/map-24b920b8dab49b44.json.gz +0 -0
- package/lib/endfield-map/resources/map-26caf8d71247abb7.png +0 -0
- package/lib/endfield-map/resources/map-2729942c89545e73.json.br +0 -0
- package/lib/endfield-map/resources/map-2729942c89545e73.json.gz +0 -0
- package/lib/endfield-map/resources/map-29063a9802f67014.json.br +0 -0
- package/lib/endfield-map/resources/map-29063a9802f67014.json.gz +0 -0
- package/lib/endfield-map/resources/map-2f35d09a3ab60a63.json.br +0 -0
- package/lib/endfield-map/resources/map-2f35d09a3ab60a63.json.gz +0 -0
- package/lib/endfield-map/resources/map-2f98a873da81425e.json.br +0 -0
- package/lib/endfield-map/resources/map-2f98a873da81425e.json.gz +0 -0
- package/lib/endfield-map/resources/map-3ab978ae44190f80.json.br +0 -0
- package/lib/endfield-map/resources/map-3ab978ae44190f80.json.gz +0 -0
- package/lib/endfield-map/resources/map-3b62c80d14208902.json.br +0 -0
- package/lib/endfield-map/resources/map-3b62c80d14208902.json.gz +0 -0
- package/lib/endfield-map/resources/map-3d486e887ae121ac.png +0 -0
- package/lib/endfield-map/resources/map-418dc4865608fce5.png +0 -0
- package/lib/endfield-map/resources/map-4366b84607335f76.json.br +0 -0
- package/lib/endfield-map/resources/map-4366b84607335f76.json.gz +0 -0
- package/lib/endfield-map/resources/map-4371259d3ab7aac7.png +0 -0
- package/lib/endfield-map/resources/map-48dcc0fd55e13f7f.png +0 -0
- package/lib/endfield-map/resources/map-4aabda0e41197570.json.br +0 -0
- package/lib/endfield-map/resources/map-4aabda0e41197570.json.gz +0 -0
- package/lib/endfield-map/resources/map-505f5687b287043c.json.br +0 -0
- package/lib/endfield-map/resources/map-505f5687b287043c.json.gz +0 -0
- package/lib/endfield-map/resources/map-5177e9d1d46f47e2.png +0 -0
- package/lib/endfield-map/resources/map-51baeb59b7f8c28d.png +0 -0
- package/lib/endfield-map/resources/map-549561b46e621874.png +0 -0
- package/lib/endfield-map/resources/map-56c6a0bef799c8dc.json.br +0 -0
- package/lib/endfield-map/resources/map-56c6a0bef799c8dc.json.gz +0 -0
- package/lib/endfield-map/resources/map-5a6963909423b674.png +0 -0
- package/lib/endfield-map/resources/map-5a9c533c0552d7b7.png +0 -0
- package/lib/endfield-map/resources/map-5d20ae9ffa9a02b0.png +0 -0
- package/lib/endfield-map/resources/map-626c8847393f60bf.png +0 -0
- package/lib/endfield-map/resources/map-63d43cbec3bed3b8.png +0 -0
- package/lib/endfield-map/resources/map-661b6ec56e722574.png +0 -0
- package/lib/endfield-map/resources/map-6ddd384eca190828.png +0 -0
- package/lib/endfield-map/resources/map-6fc1aa9144f8156c.json.br +0 -0
- package/lib/endfield-map/resources/map-6fc1aa9144f8156c.json.gz +0 -0
- package/lib/endfield-map/resources/map-738ebaa002c33cfc.png +0 -0
- package/lib/endfield-map/resources/map-758c13be6085ec07.json.br +0 -0
- package/lib/endfield-map/resources/map-758c13be6085ec07.json.gz +0 -0
- package/lib/endfield-map/resources/map-78929f26d6844509.png +0 -0
- package/lib/endfield-map/resources/map-7f09c142950a0f74.png +0 -0
- package/lib/endfield-map/resources/map-8385e11f39e1a51c.json.br +0 -0
- package/lib/endfield-map/resources/map-8385e11f39e1a51c.json.gz +0 -0
- package/lib/endfield-map/resources/map-881343926a568f27.json.br +0 -0
- package/lib/endfield-map/resources/map-881343926a568f27.json.gz +0 -0
- package/lib/endfield-map/resources/map-8d1ade3f01d952b0.png +0 -0
- package/lib/endfield-map/resources/map-8fef3495bb7ed60f.png +0 -0
- package/lib/endfield-map/resources/map-907a7ecf8a0ce352.png +0 -0
- package/lib/endfield-map/resources/map-9113d5951c4db903.json.br +0 -0
- package/lib/endfield-map/resources/map-9113d5951c4db903.json.gz +0 -0
- package/lib/endfield-map/resources/map-94ad6634436224e3.json.br +0 -0
- package/lib/endfield-map/resources/map-94ad6634436224e3.json.gz +0 -0
- package/lib/endfield-map/resources/map-99722e61da5f3e53.png +0 -0
- package/lib/endfield-map/resources/map-9be9596cc059abe3.png +0 -0
- package/lib/endfield-map/resources/map-a4db9a4763e47fbf.png +0 -0
- package/lib/endfield-map/resources/map-a4e8ef07159997e7.json.br +0 -0
- package/lib/endfield-map/resources/map-a4e8ef07159997e7.json.gz +0 -0
- package/lib/endfield-map/resources/map-ab35d06875ee418f.png +0 -0
- package/lib/endfield-map/resources/map-ac51bb3e7623cd8b.png +0 -0
- package/lib/endfield-map/resources/map-ac830a66296bc83a.png +0 -0
- package/lib/endfield-map/resources/map-b3fd04209718e55e.json.br +0 -0
- package/lib/endfield-map/resources/map-b3fd04209718e55e.json.gz +0 -0
- package/lib/endfield-map/resources/map-b8164f1af7c5d895.png +0 -0
- package/lib/endfield-map/resources/map-b8cde93b965384d8.json.br +0 -0
- package/lib/endfield-map/resources/map-b8cde93b965384d8.json.gz +0 -0
- package/lib/endfield-map/resources/map-b8e48511502e0e8d.json.br +0 -0
- package/lib/endfield-map/resources/map-b8e48511502e0e8d.json.gz +0 -0
- package/lib/endfield-map/resources/map-bfc58277d5ba649d.png +0 -0
- package/lib/endfield-map/resources/map-c013f82048c6891a.png +0 -0
- package/lib/endfield-map/resources/map-c49c947841ae4ab5.json.br +0 -0
- package/lib/endfield-map/resources/map-c49c947841ae4ab5.json.gz +0 -0
- package/lib/endfield-map/resources/map-c4d1b1c6d6706c81.json.br +0 -0
- package/lib/endfield-map/resources/map-c4d1b1c6d6706c81.json.gz +0 -0
- package/lib/endfield-map/resources/map-c607f8b4c8ad8967.png +0 -0
- package/lib/endfield-map/resources/map-c6f20276b8781326.json.br +0 -0
- package/lib/endfield-map/resources/map-c6f20276b8781326.json.gz +0 -0
- package/lib/endfield-map/resources/map-d51adf5952af1e8e.png +0 -0
- package/lib/endfield-map/resources/map-d695eface22f8b74.json.br +0 -0
- package/lib/endfield-map/resources/map-d695eface22f8b74.json.gz +0 -0
- package/lib/endfield-map/resources/map-db11d394456a7465.png +0 -0
- package/lib/endfield-map/resources/map-dcc4d218581e3dc6.png +0 -0
- package/lib/endfield-map/resources/map-ddd43dbb8ab8a4e3.json.br +0 -0
- package/lib/endfield-map/resources/map-ddd43dbb8ab8a4e3.json.gz +0 -0
- package/lib/endfield-map/resources/map-defe5db7dabf748a.png +0 -0
- package/lib/endfield-map/resources/map-e1aeb833e22520aa.json.br +0 -0
- package/lib/endfield-map/resources/map-e1aeb833e22520aa.json.gz +0 -0
- package/lib/endfield-map/resources/map-e25ee9af6e493500.png +0 -0
- package/lib/endfield-map/resources/map-e3824082cd6f747d.json.br +0 -0
- package/lib/endfield-map/resources/map-e3824082cd6f747d.json.gz +0 -0
- package/lib/endfield-map/resources/map-e694a91e630b1bd6.png +0 -0
- package/lib/endfield-map/resources/map-eaa93af79d12a87d.png +0 -0
- package/lib/endfield-map/resources/map-ec9575ee65d7766f.json.br +0 -0
- package/lib/endfield-map/resources/map-ec9575ee65d7766f.json.gz +0 -0
- package/lib/endfield-map/resources/map-ede421a329dc78a4.png +0 -0
- package/lib/endfield-map/resources/map-eff3bce8f6888fc5.json.br +0 -0
- package/lib/endfield-map/resources/map-eff3bce8f6888fc5.json.gz +0 -0
- package/lib/endfield-map/resources/map-fb5f4a59a181452c.png +0 -0
- package/lib/endfield-map/resources/map-fdeaa17eeba90d8e.json.br +0 -0
- package/lib/endfield-map/resources/map-fdeaa17eeba90d8e.json.gz +0 -0
- package/lib/rhine/assets/archive-assembly.glb +0 -0
- package/lib/rhine/assets/archive-cassette.glb +0 -0
- package/lib/rhine/fonts/MiSans-Bold.woff2 +0 -0
- package/lib/rhine/fonts/MiSans-Demibold.woff2 +0 -0
- package/lib/rhine/fonts/MiSans-Light.woff2 +0 -0
- package/lib/rhine/fonts/MiSans-Regular.woff2 +0 -0
- package/lib/rhine/fonts/MiSans-license.pdf +0 -0
- package/lib/rhine/fonts/NOTICE.txt +6 -0
- package/lib/rhine/licenses/RhineLabUI-MIT.txt +21 -0
- package/lib/rhine/licenses/entities-BSD-2-Clause.txt +11 -0
- package/lib/rhine/licenses/linkify-it-MIT.txt +22 -0
- package/lib/rhine/licenses/markdown-it-MIT.txt +22 -0
- package/lib/rhine/licenses/mdurl-MIT.txt +45 -0
- package/lib/rhine/licenses/punycode.js-MIT.txt +20 -0
- package/lib/rhine/licenses/rolling-number-MIT.txt +21 -0
- package/lib/rhine/licenses/three-MIT.txt +21 -0
- package/lib/rhine/licenses/uc.micro-MIT.txt +20 -0
- package/lib/rhine/rhine.css +1 -0
- package/lib/rhine/rhine.js +5106 -0
- package/lib/skins/common.css +31 -0
- package/lib/skins/endfield-aic.css +141 -0
- package/lib/skins/prts-agent.css +136 -0
- package/lib/skins/rhine-lab.css +70 -0
- package/package.json +112 -0
- package/presets/definition.js +26 -0
- package/presets/prts/agent.cordis.yml +25 -0
- package/presets/prts/preset.yml +3 -0
- package/presets/register.js +86 -0
- package/resources/entity-routing.json +21 -0
- package/skills/prts-investigation/SKILL.md +121 -0
- package/skills/prts-retrieval/SKILL.md +35 -0
- package/skills/prts-retrieval/references/cloud-tools.md +13 -0
- package/skills/prts-retrieval/references/diagnostics.md +15 -0
- package/skills/prts-retrieval/references/local-tools.md +24 -0
- package/skills/prts-retrieval/references/module-arknights.md +23 -0
- package/skills/prts-retrieval/references/module-dual.md +15 -0
- package/skills/prts-retrieval/references/module-endfield.md +21 -0
- package/skills/prts-retrieval/references/retrieval-process.md +44 -0
- package/skills/prts-retrieval/references/retrieval-recipes-dual.md +34 -0
- package/skills/prts-retrieval/references/retrieval-recipes-endfield.md +37 -0
- package/skills/prts-retrieval/references/retrieval-recipes.md +39 -0
- package/skills/prts-retrieval/references/tools-runtime.md +48 -0
- package/skills/prts-retrieval/references/wiki-schema.md +38 -0
- package/src/archive-presentation.js +287 -0
- package/src/cloud-projection.js +124 -0
- package/src/cloud.js +498 -0
- package/src/entity-recognizer.js +311 -0
- package/src/entity-routing.js +82 -0
- package/src/evidence-state.js +332 -0
- package/src/i18n.js +365 -0
- package/src/index.js +1261 -0
- package/src/installer.js +1362 -0
- package/src/investigation-review.js +87 -0
- package/src/investigation-store.js +579 -0
- package/src/investigation-tools.js +113 -0
- package/src/localization-format.js +23 -0
- package/src/manual-search.js +103 -0
- package/src/read.js +1097 -0
- package/src/release-compatibility.js +73 -0
- package/src/release-watch.js +40 -0
- package/src/search-projection.js +139 -0
- package/src/search.js +1522 -0
- package/src/skill.js +98 -0
- package/src/source-map.js +348 -0
- package/src/state.js +314 -0
- package/src/store.js +1569 -0
- package/src/timeline.js +386 -0
- package/src/ui.js +1353 -0
- package/src/wiki.js +111 -0
- package/tsconfig.rhine.json +15 -0
- package/ui/rhine/archive-navigation-limit.ts +31 -0
- package/ui/rhine/archive-rack.ts +83 -0
- package/ui/rhine/assets/evidence-observatory.webp +0 -0
- package/ui/rhine/board-plane-transform.ts +24 -0
- package/ui/rhine/catalogue.ts +72 -0
- package/ui/rhine/deferred-preparation.ts +146 -0
- package/ui/rhine/evidence-board-controls.css +22 -0
- package/ui/rhine/evidence-board-history.ts +73 -0
- package/ui/rhine/evidence-board-model.ts +189 -0
- package/ui/rhine/evidence-board-panel.ts +762 -0
- package/ui/rhine/evidence-board-scene.ts +548 -0
- package/ui/rhine/evidence-board.css +191 -0
- package/ui/rhine/evidence-card-art.ts +470 -0
- package/ui/rhine/evidence-card-style.ts +17 -0
- package/ui/rhine/evidence-inbox-scene.ts +43 -0
- package/ui/rhine/evidence-inbox.css +29 -0
- package/ui/rhine/evidence-inbox.ts +104 -0
- package/ui/rhine/index.ts +12 -0
- package/ui/rhine/interaction.css +20 -0
- package/ui/rhine/investigation-board-plane.ts +5 -0
- package/ui/rhine/investigation-board.css +34 -0
- package/ui/rhine/investigation-board.ts +348 -0
- package/ui/rhine/investigation-edits.ts +46 -0
- package/ui/rhine/investigation-layout.ts +29 -0
- package/ui/rhine/label-prefetch.ts +34 -0
- package/ui/rhine/looping-title.ts +40 -0
- package/ui/rhine/manual-search.css +41 -0
- package/ui/rhine/manual-search.ts +89 -0
- package/ui/rhine/original/appearance.ts +180 -0
- package/ui/rhine/original/archive-label.ts +84 -0
- package/ui/rhine/original/archive-lighting.ts +39 -0
- package/ui/rhine/original/archive-loop.ts +82 -0
- package/ui/rhine/original/archive-refill.ts +64 -0
- package/ui/rhine/original/array-detail-data.ts +2 -0
- package/ui/rhine/original/array-detail.ts +41 -0
- package/ui/rhine/original/array-interior.ts +411 -0
- package/ui/rhine/original/array-occlusion.ts +166 -0
- package/ui/rhine/original/array-shell.ts +38 -0
- package/ui/rhine/original/array-visibility.ts +309 -0
- package/ui/rhine/original/asset-url.ts +4 -0
- package/ui/rhine/original/brand.ts +12 -0
- package/ui/rhine/original/cassette-lod.ts +81 -0
- package/ui/rhine/original/data.ts +21 -0
- package/ui/rhine/original/decryption.ts +185 -0
- package/ui/rhine/original/detail-signature.ts +18 -0
- package/ui/rhine/original/glass-reveal.ts +45 -0
- package/ui/rhine/original/internal-optics.ts +70 -0
- package/ui/rhine/original/label-renderer.ts +119 -0
- package/ui/rhine/original/label-textures.ts +117 -0
- package/ui/rhine/original/model-viewer.ts +658 -0
- package/ui/rhine/original/motion.ts +116 -0
- package/ui/rhine/original/opaque-backfaces.ts +64 -0
- package/ui/rhine/original/program-preparation.ts +111 -0
- package/ui/rhine/original/quality-renderer.ts +97 -0
- package/ui/rhine/original/quality-settings.ts +111 -0
- package/ui/rhine/original/render-quality.ts +163 -0
- package/ui/rhine/original/scene.ts +2051 -0
- package/ui/rhine/original/shelf-interior.ts +142 -0
- package/ui/rhine/original/ui-transitions.ts +111 -0
- package/ui/rhine/original/viewer-camera.ts +92 -0
- package/ui/rhine/original/viewport-layout.ts +41 -0
- package/ui/rhine/original-ui.css +2334 -0
- package/ui/rhine/readability.css +46 -0
- package/ui/rhine/reading-object.ts +96 -0
- package/ui/rhine/reading-origin.ts +28 -0
- package/ui/rhine/reading-room.css +55 -0
- package/ui/rhine/report-markdown.ts +98 -0
- package/ui/rhine/scene-edge-navigation.ts +84 -0
- package/ui/rhine/scene.ts +1726 -0
- package/ui/rhine/shelf-layout.ts +38 -0
- package/ui/rhine/snapshot-update-gate.ts +22 -0
- package/ui/rhine/source-reading.ts +15 -0
- package/ui/rhine/source-selection.css +164 -0
- package/ui/rhine/temporary-performance-observers.ts +163 -0
- package/ui/rhine/temporary-performance-panel.ts +124 -0
- package/ui/rhine/temporary-performance.css +19 -0
- package/ui/rhine/temporary-performance.ts +552 -0
- package/ui/rhine/temporary-render-diagnostics.ts +105 -0
- package/ui/rhine/tool-activity.ts +69 -0
- package/ui/rhine/types.ts +219 -0
- package/ui/rhine/visible-scene-hit.ts +13 -0
- package/ui/rhine/workbench.css +818 -0
- package/ui/rhine/workbench.ts +2245 -0
- package/ui/rhine/workspace-travel.ts +32 -0
- package/vite.rhine.config.ts +19 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/** DSH 会话内的轻量证据状态:只驻留内存,按 exec.agent 隔离。 */
|
|
2
|
+
import { wikiSectionRanges } from './wiki.js'
|
|
3
|
+
import { readableRenderedLine } from './read.js'
|
|
4
|
+
import { randomBytes } from 'node:crypto'
|
|
5
|
+
|
|
6
|
+
const MAX_CANDIDATES = 240
|
|
7
|
+
const MAX_MAPPINGS = 240
|
|
8
|
+
const MAX_DOCUMENTS = 96
|
|
9
|
+
const MAX_LINES = 4000
|
|
10
|
+
|
|
11
|
+
function newState() {
|
|
12
|
+
const suffix = randomBytes(8).toString('hex')
|
|
13
|
+
return {
|
|
14
|
+
intentId: `dsh-${suffix}`,
|
|
15
|
+
cloudIntentId: `prts-${suffix}`,
|
|
16
|
+
lastCloudRequestId: '',
|
|
17
|
+
searchCandidates: [],
|
|
18
|
+
cloudSourceMappings: [],
|
|
19
|
+
readCoverage: [],
|
|
20
|
+
documents: new Map(),
|
|
21
|
+
completedSearchCalls: new Map(),
|
|
22
|
+
dataVersion: null,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function collectVisibleText(value, output = []) {
|
|
27
|
+
if (!value || typeof value !== 'object') return output
|
|
28
|
+
if (value.type === 'text' && typeof value.text === 'string') output.push(value.text)
|
|
29
|
+
if (Array.isArray(value)) for (const item of value) collectVisibleText(item, output)
|
|
30
|
+
else for (const child of Object.values(value)) collectVisibleText(child, output)
|
|
31
|
+
return output
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 当前模型可见 surface 中的工具结果正文;压缩或 spill 后不再含原文的节点不能复用。 */
|
|
35
|
+
export function visibleToolResults(agent) {
|
|
36
|
+
const session = agent?.session
|
|
37
|
+
const nodes = session?.surface?.nodes
|
|
38
|
+
if (typeof session?.eventAt !== 'function' || !nodes
|
|
39
|
+
|| typeof nodes[Symbol.iterator] !== 'function') return new Map()
|
|
40
|
+
const visible = new Map()
|
|
41
|
+
for (const seq of nodes) {
|
|
42
|
+
const event = session.eventAt(seq)
|
|
43
|
+
if (event?.type !== 'tool/result') continue
|
|
44
|
+
const callId = event.data?.message?.source?.callId
|
|
45
|
+
if (typeof callId === 'string' && callId) {
|
|
46
|
+
visible.set(callId, collectVisibleText(event.data.message?.content).join('\n'))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return visible
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createEvidenceStateRegistry() {
|
|
53
|
+
const contexts = new WeakMap()
|
|
54
|
+
return {
|
|
55
|
+
forExecution(exec, dataVersion = null) {
|
|
56
|
+
const agent = exec?.agent
|
|
57
|
+
const owner = agent && (typeof agent === 'object' || typeof agent === 'function')
|
|
58
|
+
? agent
|
|
59
|
+
: exec && (typeof exec === 'object' || typeof exec === 'function') ? exec : null
|
|
60
|
+
// 没有可识别的宿主会话对象时宁可放弃跨调用复用,也不能让不同请求共享证据。
|
|
61
|
+
if (!owner) {
|
|
62
|
+
const state = newState()
|
|
63
|
+
state.dataVersion = dataVersion
|
|
64
|
+
return state
|
|
65
|
+
}
|
|
66
|
+
let state = contexts.get(owner)
|
|
67
|
+
if (!state) {
|
|
68
|
+
state = newState()
|
|
69
|
+
contexts.set(owner, state)
|
|
70
|
+
}
|
|
71
|
+
if (dataVersion && state.dataVersion && state.dataVersion !== dataVersion) {
|
|
72
|
+
state = newState()
|
|
73
|
+
contexts.set(owner, state)
|
|
74
|
+
}
|
|
75
|
+
if (dataVersion) state.dataVersion = dataVersion
|
|
76
|
+
return state
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function rememberSearchCandidates(state, response) {
|
|
82
|
+
if (!Array.isArray(response?.documents)) return
|
|
83
|
+
for (const document of response.documents) for (const match of document.matches || []) {
|
|
84
|
+
const candidate = {
|
|
85
|
+
title: String(document.title || ''), line: Number(match.line_start || 0),
|
|
86
|
+
}
|
|
87
|
+
const key = `${candidate.title}:${candidate.line}`
|
|
88
|
+
const index = state.searchCandidates.findIndex((item) => item._key === key)
|
|
89
|
+
if (index >= 0) state.searchCandidates[index] = { ...candidate, _key: key }
|
|
90
|
+
else state.searchCandidates.push({ ...candidate, _key: key })
|
|
91
|
+
}
|
|
92
|
+
if (state.searchCandidates.length > MAX_CANDIDATES) {
|
|
93
|
+
state.searchCandidates.splice(0, state.searchCandidates.length - MAX_CANDIDATES)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function rememberCloudMappings(state, response) {
|
|
98
|
+
const requestId = String(response?.data?.request?.request_id || response?.data?.request_id || '')
|
|
99
|
+
if (requestId) state.lastCloudRequestId = requestId
|
|
100
|
+
for (const mapping of response?.local_source_mappings || []) {
|
|
101
|
+
const key = `${mapping.evidence_id || mapping.candidate_id || ''}:${mapping.document_id}`
|
|
102
|
+
const compact = { ...mapping, _key: key }
|
|
103
|
+
const index = state.cloudSourceMappings.findIndex((item) => item._key === key)
|
|
104
|
+
if (index >= 0) state.cloudSourceMappings[index] = compact
|
|
105
|
+
else state.cloudSourceMappings.push(compact)
|
|
106
|
+
}
|
|
107
|
+
if (state.cloudSourceMappings.length > MAX_MAPPINGS) {
|
|
108
|
+
state.cloudSourceMappings.splice(0, state.cloudSourceMappings.length - MAX_MAPPINGS)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function sourceRefParts(sourceRef) {
|
|
113
|
+
const match = String(sourceRef || '').match(/^(.*):L([1-9][0-9]*)$/u)
|
|
114
|
+
return match ? { prefix: match[1], line: Number(match[2]) } : null
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 将已转换的 corpus_read contract 解析为稳定篇章与请求行区间。 */
|
|
118
|
+
export async function resolveReadWindow(store, contract) {
|
|
119
|
+
await store.ready()
|
|
120
|
+
const locator = contract?.locator || {}
|
|
121
|
+
const parsed = sourceRefParts(locator.source_ref)
|
|
122
|
+
let documentId = String(locator.document_id || '')
|
|
123
|
+
if (!documentId && locator.document_uid) {
|
|
124
|
+
documentId = String(store.getDocumentIdByUid(locator.document_uid) || '')
|
|
125
|
+
}
|
|
126
|
+
if (!documentId && parsed) documentId = String(store.getDocumentIdByPrefix(parsed.prefix) || '')
|
|
127
|
+
if (!documentId && locator.display_title) {
|
|
128
|
+
// 工具面(corpus_search / corpus_read)的 title 是自然语言完整标题
|
|
129
|
+
// (naturalTitleIndex);原始 display_title 仅作回退。此前只查 titleIndex,
|
|
130
|
+
// 自然标题一律解析失败,读取覆盖去重对最常见的标题路径完全失效。
|
|
131
|
+
// 同名歧义时不跟踪(读取本身会给出消歧错误或稳定选择)。
|
|
132
|
+
const title = String(locator.display_title)
|
|
133
|
+
const ids = store.naturalTitleIndex.get(title) || store.titleIndex.get(title) || []
|
|
134
|
+
if (ids.length === 1) documentId = ids[0]
|
|
135
|
+
}
|
|
136
|
+
if (!documentId) return null
|
|
137
|
+
const lineCount = Number(store.documents.get(documentId)?.document?.line_count || Infinity)
|
|
138
|
+
const boundedEnd = (line) => Math.min(Number(line), lineCount)
|
|
139
|
+
const selection = contract.selection || {}
|
|
140
|
+
if (selection.mode === 'around') {
|
|
141
|
+
const center = parsed?.line || Number(selection.center_line || 0)
|
|
142
|
+
if (!center || center > lineCount) return null
|
|
143
|
+
// 未显式给 before/after 时按契约默认 ±3 计窗口(normalizeReadRequest 的
|
|
144
|
+
// 默认值);此前按 0 计,重复请求的回放只剩中心 1 行,丢失上下文。
|
|
145
|
+
const before = Number(selection.before_lines ?? 3)
|
|
146
|
+
const after = Number(selection.after_lines ?? 3)
|
|
147
|
+
return { documentId, lineStart: Math.max(1, center - before),
|
|
148
|
+
lineEnd: boundedEnd(center + after) }
|
|
149
|
+
}
|
|
150
|
+
if (selection.mode === 'range') return { documentId,
|
|
151
|
+
lineStart: Number(selection.start_line), lineEnd: boundedEnd(selection.end_line) }
|
|
152
|
+
if (selection.mode === 'section') {
|
|
153
|
+
const found = await store.getDocument(documentId)
|
|
154
|
+
if (found?.record.document.document_type !== 'knowledge'
|
|
155
|
+
|| found.record.document.document_kind !== 'wiki') return null
|
|
156
|
+
const ranges = found ? wikiSectionRanges(found.record, [selection.section]) : []
|
|
157
|
+
if (ranges.length !== 1) return null
|
|
158
|
+
return { documentId, lineStart: ranges[0].start_line,
|
|
159
|
+
lineEnd: boundedEnd(ranges[0].end_line) }
|
|
160
|
+
}
|
|
161
|
+
if (selection.mode === 'document' && !selection.cursor) {
|
|
162
|
+
const start = Number(selection.start_line || 1)
|
|
163
|
+
return { documentId, lineStart: start,
|
|
164
|
+
lineEnd: boundedEnd(start + Number(contract.limits?.max_lines || 100) - 1) }
|
|
165
|
+
}
|
|
166
|
+
return null
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** 覆盖判定用的行标记必须与 renderRead 的模型可见格式逐字一致(含剥离说话人前缀与行尾空白)。 */
|
|
170
|
+
function renderedLineMarker(line) {
|
|
171
|
+
return readableRenderedLine(line)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function lineVisible(cached, lineNumber, visibleToolResults) {
|
|
175
|
+
if (!cached?.lines.has(lineNumber) || !visibleToolResults?.size) return false
|
|
176
|
+
const sources = cached.lineSources?.get(lineNumber)
|
|
177
|
+
const marker = renderedLineMarker(cached.lines.get(lineNumber))
|
|
178
|
+
return Boolean(sources && [...sources].some((callId) =>
|
|
179
|
+
String(visibleToolResults.get(callId) || '').includes(marker)))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function coveredRead(state, requested, visibleToolResults) {
|
|
183
|
+
if (!requested) return null
|
|
184
|
+
const cached = state.documents.get(requested.documentId)
|
|
185
|
+
for (let line = requested.lineStart; line <= requested.lineEnd; line += 1) {
|
|
186
|
+
if (!lineVisible(cached, line, visibleToolResults)) return null
|
|
187
|
+
}
|
|
188
|
+
return requested
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** 计算请求区间中已经核验与仍需从资料包读取的连续片段。 */
|
|
192
|
+
export function planReadCoverage(state, requested, visibleToolResults) {
|
|
193
|
+
if (!requested || requested.lineEnd < requested.lineStart) return null
|
|
194
|
+
const reusedRanges = []
|
|
195
|
+
const cached = state.documents.get(requested.documentId)
|
|
196
|
+
for (let line = requested.lineStart; line <= requested.lineEnd; line += 1) {
|
|
197
|
+
if (!lineVisible(cached, line, visibleToolResults)) continue
|
|
198
|
+
const previous = reusedRanges.at(-1)
|
|
199
|
+
if (previous?.lineEnd === line - 1) previous.lineEnd = line
|
|
200
|
+
else reusedRanges.push({ lineStart: line, lineEnd: line })
|
|
201
|
+
}
|
|
202
|
+
const unreadRanges = []
|
|
203
|
+
let cursor = requested.lineStart
|
|
204
|
+
for (const range of reusedRanges) {
|
|
205
|
+
if (cursor < range.lineStart) unreadRanges.push({ lineStart: cursor, lineEnd: range.lineStart - 1 })
|
|
206
|
+
cursor = Math.max(cursor, range.lineEnd + 1)
|
|
207
|
+
}
|
|
208
|
+
if (cursor <= requested.lineEnd) unreadRanges.push({ lineStart: cursor, lineEnd: requested.lineEnd })
|
|
209
|
+
return { reusedRanges, unreadRanges }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function mergeCoverage(state, entry) {
|
|
213
|
+
const overlaps = state.readCoverage.filter((item) => item.documentId === entry.documentId
|
|
214
|
+
&& item.lineEnd + 1 >= entry.lineStart && entry.lineEnd + 1 >= item.lineStart)
|
|
215
|
+
if (overlaps.length) {
|
|
216
|
+
entry.lineStart = Math.min(entry.lineStart, ...overlaps.map((item) => item.lineStart))
|
|
217
|
+
entry.lineEnd = Math.max(entry.lineEnd, ...overlaps.map((item) => item.lineEnd))
|
|
218
|
+
state.readCoverage = state.readCoverage.filter((item) => !overlaps.includes(item))
|
|
219
|
+
}
|
|
220
|
+
state.readCoverage.push(entry)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function rememberRead(state, response, { callId = '', store = null } = {}) {
|
|
224
|
+
if (response?.status !== 'ok' || response.duplicate_read || response.content?.format !== 'lines') return
|
|
225
|
+
const groups = new Map()
|
|
226
|
+
for (const line of response.content.lines || []) {
|
|
227
|
+
const documentId = String(line.document_id || response.document?.document_id || '')
|
|
228
|
+
const lineNumber = Number(line.line_number)
|
|
229
|
+
if (!documentId || !Number.isInteger(lineNumber) || lineNumber < 1) continue
|
|
230
|
+
const group = groups.get(documentId) || { lines: [], lineStart: lineNumber, lineEnd: lineNumber }
|
|
231
|
+
group.lines.push(line)
|
|
232
|
+
group.lineStart = Math.min(group.lineStart, lineNumber)
|
|
233
|
+
group.lineEnd = Math.max(group.lineEnd, lineNumber)
|
|
234
|
+
groups.set(documentId, group)
|
|
235
|
+
}
|
|
236
|
+
for (const [documentId, group] of groups) {
|
|
237
|
+
let cached = state.documents.get(documentId)
|
|
238
|
+
if (!cached) {
|
|
239
|
+
const document = documentId === response.document?.document_id
|
|
240
|
+
? response.document : store?.documents?.get(documentId)?.document || { document_id: documentId }
|
|
241
|
+
cached = { document: structuredClone(document), lines: new Map(), lineSources: new Map(),
|
|
242
|
+
dataVersion: response.data_version || null, integrity: structuredClone(response.integrity || {}) }
|
|
243
|
+
state.documents.set(documentId, cached)
|
|
244
|
+
}
|
|
245
|
+
for (const line of group.lines) {
|
|
246
|
+
const lineNumber = Number(line.line_number)
|
|
247
|
+
cached.lines.set(lineNumber, structuredClone(line))
|
|
248
|
+
if (callId) {
|
|
249
|
+
let sources = cached.lineSources.get(lineNumber)
|
|
250
|
+
if (!sources) {
|
|
251
|
+
sources = new Set()
|
|
252
|
+
cached.lineSources.set(lineNumber, sources)
|
|
253
|
+
}
|
|
254
|
+
sources.add(callId)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
mergeCoverage(state, { documentId, lineStart: group.lineStart, lineEnd: group.lineEnd })
|
|
258
|
+
}
|
|
259
|
+
while (state.documents.size > MAX_DOCUMENTS
|
|
260
|
+
|| [...state.documents.values()].reduce((sum, item) => sum + item.lines.size, 0) > MAX_LINES) {
|
|
261
|
+
const oldest = state.documents.keys().next().value
|
|
262
|
+
state.documents.delete(oldest)
|
|
263
|
+
state.readCoverage = state.readCoverage.filter((item) => item.documentId !== oldest)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function replayCoveredRead(state, requested, contract) {
|
|
268
|
+
const cached = state.documents.get(requested.documentId)
|
|
269
|
+
if (!cached) return null
|
|
270
|
+
const lines = [...cached.lines.values()].filter((line) => line.line_number >= requested.lineStart
|
|
271
|
+
&& line.line_number <= requested.lineEnd).sort((a, b) => a.line_number - b.line_number)
|
|
272
|
+
if (!lines.length || lines[0].line_number > requested.lineStart
|
|
273
|
+
|| lines.at(-1).line_number < requested.lineEnd) return null
|
|
274
|
+
const characterCount = lines.reduce((sum, line) => sum + String(line.text || '').length, 0)
|
|
275
|
+
// 缓存命中仍服从本次预算;更小的预算交回标准读取器计算实际页边界。
|
|
276
|
+
if (lines.length > (contract.limits?.max_lines ?? 100)
|
|
277
|
+
|| characterCount > (contract.limits?.max_chars ?? 12000)) return null
|
|
278
|
+
const documentMode = contract.selection.mode === 'document'
|
|
279
|
+
const total = documentMode ? cached.document.line_count : requested.lineEnd - requested.lineStart + 1
|
|
280
|
+
const hasMore = documentMode && requested.lineEnd < total
|
|
281
|
+
return {
|
|
282
|
+
contract_version: 'prts-corpus-tools-v1', status: 'ok', duplicate_read: true,
|
|
283
|
+
data_version: cached.dataVersion, normalized_request: contract,
|
|
284
|
+
document: structuredClone(cached.document),
|
|
285
|
+
selection: { mode: contract.selection.mode, line_start: requested.lineStart,
|
|
286
|
+
line_end: requested.lineEnd, line_count: lines.length, character_count: characterCount,
|
|
287
|
+
...(contract.selection.mode === 'section' ? { wiki_section: contract.selection.section } : {}),
|
|
288
|
+
truncated: hasMore },
|
|
289
|
+
content: { format: 'lines', lines },
|
|
290
|
+
page: { limit: contract.limits?.max_lines ?? 100, returned: lines.length,
|
|
291
|
+
has_more: hasMore, next_cursor: null, total, total_relation: 'eq' },
|
|
292
|
+
integrity: structuredClone(cached.integrity),
|
|
293
|
+
stats: { elapsed_ms: 0, scanned_documents: 0, scanned_lines: 0, returned_chars: 0,
|
|
294
|
+
estimated_input_tokens: 0, truncated: hasMore, reused_local_evidence: true },
|
|
295
|
+
warnings: [],
|
|
296
|
+
guidance: '该范围已在当前模型可见的上文工具结果中完整出现;这里复用已核验的本地原文,不要再次读取相同范围。',
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 标准读取器先计算本次预算和续页,再从该页移除模型上文中已经可见的行。
|
|
302
|
+
* 去重只改变交付正文,不改变已确定的页边界;coverage 明示旧行的来源。
|
|
303
|
+
*/
|
|
304
|
+
export function reuseReadCoverage(response, plan) {
|
|
305
|
+
if (!plan?.reusedRanges.length || !plan.unreadRanges.length
|
|
306
|
+
|| response?.status !== 'ok' || response.content?.format !== 'lines') return response
|
|
307
|
+
const lines = response.content.lines.filter((line) => plan.unreadRanges.some((range) =>
|
|
308
|
+
line.line_number >= range.lineStart && line.line_number <= range.lineEnd))
|
|
309
|
+
const characterCount = lines.reduce((sum, line) => sum + String(line.text || '').length, 0)
|
|
310
|
+
const publicRanges = (ranges) => ranges.map((range) => ({
|
|
311
|
+
line_start: range.lineStart, line_end: range.lineEnd,
|
|
312
|
+
}))
|
|
313
|
+
return {
|
|
314
|
+
...response, partial_read_reused: true,
|
|
315
|
+
selection: { ...response.selection, line_count: lines.length, character_count: characterCount },
|
|
316
|
+
content: { format: 'lines', lines },
|
|
317
|
+
coverage: {
|
|
318
|
+
requested_range: { line_start: response.selection.line_start, line_end: response.selection.line_end },
|
|
319
|
+
reused_ranges: publicRanges(plan.reusedRanges),
|
|
320
|
+
fetched_ranges: publicRanges(plan.unreadRanges),
|
|
321
|
+
complete: true,
|
|
322
|
+
},
|
|
323
|
+
page: { ...response.page, returned: lines.length },
|
|
324
|
+
stats: { ...response.stats, returned_chars: characterCount,
|
|
325
|
+
estimated_input_tokens: Math.ceil(characterCount / 2.5),
|
|
326
|
+
reused_local_evidence: true,
|
|
327
|
+
},
|
|
328
|
+
warnings: [...(response.warnings || []), { code: 'PARTIAL_READ_REUSED',
|
|
329
|
+
message: `本页已有 ${plan.reusedRanges.length} 段经过核验,本次只返回新读取行。` }],
|
|
330
|
+
guidance: '本页与当前模型可见的上文原文部分重叠;这里只返回新读取行,coverage.reused_ranges 对应旧行就在上方可见工具结果中。',
|
|
331
|
+
}
|
|
332
|
+
}
|