dsh-openwrite 0.2.8

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 (275) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +107 -0
  3. package/THIRD_PARTY_NOTICES.md +33 -0
  4. package/assets/logo-dark.svg +31 -0
  5. package/assets/logo-light.svg +31 -0
  6. package/cordis.patch.yml +11 -0
  7. package/docs/BENCHMARK_TASKS.md +54 -0
  8. package/docs/COST_MEASUREMENT.md +39 -0
  9. package/docs/DEVELOPMENT.md +65 -0
  10. package/docs/DSH_STORE.md +58 -0
  11. package/docs/FRAMEWORK_ADVANTAGES.md +288 -0
  12. package/docs/IMPROVEMENT_PLAN.md +252 -0
  13. package/docs/INSTALL.md +99 -0
  14. package/docs/LEARNING.md +68 -0
  15. package/docs/OPEN_SOURCE_NOVEL_AUDIT.md +191 -0
  16. package/docs/PLUGIN_MAINTENANCE.md +54 -0
  17. package/docs/PLUGIN_MAINTENANCE_RC7.md +157 -0
  18. package/docs/QA_20260908.md +103 -0
  19. package/docs/QA_20260909_BENCHMARK.md +24 -0
  20. package/docs/RELEASE_ACCEPTANCE.md +116 -0
  21. package/docs/REVIEW_DAG_FRAMEWORK.md +82 -0
  22. package/docs/TARGETED_NOVEL_PROJECT_REVIEW.md +167 -0
  23. package/docs/WORKFLOWS.md +74 -0
  24. package/docs/WORKSPACE_CONTEXT_CONTRACT.md +125 -0
  25. package/docs/WORKSPACE_INTEGRATION_GOAL.md +278 -0
  26. package/docs/research/autosave-race-repro.mjs +133 -0
  27. package/docs/research/framework-stats-2026-09-05.json +116 -0
  28. package/dog-tools.mjs +11 -0
  29. package/package.json +132 -0
  30. package/packages/openwrite-bridge/LICENSE +201 -0
  31. package/packages/openwrite-bridge/cordis.patch.yml +10 -0
  32. package/packages/openwrite-bridge/lib/client.d.ts +95 -0
  33. package/packages/openwrite-bridge/lib/client.js +193 -0
  34. package/packages/openwrite-bridge/lib/contracts-generated.d.ts +367 -0
  35. package/packages/openwrite-bridge/lib/contracts-generated.js +1764 -0
  36. package/packages/openwrite-bridge/lib/dog-delivery.d.ts +12 -0
  37. package/packages/openwrite-bridge/lib/dog-delivery.js +362 -0
  38. package/packages/openwrite-bridge/lib/dog-review.d.ts +50 -0
  39. package/packages/openwrite-bridge/lib/dog-review.js +470 -0
  40. package/packages/openwrite-bridge/lib/domain.d.ts +70 -0
  41. package/packages/openwrite-bridge/lib/domain.js +496 -0
  42. package/packages/openwrite-bridge/lib/index.d.ts +17 -0
  43. package/packages/openwrite-bridge/lib/index.js +89 -0
  44. package/packages/openwrite-bridge/lib/managed-runtime.d.ts +95 -0
  45. package/packages/openwrite-bridge/lib/managed-runtime.js +510 -0
  46. package/packages/openwrite-bridge/lib/preset-tools.d.ts +13 -0
  47. package/packages/openwrite-bridge/lib/preset-tools.js +18 -0
  48. package/packages/openwrite-bridge/lib/tool-policy.d.ts +6 -0
  49. package/packages/openwrite-bridge/lib/tool-policy.js +56 -0
  50. package/packages/openwrite-bridge/lib/tools.d.ts +24 -0
  51. package/packages/openwrite-bridge/lib/tools.js +2404 -0
  52. package/packages/openwrite-bridge/package.json +67 -0
  53. package/packages/studio-panel/LICENSE +21 -0
  54. package/packages/studio-panel/README.md +51 -0
  55. package/packages/studio-panel/cordis.patch.yml +5 -0
  56. package/packages/studio-panel/lib/client.js +158087 -0
  57. package/packages/studio-panel/lib/client.js.map +1 -0
  58. package/packages/studio-panel/lib/index.js +50 -0
  59. package/packages/studio-panel/lib/types/client/AssetEditor.d.ts +122 -0
  60. package/packages/studio-panel/lib/types/client/AssetsView.d.ts +43 -0
  61. package/packages/studio-panel/lib/types/client/BenchmarkView.d.ts +9 -0
  62. package/packages/studio-panel/lib/types/client/ContinuousReader.d.ts +13 -0
  63. package/packages/studio-panel/lib/types/client/CreationView.d.ts +6 -0
  64. package/packages/studio-panel/lib/types/client/DagCanvas.d.ts +11 -0
  65. package/packages/studio-panel/lib/types/client/DiscardDraftDialog.d.ts +8 -0
  66. package/packages/studio-panel/lib/types/client/DomainToolCard.d.ts +9 -0
  67. package/packages/studio-panel/lib/types/client/GraphView.d.ts +46 -0
  68. package/packages/studio-panel/lib/types/client/HeaderChrome.d.ts +18 -0
  69. package/packages/studio-panel/lib/types/client/LaunchOpenWrite.d.ts +13 -0
  70. package/packages/studio-panel/lib/types/client/LibraryView.d.ts +6 -0
  71. package/packages/studio-panel/lib/types/client/MarkdownText.d.ts +3 -0
  72. package/packages/studio-panel/lib/types/client/ModelView.d.ts +9 -0
  73. package/packages/studio-panel/lib/types/client/OperationsView.d.ts +6 -0
  74. package/packages/studio-panel/lib/types/client/OutlinePlans.d.ts +37 -0
  75. package/packages/studio-panel/lib/types/client/OutlineView.d.ts +29 -0
  76. package/packages/studio-panel/lib/types/client/ResearchView.d.ts +7 -0
  77. package/packages/studio-panel/lib/types/client/ReviewCard.d.ts +28 -0
  78. package/packages/studio-panel/lib/types/client/SceneWorkbench.d.ts +55 -0
  79. package/packages/studio-panel/lib/types/client/SearchView.d.ts +33 -0
  80. package/packages/studio-panel/lib/types/client/TasksView.d.ts +11 -0
  81. package/packages/studio-panel/lib/types/client/TransferWorkspaces.d.ts +19 -0
  82. package/packages/studio-panel/lib/types/client/TurnMutationSummary.d.ts +62 -0
  83. package/packages/studio-panel/lib/types/client/VditorBody.d.ts +55 -0
  84. package/packages/studio-panel/lib/types/client/WorkbenchStore.d.ts +121 -0
  85. package/packages/studio-panel/lib/types/client/api.d.ts +72 -0
  86. package/packages/studio-panel/lib/types/client/asset-drafts.d.ts +20 -0
  87. package/packages/studio-panel/lib/types/client/benchmark-options.d.ts +43 -0
  88. package/packages/studio-panel/lib/types/client/draft-store.d.ts +26 -0
  89. package/packages/studio-panel/lib/types/client/dto.d.ts +517 -0
  90. package/packages/studio-panel/lib/types/client/index.d.ts +4 -0
  91. package/packages/studio-panel/lib/types/client/locales.d.ts +15 -0
  92. package/packages/studio-panel/lib/types/client/manuscript-annotations.d.ts +59 -0
  93. package/packages/studio-panel/lib/types/client/manuscript-markers.d.ts +61 -0
  94. package/packages/studio-panel/lib/types/client/manuscript-mentions.d.ts +30 -0
  95. package/packages/studio-panel/lib/types/client/manuscript-overlay-dom.d.ts +7 -0
  96. package/packages/studio-panel/lib/types/client/manuscript-overlay.d.ts +42 -0
  97. package/packages/studio-panel/lib/types/client/manuscript-selection.d.ts +59 -0
  98. package/packages/studio-panel/lib/types/client/model-view-memory.d.ts +17 -0
  99. package/packages/studio-panel/lib/types/client/storage.d.ts +8 -0
  100. package/packages/studio-panel/lib/types/client/workbench-epochs.d.ts +16 -0
  101. package/packages/studio-panel/lib/types/client/workspace-context.d.ts +52 -0
  102. package/packages/studio-panel/lib/types/client/writing-scope.d.ts +7 -0
  103. package/packages/studio-panel/lib/types/index.d.ts +5 -0
  104. package/packages/studio-panel/package.json +105 -0
  105. package/packages/studio-panel/vendor/vditor/LICENSE +21 -0
  106. package/packages/studio-panel/vendor/vditor/dist/css/content-theme/dark.css +123 -0
  107. package/packages/studio-panel/vendor/vditor/dist/css/content-theme/light.css +101 -0
  108. package/packages/studio-panel/vendor/vditor/dist/index.css +1823 -0
  109. package/packages/studio-panel/vendor/vditor/dist/index.min.js +1 -0
  110. package/packages/studio-panel/vendor/vditor/dist/js/i18n/zh_CN.js +86 -0
  111. package/packages/studio-panel/vendor/vditor/dist/js/icons/ant.js +174 -0
  112. package/packages/studio-panel/vendor/vditor/dist/js/lute/lute.min.js +72 -0
  113. package/plugin.d.ts +3 -0
  114. package/plugin.mjs +80 -0
  115. package/presets/README.md +32 -0
  116. package/presets/openwrite/agent.cordis.yml +317 -0
  117. package/presets/openwrite/preset.yml +3 -0
  118. package/presets/openwrite/skills/canon/SKILL.md +15 -0
  119. package/presets/openwrite/skills/dialoguequality/SKILL.md +166 -0
  120. package/presets/openwrite/skills/dog-delivery-query/SKILL.md +40 -0
  121. package/presets/openwrite/skills/dog-import-query/SKILL.md +27 -0
  122. package/presets/openwrite/skills/dog-review-query/SKILL.md +40 -0
  123. package/presets/openwrite/skills/export-book/SKILL.md +14 -0
  124. package/presets/openwrite/skills/foreshadow/SKILL.md +14 -0
  125. package/presets/openwrite/skills/foreshadowing-system/SKILL.md +125 -0
  126. package/presets/openwrite/skills/learn/SKILL.md +24 -0
  127. package/presets/openwrite/skills/novel-creator/SKILL.md +88 -0
  128. package/presets/openwrite/skills/novel-creator/templates/beat_templates.yaml +179 -0
  129. package/presets/openwrite/skills/novel-creator/templates/example_usage.yaml +74 -0
  130. package/presets/openwrite/skills/novel-reviewer/SKILL.md +85 -0
  131. package/presets/openwrite/skills/oh-story-deslop/LICENSE +21 -0
  132. package/presets/openwrite/skills/oh-story-deslop/SKILL.md +36 -0
  133. package/presets/openwrite/skills/oh-story-deslop/agents/openai.yaml +4 -0
  134. package/presets/openwrite/skills/oh-story-deslop/references/method.md +22 -0
  135. package/presets/openwrite/skills/oh-story-long-analyze/LICENSE +21 -0
  136. package/presets/openwrite/skills/oh-story-long-analyze/SKILL.md +37 -0
  137. package/presets/openwrite/skills/oh-story-long-analyze/agents/openai.yaml +4 -0
  138. package/presets/openwrite/skills/oh-story-long-analyze/references/method.md +22 -0
  139. package/presets/openwrite/skills/oh-story-long-scan/LICENSE +21 -0
  140. package/presets/openwrite/skills/oh-story-long-scan/SKILL.md +36 -0
  141. package/presets/openwrite/skills/oh-story-long-scan/agents/openai.yaml +4 -0
  142. package/presets/openwrite/skills/oh-story-long-scan/references/method.md +21 -0
  143. package/presets/openwrite/skills/oh-story-long-write/LICENSE +21 -0
  144. package/presets/openwrite/skills/oh-story-long-write/SKILL.md +37 -0
  145. package/presets/openwrite/skills/oh-story-long-write/agents/openai.yaml +4 -0
  146. package/presets/openwrite/skills/oh-story-long-write/references/method.md +33 -0
  147. package/presets/openwrite/skills/oh-story-review/LICENSE +21 -0
  148. package/presets/openwrite/skills/oh-story-review/SKILL.md +36 -0
  149. package/presets/openwrite/skills/oh-story-review/agents/openai.yaml +4 -0
  150. package/presets/openwrite/skills/oh-story-review/references/method.md +25 -0
  151. package/presets/openwrite/skills/progress/SKILL.md +16 -0
  152. package/presets/openwrite/skills/review-chapter/SKILL.md +17 -0
  153. package/presets/openwrite/skills/revise-span/SKILL.md +16 -0
  154. package/presets/openwrite/skills/style-system/SKILL.md +135 -0
  155. package/presets/openwrite/skills/style-system/prompts/analyze_style.md +214 -0
  156. package/presets/openwrite/skills/style-system/prompts/batch_extract_style.md +66 -0
  157. package/presets/openwrite/skills/style-system/prompts/compose_style.md +83 -0
  158. package/presets/openwrite/skills/style-system/prompts/extract_style.md +84 -0
  159. package/presets/openwrite/skills/style-system/prompts/initialize_style.md +52 -0
  160. package/presets/openwrite/skills/style-system/templates/analysis_report.yaml +78 -0
  161. package/presets/openwrite/skills/style-system/templates/composed_style.md +96 -0
  162. package/presets/openwrite/skills/style-system/templates/extraction_report.yaml +80 -0
  163. package/presets/openwrite/skills/style-system/templates/fingerprint.yaml +61 -0
  164. package/presets/openwrite/skills/style-system/tools/README.md +33 -0
  165. package/presets/openwrite/skills/truth-validation/SKILL.md +119 -0
  166. package/presets/openwrite/skills/workflow-manager/SKILL.md +81 -0
  167. package/presets/openwrite/skills/world-query/SKILL.md +45 -0
  168. package/presets/openwrite/skills/write-next/SKILL.md +17 -0
  169. package/release/core-requirements.in +12 -0
  170. package/release/licenses/@deepseek-ai__cosmokit@1.8.3/LICENSE +21 -0
  171. package/release/licenses/@deepseek-ai__schemastery@3.18.2/LICENSE +21 -0
  172. package/release/licenses/@isaacs__fs-minipass@4.0.1/LICENSE +15 -0
  173. package/release/licenses/@standard-schema__spec@1.1.0/LICENSE +21 -0
  174. package/release/licenses/@types__d3-color@3.1.3/LICENSE +21 -0
  175. package/release/licenses/@types__d3-drag@3.0.7/LICENSE +21 -0
  176. package/release/licenses/@types__d3-interpolate@3.0.4/LICENSE +21 -0
  177. package/release/licenses/@types__d3-selection@3.0.11/LICENSE +21 -0
  178. package/release/licenses/@types__d3-transition@3.0.9/LICENSE +21 -0
  179. package/release/licenses/@types__d3-zoom@3.0.8/LICENSE +21 -0
  180. package/release/licenses/@xyflow__react@12.11.6/LICENSE +21 -0
  181. package/release/licenses/@xyflow__system@0.0.82/LICENSE +21 -0
  182. package/release/licenses/ajv-formats@3.0.1/LICENSE +21 -0
  183. package/release/licenses/ajv@8.20.0/LICENSE +22 -0
  184. package/release/licenses/chownr@3.0.0/LICENSE.md +63 -0
  185. package/release/licenses/classcat@5.0.5/LICENSE.md +7 -0
  186. package/release/licenses/commander@7.2.0/LICENSE +22 -0
  187. package/release/licenses/d3-array@3.2.4/LICENSE +13 -0
  188. package/release/licenses/d3-axis@3.0.0/LICENSE +13 -0
  189. package/release/licenses/d3-brush@3.0.0/LICENSE +13 -0
  190. package/release/licenses/d3-chord@3.0.1/LICENSE +13 -0
  191. package/release/licenses/d3-color@3.1.0/LICENSE +13 -0
  192. package/release/licenses/d3-contour@4.0.2/LICENSE +13 -0
  193. package/release/licenses/d3-delaunay@6.0.4/LICENSE +14 -0
  194. package/release/licenses/d3-dispatch@3.0.1/LICENSE +13 -0
  195. package/release/licenses/d3-drag@3.0.0/LICENSE +13 -0
  196. package/release/licenses/d3-dsv@3.0.1/LICENSE +13 -0
  197. package/release/licenses/d3-ease@3.0.1/LICENSE +28 -0
  198. package/release/licenses/d3-fetch@3.0.1/LICENSE +13 -0
  199. package/release/licenses/d3-force@3.0.0/LICENSE +13 -0
  200. package/release/licenses/d3-format@3.1.2/LICENSE +13 -0
  201. package/release/licenses/d3-geo@3.1.1/LICENSE +34 -0
  202. package/release/licenses/d3-hierarchy@3.1.2/LICENSE +13 -0
  203. package/release/licenses/d3-interpolate@3.0.1/LICENSE +13 -0
  204. package/release/licenses/d3-path@3.1.0/LICENSE +13 -0
  205. package/release/licenses/d3-polygon@3.0.1/LICENSE +13 -0
  206. package/release/licenses/d3-quadtree@3.0.1/LICENSE +13 -0
  207. package/release/licenses/d3-random@3.0.1/LICENSE +13 -0
  208. package/release/licenses/d3-scale-chromatic@3.1.0/LICENSE +28 -0
  209. package/release/licenses/d3-scale@4.0.2/LICENSE +13 -0
  210. package/release/licenses/d3-selection@3.0.0/LICENSE +13 -0
  211. package/release/licenses/d3-shape@3.2.0/LICENSE +13 -0
  212. package/release/licenses/d3-time-format@4.1.0/LICENSE +13 -0
  213. package/release/licenses/d3-time@3.1.0/LICENSE +13 -0
  214. package/release/licenses/d3-timer@3.0.1/LICENSE +13 -0
  215. package/release/licenses/d3-transition@3.0.1/LICENSE +13 -0
  216. package/release/licenses/d3-zoom@3.0.0/LICENSE +13 -0
  217. package/release/licenses/d3@7.9.0/LICENSE +13 -0
  218. package/release/licenses/delaunator@5.1.0/LICENSE +15 -0
  219. package/release/licenses/elkjs@0.12.0/LICENSE.md +263 -0
  220. package/release/licenses/fast-deep-equal@3.1.3/LICENSE +21 -0
  221. package/release/licenses/fast-uri@3.1.7/LICENSE +30 -0
  222. package/release/licenses/fflate@0.8.2/LICENSE +21 -0
  223. package/release/licenses/graceful-fs@4.2.11/LICENSE +15 -0
  224. package/release/licenses/iconv-lite@0.6.3/LICENSE +21 -0
  225. package/release/licenses/internmap@2.0.3/LICENSE +13 -0
  226. package/release/licenses/json-schema-traverse@1.0.0/LICENSE +21 -0
  227. package/release/licenses/lucide-react@0.468.0/LICENSE +15 -0
  228. package/release/licenses/minipass@7.1.3/LICENSE.md +55 -0
  229. package/release/licenses/minizlib@3.1.0/LICENSE +26 -0
  230. package/release/licenses/proper-lockfile@4.1.2/LICENSE +21 -0
  231. package/release/licenses/require-from-string@2.0.2/license +21 -0
  232. package/release/licenses/retry@0.12.0/License +21 -0
  233. package/release/licenses/robust-predicates@3.0.3/LICENSE +24 -0
  234. package/release/licenses/rw@1.3.3/LICENSE +26 -0
  235. package/release/licenses/safer-buffer@2.1.2/LICENSE +21 -0
  236. package/release/licenses/signal-exit@3.0.7/LICENSE.txt +16 -0
  237. package/release/licenses/tar@7.5.7/LICENSE.md +55 -0
  238. package/release/licenses/use-sync-external-store@1.6.0/LICENSE +21 -0
  239. package/release/licenses/yallist@5.0.0/LICENSE.md +63 -0
  240. package/release/licenses/zustand@4.5.7/LICENSE +21 -0
  241. package/release/openwrite-5.8.1-py3-none-any.whl +0 -0
  242. package/release/platform-overrides.in +5 -0
  243. package/release/python-constraints.in +96 -0
  244. package/release/requirements.lock +2927 -0
  245. package/release/runtime-manifest.json +98 -0
  246. package/release/sdk-compatibility.json +240 -0
  247. package/release/source.json +859 -0
  248. package/release/wheels/cryptography-50.0.1-cp312-abi3-macosx_13_0_x86_64.whl +0 -0
  249. package/release/wheels/licenses/LICENSE.txt +177 -0
  250. package/release/wheels/provenance.json +15 -0
  251. package/scripts/dog/delivery-stage.js +32 -0
  252. package/scripts/dog/import-chapter.js +20 -0
  253. package/scripts/dog/import-record.js +35 -0
  254. package/scripts/dog/review-dimension.js +41 -0
  255. package/scripts/dog/review-record.js +42 -0
  256. package/scripts/maintenance.mjs +179 -0
  257. package/scripts/prepare.mjs +21 -0
  258. package/vendor/dsh-dog/LICENSE +29 -0
  259. package/vendor/dsh-dog/README.md +114 -0
  260. package/vendor/dsh-dog/UPSTREAM.md +22 -0
  261. package/vendor/dsh-dog/cordis.patch.yml +14 -0
  262. package/vendor/dsh-dog/lib/client.js +2898 -0
  263. package/vendor/dsh-dog/lib/client.js.map +1 -0
  264. package/vendor/dsh-dog/lib/core-BiEBMLz3.d.ts +495 -0
  265. package/vendor/dsh-dog/lib/core-DdfMcrB4.js +2074 -0
  266. package/vendor/dsh-dog/lib/core.d.ts +2 -0
  267. package/vendor/dsh-dog/lib/core.js +2 -0
  268. package/vendor/dsh-dog/lib/index.d.ts +133 -0
  269. package/vendor/dsh-dog/lib/index.js +967 -0
  270. package/vendor/dsh-dog/package.json +97 -0
  271. package/vendor/dsh-dog/schemas/schema-0.2/graph.schema.json +139 -0
  272. package/vendor/dsh-dog/schemas/schema-0.2/report.schema.json +87 -0
  273. package/vendor/dsh-dog/schemas/schema-0.2/run.schema.json +259 -0
  274. package/vendor/dsh-dog/schemas/schema-0.2/runtime-event.schema.json +105 -0
  275. package/vendor/dsh-dog/schemas/schema-0.2/verification.schema.json +40 -0
@@ -0,0 +1,470 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
3
+ import { join, relative } from 'node:path';
4
+ import { validateReviewV2Decision } from './contracts-generated.js';
5
+ import { assertContained, assertWorkspaceRootMatch } from './dog-delivery.js';
6
+ export const DOG_REVIEW_DIMENSIONS = {
7
+ 1: 'OOC检查', 2: '时间线检查', 3: '设定冲突', 4: '战力崩坏', 5: '数值检查',
8
+ 6: '伏笔检查', 7: '节奏检查', 8: '文风检查', 9: '信息越界', 10: '词汇疲劳',
9
+ 11: '利益链断裂', 12: '年代考据', 13: '配角降智', 14: '配角工具人化', 15: '爽点虚化',
10
+ 16: '台词失真', 17: '流水账', 18: '知识库污染', 19: '视角一致性', 20: '段落等长',
11
+ 21: '套话密度', 22: '公式化转折', 23: '列表式结构', 24: '支线停滞', 25: '弧线平坦',
12
+ 26: '节奏单调', 27: '敏感词检查', 28: '正传事件冲突', 29: '未来信息泄露',
13
+ 30: '世界规则跨书一致性', 31: '番外伏笔隔离', 32: '读者期待管理', 33: '大纲偏离检测',
14
+ 34: '角色还原度', 35: '世界规则遵守', 36: '关系动态', 37: '正典事件一致性',
15
+ };
16
+ export const DOG_REVIEW_DOMAINS = [
17
+ { id: 'coherence', name: '连贯与逻辑', weight: 20, legacyCheckIds: [2, 3, 4, 5, 9, 11, 35] },
18
+ { id: 'character', name: '角色与关系', weight: 15, legacyCheckIds: [1, 13, 14, 16, 34, 36] },
19
+ { id: 'plot', name: '情节与承诺', weight: 20, legacyCheckIds: [6, 15, 24, 25, 32, 33] },
20
+ { id: 'pacing', name: '节奏与场景', weight: 15, legacyCheckIds: [7, 17, 26] },
21
+ { id: 'prose', name: '文风与表达', weight: 15, legacyCheckIds: [8, 10, 19, 20, 21, 22, 23] },
22
+ { id: 'canon', name: '正典与资料', weight: 15, legacyCheckIds: [12, 18, 28, 29, 30, 31, 37] },
23
+ ];
24
+ const HARD_SEVERITIES = new Set(['critical', 'blocker']);
25
+ function record(value) {
26
+ return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
27
+ }
28
+ function values(value) {
29
+ return Array.isArray(value) ? value : [];
30
+ }
31
+ const FRAMEWORK_REVISION_FIELDS = [
32
+ 'schema_version', 'id', 'version', 'rubric_version', 'graph_schema_version',
33
+ 'root', 'topology_locked', 'topology',
34
+ ];
35
+ function canonicalJson(value) {
36
+ if (Array.isArray(value))
37
+ return `[${value.map(canonicalJson).join(',')}]`;
38
+ if (value !== null && typeof value === 'object') {
39
+ const source = value;
40
+ return `{${Object.keys(source).sort().map(key => `${JSON.stringify(key)}:${canonicalJson(source[key])}`).join(',')}}`;
41
+ }
42
+ return JSON.stringify(value);
43
+ }
44
+ export function dogReviewFrameworkRevision(frameworkValue) {
45
+ const framework = record(frameworkValue);
46
+ const identity = {};
47
+ for (const key of FRAMEWORK_REVISION_FIELDS)
48
+ identity[key] = framework[key];
49
+ return `sha256:${createHash('sha256').update(canonicalJson(identity)).digest('hex')}`;
50
+ }
51
+ /** Bind OpenWrite's versioned review blueprint to one chapter. The bridge
52
+ * supplies paths only; all nodes and edges come from the canonical server. */
53
+ export function instantiateDogReviewFramework(frameworkValue, chapterId, relativeDirectory) {
54
+ const framework = record(frameworkValue);
55
+ if (framework['schema_version'] !== 'openwrite.review-dag-framework.v1') {
56
+ throw new Error(`unsupported review DAG framework: ${String(framework['schema_version'] ?? 'missing')}`);
57
+ }
58
+ if (framework['id'] !== 'openwrite.standard-chapter-review') {
59
+ throw new Error(`unsupported review DAG framework id: ${String(framework['id'] ?? 'missing')}`);
60
+ }
61
+ if (!/^sha256:[a-f0-9]{64}$/.test(String(framework['revision'] ?? ''))) {
62
+ throw new Error('review DAG framework lacks a valid revision');
63
+ }
64
+ if (framework['revision'] !== dogReviewFrameworkRevision(framework)) {
65
+ throw new Error('review DAG framework revision does not match its content');
66
+ }
67
+ if (!/^ch_\d+$/.test(chapterId))
68
+ throw new Error('review DAG chapter id must match ch_<digits>');
69
+ const prefix = relativeDirectory.replaceAll('\\', '/').replace(/\/+$/, '');
70
+ if (!prefix || prefix.startsWith('/') || prefix.split('/').includes('..')) {
71
+ throw new Error('review DAG artifact directory must be a contained relative path');
72
+ }
73
+ const topology = record(framework['topology']);
74
+ const templates = record(topology['nodes']);
75
+ const contains = values(topology['contains']);
76
+ const dependsOn = values(topology['dependsOn']);
77
+ const invariants = record(framework['invariants']);
78
+ if (framework['graph_schema_version'] !== '0.9')
79
+ throw new Error('unsupported review DAG graph schema');
80
+ if (Object.keys(templates).length !== Number(invariants['node_count'] ?? -1) || Object.keys(templates).length !== 47) {
81
+ throw new Error('review DAG framework node count does not match its invariant');
82
+ }
83
+ if (contains.length !== Number(invariants['contains_count'] ?? -1) || dependsOn.length !== Number(invariants['dependency_count'] ?? -1)) {
84
+ throw new Error('review DAG framework edge counts do not match its invariants');
85
+ }
86
+ for (let checkId = 1; checkId <= 37; checkId += 1) {
87
+ if (!Object.hasOwn(templates, `dim-${String(checkId).padStart(2, '0')}`)) {
88
+ throw new Error(`review DAG framework is missing legacy check ${checkId}`);
89
+ }
90
+ }
91
+ const root = String(framework['root'] ?? '');
92
+ if (!Object.hasOwn(templates, root) || record(templates[root])['kind'] !== 'composite') {
93
+ throw new Error('review DAG framework root must be a composite node');
94
+ }
95
+ const nodeIds = new Set(Object.keys(templates));
96
+ const parentByChild = new Map();
97
+ for (const edgeValue of contains) {
98
+ const edge = record(edgeValue);
99
+ const parent = String(edge['parent'] ?? '');
100
+ const child = String(edge['child'] ?? '');
101
+ if (!nodeIds.has(parent) || !nodeIds.has(child))
102
+ throw new Error('review DAG contains edge references an unknown node');
103
+ if (parentByChild.has(child))
104
+ throw new Error(`review DAG node has multiple parents: ${child}`);
105
+ parentByChild.set(child, parent);
106
+ }
107
+ if (parentByChild.size !== nodeIds.size - 1 || parentByChild.has(root)) {
108
+ throw new Error('review DAG containment must form one rooted tree');
109
+ }
110
+ const adjacency = new Map([...nodeIds].map(nodeId => [nodeId, []]));
111
+ for (const edgeValue of dependsOn) {
112
+ const edge = record(edgeValue);
113
+ const source = String(edge['source'] ?? '');
114
+ const target = String(edge['target'] ?? '');
115
+ if (!nodeIds.has(source) || !nodeIds.has(target))
116
+ throw new Error('review DAG dependency references an unknown node');
117
+ adjacency.get(target)?.push(source);
118
+ }
119
+ const visiting = new Set();
120
+ const visited = new Set();
121
+ const visit = (nodeId) => {
122
+ if (visiting.has(nodeId))
123
+ throw new Error('review DAG framework dependencies must be acyclic');
124
+ if (visited.has(nodeId))
125
+ return;
126
+ visiting.add(nodeId);
127
+ for (const successor of adjacency.get(nodeId) ?? [])
128
+ visit(successor);
129
+ visiting.delete(nodeId);
130
+ visited.add(nodeId);
131
+ };
132
+ for (const nodeId of nodeIds)
133
+ visit(nodeId);
134
+ const nodes = {};
135
+ for (const [nodeId, templateValue] of Object.entries(templates)) {
136
+ const template = record(templateValue);
137
+ const artifact = String(template['artifact'] ?? '');
138
+ if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*\.json$/.test(artifact)) {
139
+ throw new Error(`review DAG node ${nodeId} has an unsafe artifact binding`);
140
+ }
141
+ const titleTemplate = typeof template['title_template'] === 'string'
142
+ ? template['title_template']
143
+ : String(template['title'] ?? nodeId);
144
+ const node = {
145
+ kind: template['kind'],
146
+ title: titleTemplate.replaceAll('{chapter_id}', chapterId),
147
+ constraint: template['constraint'],
148
+ target: `${prefix}/${artifact}`,
149
+ verifier: template['verifier'],
150
+ };
151
+ if (template['completion'] !== undefined)
152
+ node['completion'] = template['completion'];
153
+ nodes[nodeId] = node;
154
+ }
155
+ return {
156
+ schemaVersion: String(framework['graph_schema_version'] ?? ''),
157
+ id: `novel-review-${chapterId}`,
158
+ root,
159
+ nodes,
160
+ contains: JSON.parse(JSON.stringify(contains)),
161
+ dependsOn: JSON.parse(JSON.stringify(dependsOn)),
162
+ };
163
+ }
164
+ function dimension(value) {
165
+ const number = typeof value === 'number' ? value : Number(value);
166
+ return Number.isInteger(number) && number in DOG_REVIEW_DIMENSIONS ? number : undefined;
167
+ }
168
+ function reviewV2(review) {
169
+ return record(review['review_v2']);
170
+ }
171
+ function selectedDimensions(review) {
172
+ const configured = reviewV2(review)['requested_dimensions'] ?? review['dimensions'];
173
+ if (!Array.isArray(configured))
174
+ return new Set(Object.keys(DOG_REVIEW_DIMENSIONS).map(Number));
175
+ return new Set(configured.map(dimension).filter((item) => item !== undefined));
176
+ }
177
+ function reviewSeverity(issue) {
178
+ const raw = String(issue['review_severity'] ?? issue['legacy_severity'] ?? issue['severity'] ?? 'warning').toLowerCase();
179
+ if (raw === 'critical' || raw === 'blocker')
180
+ return 'critical';
181
+ if (raw === 'info' || raw === 'low')
182
+ return 'info';
183
+ return 'warning';
184
+ }
185
+ function revisionPriority(issue) {
186
+ const raw = String(issue['revision_priority'] ?? '').toLowerCase();
187
+ if (['blocker', 'high', 'medium', 'low'].includes(raw))
188
+ return raw;
189
+ return { critical: 'blocker', warning: 'medium', info: 'low' }[reviewSeverity(issue)] ?? 'medium';
190
+ }
191
+ function normalizedIssue(value) {
192
+ const item = record(value);
193
+ const evidence = item['evidence'];
194
+ const severity = reviewSeverity(item);
195
+ return {
196
+ id: String(item['id'] ?? ''), severity, reviewSeverity: severity,
197
+ revisionPriority: revisionPriority(item), category: String(item['category'] ?? ''),
198
+ description: String(item['description'] ?? item['summary'] ?? ''),
199
+ suggestion: String(item['suggestion'] ?? ''),
200
+ evidence: typeof evidence === 'string' ? { quote: evidence } : record(evidence),
201
+ };
202
+ }
203
+ function verdict(status) {
204
+ if (['pass', 'completed', 'evaluated', 'not_applicable'].includes(status))
205
+ return 'pass';
206
+ if (['blocked', 'fail', 'failed', 'revise'].includes(status))
207
+ return 'fail';
208
+ return 'inconclusive';
209
+ }
210
+ /** v1-only adapter: derive a gate from legacy severities. Never used when
211
+ * review_v2 is present; v2 records carry OpenWrite's canonical gate_status. */
212
+ function legacyGateStatus(review) {
213
+ return values(review['issue_details']).some(item => reviewSeverity(record(item)) === 'critical') ? 'blocked' : 'pass';
214
+ }
215
+ /** v1-only adapter: derive delivery from legacy score/passed semantics. */
216
+ function legacyDeliveryStatus(review, threshold) {
217
+ if (legacyGateStatus(review) === 'blocked')
218
+ return 'blocked';
219
+ if (review['passed'] === false)
220
+ return 'revise';
221
+ return Number(review['score'] ?? 0) >= threshold ? 'pass' : 'revise';
222
+ }
223
+ function gateStatus(review) {
224
+ const v2 = reviewV2(review);
225
+ if (Object.keys(v2).length > 0)
226
+ return String(v2['gate_status'] ?? 'inconclusive').toLowerCase();
227
+ return legacyGateStatus(review);
228
+ }
229
+ function deliveryStatus(review, threshold) {
230
+ const v2 = reviewV2(review);
231
+ if (Object.keys(v2).length > 0)
232
+ return String(v2['delivery_status'] ?? 'inconclusive').toLowerCase();
233
+ return legacyDeliveryStatus(review, threshold);
234
+ }
235
+ function criteriaByDimension(review) {
236
+ const mapped = new Map();
237
+ for (const domain of values(reviewV2(review)['domains'])) {
238
+ for (const criterionValue of values(record(domain)['criteria'])) {
239
+ const criterion = record(criterionValue);
240
+ for (const rawId of values(criterion['legacy_check_ids'])) {
241
+ const number = dimension(rawId);
242
+ if (number !== undefined)
243
+ mapped.set(number, criterion);
244
+ }
245
+ }
246
+ }
247
+ return mapped;
248
+ }
249
+ export function buildDogReviewBundle(reviewValue, chapterId, threshold) {
250
+ if (!Number.isInteger(threshold) || threshold < 0 || threshold > 100) {
251
+ throw new Error('review threshold must be an integer between 0 and 100');
252
+ }
253
+ const review = record(reviewValue);
254
+ // Existence/type/version policy, mirroring Python review_store:
255
+ // a present review_v2 key (even null) must be a non-empty JSON object
256
+ // declaring the supported schema version; only records without the key
257
+ // ride the legacy v1 adapter.
258
+ const hasV2 = Object.hasOwn(review, 'review_v2');
259
+ const rawV2 = review['review_v2'];
260
+ if (hasV2) {
261
+ if (rawV2 === null || typeof rawV2 !== 'object' || Array.isArray(rawV2)) {
262
+ throw new Error(`review_v2 must be a JSON object when present, got ${rawV2 === null ? 'null' : typeof rawV2}`);
263
+ }
264
+ const versionProbe = rawV2;
265
+ if (Object.keys(versionProbe).length === 0) {
266
+ throw new Error('review_v2 empty object: unsupported or missing schema version');
267
+ }
268
+ if (versionProbe['schema_version'] !== 'openwrite.review.v2') {
269
+ throw new Error(`unsupported review_v2 schema version: ${String(versionProbe['schema_version'])}`);
270
+ }
271
+ // Full schema-derived contract check (generated from
272
+ // OpenWrite contracts/review-v2-decision.schema.json).
273
+ validateReviewV2Decision(rawV2);
274
+ }
275
+ const selected = selectedDimensions(review);
276
+ const grouped = new Map();
277
+ for (const number of Object.keys(DOG_REVIEW_DIMENSIONS).map(Number))
278
+ grouped.set(number, []);
279
+ const unmappedIssues = [];
280
+ for (const rawIssue of values(review['issue_details'])) {
281
+ const number = dimension(record(rawIssue)['dimension']);
282
+ const issue = normalizedIssue(rawIssue);
283
+ if (number === undefined)
284
+ unmappedIssues.push(issue);
285
+ else
286
+ grouped.get(number)?.push(issue);
287
+ }
288
+ const criteria = criteriaByDimension(review);
289
+ const gate = gateStatus(review);
290
+ const dimensionRecords = Object.entries(DOG_REVIEW_DIMENSIONS).map(([key, name]) => {
291
+ const number = Number(key);
292
+ const issues = grouped.get(number) ?? [];
293
+ const criterion = criteria.get(number) ?? {};
294
+ const criterionStatus = String(criterion['status'] ?? '');
295
+ let itemVerdict;
296
+ let status;
297
+ if (!selected.has(number)) {
298
+ itemVerdict = 'inconclusive';
299
+ status = 'not_requested';
300
+ }
301
+ else if (issues.some(item => item['reviewSeverity'] === 'critical')) {
302
+ itemVerdict = 'fail';
303
+ status = 'blocked';
304
+ }
305
+ else if (number === 27) {
306
+ itemVerdict = verdict(gate);
307
+ status = gate;
308
+ }
309
+ else if (Object.keys(criterion).length > 0) {
310
+ itemVerdict = verdict(criterionStatus);
311
+ status = criterionStatus;
312
+ }
313
+ else {
314
+ itemVerdict = 'pass';
315
+ status = 'legacy_evaluated';
316
+ }
317
+ return {
318
+ schemaVersion: 'dsh-novel.review.dimension.v2', recordType: 'review-dimension', chapterId,
319
+ dimension: number, name, verdict: itemVerdict, status, criterionId: String(criterion['id'] ?? ''),
320
+ issueCount: issues.length, issues, sourceReviewPassed: Boolean(review['passed']), sourceReviewScore: review['score'],
321
+ };
322
+ });
323
+ const v2 = reviewV2(review);
324
+ const rawDomains = new Map(values(v2['domains']).map(item => [String(record(item)['id'] ?? ''), record(item)]));
325
+ const domains = DOG_REVIEW_DOMAINS.map(spec => {
326
+ const raw = rawDomains.get(spec.id) ?? {};
327
+ const domainDimensions = dimensionRecords.filter(item => spec.legacyCheckIds.includes(Number(item['dimension'])));
328
+ const status = String(raw['status'] ?? '') || (domainDimensions.some(item => item['verdict'] === 'inconclusive') ? 'inconclusive' : 'evaluated');
329
+ const domainVerdict = domainDimensions.some(item => item['verdict'] === 'fail') ? 'fail' : verdict(status);
330
+ return {
331
+ schemaVersion: 'dsh-novel.review.domain.v2', recordType: 'review-domain', chapterId,
332
+ id: spec.id, name: spec.name, weight: spec.weight, verdict: domainVerdict, status,
333
+ earned: raw['earned'], max: raw['max'], potentialMax: raw['potential_max'], coverage: raw['coverage'],
334
+ legacyCheckIds: [...spec.legacyCheckIds], criteria: values(raw['criteria']),
335
+ issues: spec.legacyCheckIds.flatMap(number => grouped.get(number) ?? []),
336
+ };
337
+ });
338
+ const delivery = deliveryStatus(review, threshold);
339
+ return {
340
+ manifest: {
341
+ schemaVersion: 'dsh-novel.review.manifest.v2', recordType: 'review', chapterId, threshold,
342
+ verdict: verdict(delivery), executionStatus: String(v2['execution_status'] ?? (Object.keys(review).length > 0 ? 'completed' : 'failed')),
343
+ qualityScore: v2['quality_score'] ?? review['score'], coverage: v2['coverage'] ?? (Object.keys(review).length > 0 ? 1 : 0),
344
+ gateStatus: gate, deliveryStatus: delivery, sourceReviewPassed: Boolean(review['passed']), score: review['score'],
345
+ summary: String(review['summary'] ?? ''), requestedDimensions: [...selected].sort((a, b) => a - b),
346
+ dimensionCount: 37, issueCount: dimensionRecords.reduce((sum, item) => sum + Number(item['issueCount']), 0) + unmappedIssues.length,
347
+ unmappedIssueCount: unmappedIssues.length, unmappedIssues, sourceRevision: String(review['source_revision'] ?? ''),
348
+ provenance: record(v2['provenance']), domains, dimensions: dimensionRecords,
349
+ decisionSource: Object.hasOwn(review, 'review_v2') ? 'v2' : 'v1-adapter',
350
+ },
351
+ dimensionRecords,
352
+ };
353
+ }
354
+ async function findNamedFiles(root, filename) {
355
+ const found = [];
356
+ async function walk(directory) {
357
+ let entries;
358
+ try {
359
+ entries = await readdir(directory, { withFileTypes: true });
360
+ }
361
+ catch {
362
+ return;
363
+ }
364
+ for (const entry of entries) {
365
+ const path = join(directory, entry.name);
366
+ if (entry.isDirectory())
367
+ await walk(path);
368
+ else if (entry.isFile() && entry.name === filename)
369
+ found.push(path);
370
+ }
371
+ }
372
+ await walk(root);
373
+ return found;
374
+ }
375
+ /** Materialize the review response into model-free files consumable by dsh-dog. */
376
+ export async function materializeDogReview(response, chapterId, threshold, expectedRoot) {
377
+ const outer = record(response);
378
+ const review = record(outer['result'] ?? response);
379
+ const framework = record(outer['review_framework']);
380
+ const workspace = record(outer['workspace']);
381
+ const project = record(workspace['project']);
382
+ const snapshot = record(workspace['snapshot']);
383
+ const root = String(project['root'] ?? '');
384
+ const novelId = String(snapshot['novel_id'] ?? '');
385
+ if (!root || !novelId)
386
+ throw new Error('review response lacks workspace project root or novel_id');
387
+ if (expectedRoot !== undefined)
388
+ assertWorkspaceRootMatch(root, expectedRoot);
389
+ try {
390
+ if (!(await stat(root)).isDirectory())
391
+ throw new Error('not a directory');
392
+ }
393
+ catch {
394
+ throw new Error(`invalid project root: ${root}`);
395
+ }
396
+ const { manifest, dimensionRecords } = buildDogReviewBundle(review, chapterId, threshold);
397
+ // The artifact records retain the exact blueprint identity so a historical
398
+ // review can always be explained against the topology that produced it.
399
+ manifest['frameworkId'] = framework['id'];
400
+ manifest['frameworkVersion'] = framework['version'];
401
+ manifest['frameworkRevision'] = framework['revision'];
402
+ const manuscriptFiles = await findNamedFiles(join(root, 'data', 'novels', novelId, 'data', 'manuscript'), `${chapterId}.md`);
403
+ const currentRevision = manuscriptFiles.length === 1
404
+ ? `sha256:${createHash('sha256').update(await readFile(manuscriptFiles[0])).digest('hex')}`
405
+ : '';
406
+ const sourceRevision = String(manifest['sourceRevision'] ?? '') || currentRevision;
407
+ const stale = sourceRevision !== '' && currentRevision !== '' && sourceRevision !== currentRevision;
408
+ manifest['sourceRevision'] = sourceRevision;
409
+ manifest['currentRevision'] = currentRevision;
410
+ manifest['stale'] = stale;
411
+ if (stale) {
412
+ manifest['verdict'] = 'inconclusive';
413
+ manifest['deliveryStatus'] = 'stale';
414
+ }
415
+ const relativeDir = join('data', 'novels', novelId, 'data', 'dog', 'reviews', chapterId).replaceAll('\\', '/');
416
+ const graph = instantiateDogReviewFramework(framework, chapterId, relativeDir);
417
+ const directory = join(root, relativeDir);
418
+ assertContained(directory, root);
419
+ await mkdir(directory, { recursive: true });
420
+ const write = async (name, value) => writeFile(join(directory, name), `${JSON.stringify(value, null, 2)}\n`, 'utf8');
421
+ await write('review.json', manifest);
422
+ await write('context.json', {
423
+ schemaVersion: 'dsh-novel.review.context.v2', recordType: 'review-context', chapterId,
424
+ verdict: stale || sourceRevision === '' ? 'inconclusive' : 'pass',
425
+ status: stale ? 'stale' : sourceRevision !== '' ? 'current' : 'missing_revision',
426
+ sourceRevision, currentRevision, provenance: manifest['provenance'],
427
+ });
428
+ for (const domain of values(manifest['domains']).map(record))
429
+ await write(`domain_${domain['id']}.json`, domain);
430
+ const gateDimension = dimensionRecords.find(item => item['dimension'] === 27) ?? {};
431
+ await write('gate.json', {
432
+ schemaVersion: 'dsh-novel.review.gate.v2', recordType: 'review-gate', chapterId,
433
+ verdict: verdict(String(manifest['gateStatus'])), status: manifest['gateStatus'], legacyCheckIds: [27], issues: gateDimension['issues'],
434
+ });
435
+ await write('aggregate.json', {
436
+ schemaVersion: 'dsh-novel.review.aggregate.v2', recordType: 'review-aggregate', chapterId,
437
+ verdict: manifest['verdict'], executionStatus: manifest['executionStatus'], qualityScore: manifest['qualityScore'],
438
+ coverage: manifest['coverage'], gateStatus: manifest['gateStatus'], deliveryStatus: manifest['deliveryStatus'], threshold,
439
+ });
440
+ for (const item of dimensionRecords)
441
+ await write(`dim_${String(item['dimension']).padStart(2, '0')}.json`, item);
442
+ const target = (name) => `${relativeDir}/${name}`;
443
+ const graphPath = join(directory, 'dog-graph.json');
444
+ await write('dog-graph.json', graph);
445
+ return {
446
+ status: 'ready', graphPath, manifestPath: join(directory, 'review.json'),
447
+ graphTarget: relative(root, graphPath), manifestTarget: target('review.json'),
448
+ dimensions: 37, domains: 6,
449
+ framework: { id: framework['id'], version: framework['version'], revision: framework['revision'] },
450
+ };
451
+ }
452
+ export async function materializeCompletedDogTaskReview(response, workspace, framework, threshold = 70, expectedRoot) {
453
+ const outer = record(response);
454
+ const task = record(outer['task'] ?? response);
455
+ if (task['type'] !== 'chapter_review' || task['status'] !== 'completed')
456
+ return undefined;
457
+ const review = record(task['result']);
458
+ const input = record(task['input']);
459
+ const chapterId = String(task['chapter_id'] ?? review['chapter_id'] ?? '').trim()
460
+ || String(input['path'] ?? '').match(/(?:^|\/)(ch_\d+)\.md$/)?.[1];
461
+ if (!chapterId || !/^ch_\d+$/.test(chapterId))
462
+ throw new Error('completed chapter_review task lacks a resolvable chapter id');
463
+ return await materializeDogReview({ result: review, workspace, review_framework: framework }, chapterId, threshold, expectedRoot);
464
+ }
465
+ export function reviewChapterId(args) {
466
+ const candidate = args.chapter_id?.trim() || args.path?.match(/(?:^|\/)(ch_\d+)\.md$/)?.[1];
467
+ if (!candidate || !/^ch_\d+$/.test(candidate))
468
+ throw new Error('could not resolve chapter id for DoG review graph');
469
+ return candidate;
470
+ }
@@ -0,0 +1,70 @@
1
+ import type { BackendConnection } from './managed-runtime.js';
2
+ import { Service, type Context } from '@deepseek-ai/cordis';
3
+ import type { ToolRunContext } from '@deepseek-ai/dsh-tools';
4
+ import { StudioClient, type WorkspaceContext } from './client.js';
5
+ export declare const CONFIG_ROUTE = "/studio-panel/config.json";
6
+ export declare const API_PROXY_ROUTE = "/studio-panel/api";
7
+ export declare const EVENTS_ROUTE = "/studio-panel/events";
8
+ export declare const INVALIDATION_ROUTE = "/studio-panel/invalidation.json";
9
+ declare module '@deepseek-ai/cordis' {
10
+ interface Context {
11
+ novelDomain: NovelDomainService;
12
+ }
13
+ }
14
+ export interface NovelDomainOptions {
15
+ outputDir?: string;
16
+ authorizeRequest?: (headers: Record<string, string | string[] | undefined>) => number | undefined;
17
+ connect?: () => Promise<BackendConnection>;
18
+ baseUrl: string;
19
+ timeoutMs: number;
20
+ /**
21
+ * Host-trusted workspace_id → canonical root resolution (dsh
22
+ * `ctx.workspaceRegistry`), looked up lazily per call so plugin start order
23
+ * cannot strand it. Absent resolver or a `null` result means the registry
24
+ * service itself is missing: every browser entry fails closed with
25
+ * WORKSPACE_REGISTRY_UNAVAILABLE. `undefined` means unknown id.
26
+ */
27
+ resolveWorkspace?: (workspaceId: string) => string | undefined | null;
28
+ }
29
+ /** Single host-side boundary shared by agent tools, browser proxy and live invalidation. */
30
+ export declare class NovelDomainService extends Service {
31
+ readonly toolOptions: {
32
+ timeoutMs: number;
33
+ outputDir?: string;
34
+ };
35
+ readonly client: StudioClient;
36
+ private readonly authorizeRequest;
37
+ private readonly connect;
38
+ readonly baseUrl: string;
39
+ private readonly resolveWorkspace;
40
+ /** Per-root invalidation state, keyed by canonical workspace root ('' = legacy). */
41
+ private readonly roots;
42
+ /** Open SSE connections bound to the root key they subscribed to. */
43
+ private readonly streams;
44
+ constructor(ctx: Context, options: NovelDomainOptions);
45
+ /**
46
+ * Factory for the tool surface: every tool execution derives its own
47
+ * request-scoped client from the dsh session context (fail closed).
48
+ */
49
+ clientFactory(): (exec: ToolRunContext) => StudioClient;
50
+ /** Record a mutation under its root and broadcast only to that root's SSE connections. */
51
+ notifyMutation(path: string, context?: WorkspaceContext): void;
52
+ /** Snapshot of one root's invalidation state (zero revision when never mutated). */
53
+ private snapshotFor;
54
+ /**
55
+ * Best-effort read of the upstream per-root context epoch (the only signal
56
+ * that background task transitions bump). ANY failure — network, non-2xx,
57
+ * missing/invalid field — degrades to null so the caller omits the field;
58
+ * this helper never throws and never breaks a snapshot.
59
+ */
60
+ private fetchContextEpoch;
61
+ /**
62
+ * Resolve a browser-supplied workspace id through the host registry, failing
63
+ * closed per the contract: missing id → 400 WORKSPACE_CONTEXT_MISSING, no
64
+ * registry → 503 WORKSPACE_REGISTRY_UNAVAILABLE, unknown id → 400 WORKSPACE_UNKNOWN.
65
+ */
66
+ private resolveBrowserWorkspace;
67
+ private static sendRejection;
68
+ registerWebRoutes(ctx: Context): void;
69
+ private createProxyHandler;
70
+ }