@knights-eye-chess/ai-analysis 0.0.0-stage → 0.1.0-dev.14

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 (258) hide show
  1. package/LICENSE +235 -0
  2. package/LICENSING.md +26 -0
  3. package/NOTICE +5 -0
  4. package/NPM-PUBLISHING.md +24 -0
  5. package/README.md +141 -2
  6. package/SOURCE-ORIGIN.json +16 -0
  7. package/THIRD_PARTY_NOTICES.md +18 -0
  8. package/build.mjs +22 -0
  9. package/dist/answer-pipeline.test.mjs +375 -0
  10. package/dist/chunk-PLKNJRPF.js +10662 -0
  11. package/dist/chunk-XQDZJOYD.js +2515 -0
  12. package/dist/index.js +146 -0
  13. package/dist/owner-tests/chess-reasoning-compact-search-preparation.test.mjs +963 -0
  14. package/dist/owner-tests/mechanism-encoding.test.mjs +529 -0
  15. package/dist/owner-tests/move-mechanism-facts.test.mjs +396 -0
  16. package/dist/owner-tests/pipeline-host-preparation.test.mjs +10556 -0
  17. package/dist/owner-tests/position-preparation.test.mjs +625 -0
  18. package/dist/owner-tests/preparation-consolidation.test.mjs +1052 -0
  19. package/dist/owner-tests/prose-reference-admission.test.mjs +1770 -0
  20. package/dist/owner-tests/score-comparison-notices.test.mjs +240 -0
  21. package/dist/owner-tests/simple-engine-presentation.test.mjs +233 -0
  22. package/dist/owner-tests/tool-input-loop-guard.test.mjs +78 -0
  23. package/dist/research.js +2274 -0
  24. package/dist/runtime-closure.json +103 -0
  25. package/dist/shared-runtime-closure.json +39 -0
  26. package/dist/shared.js +152 -0
  27. package/dist/types/ai-request-failure.d.ts +14 -0
  28. package/dist/types/ai-request-limits.d.ts +4 -0
  29. package/dist/types/ai-routing-diagnostics.d.ts +14 -0
  30. package/dist/types/chess-reasoning/ablation-adaptive-evidence-loop.d.ts +127 -0
  31. package/dist/types/chess-reasoning/ablation-adaptive-runtime.d.ts +178 -0
  32. package/dist/types/chess-reasoning/ablation-direct-synthesis.d.ts +1030 -0
  33. package/dist/types/chess-reasoning/ablation-evidence-bundle.d.ts +185 -0
  34. package/dist/types/chess-reasoning/ablation-evidence-capture.d.ts +37 -0
  35. package/dist/types/chess-reasoning/ablation-lean-direct-runtime.d.ts +193 -0
  36. package/dist/types/chess-reasoning/ablation-model-facing-evidence.d.ts +38 -0
  37. package/dist/types/chess-reasoning/ablation-transport-correction.d.ts +13 -0
  38. package/dist/types/chess-reasoning/ai-sdk-runtime-support.d.ts +93 -0
  39. package/dist/types/chess-reasoning/answer-host-contracts.d.ts +59 -0
  40. package/dist/types/chess-reasoning/answer-model-connector.d.ts +68 -0
  41. package/dist/types/chess-reasoning/answer-pipeline-exports.d.ts +98 -0
  42. package/dist/types/chess-reasoning/answer-pipeline.d.ts +41 -0
  43. package/dist/types/chess-reasoning/answer-reference-validation.d.ts +40 -0
  44. package/dist/types/chess-reasoning/answer-tool-host.d.ts +8 -0
  45. package/dist/types/chess-reasoning/branch-expansion-policy.d.ts +13 -0
  46. package/dist/types/chess-reasoning/branch-expansion.d.ts +39 -0
  47. package/dist/types/chess-reasoning/causal-claim-checks.d.ts +21 -0
  48. package/dist/types/chess-reasoning/chess-reference-dictionary.d.ts +13 -0
  49. package/dist/types/chess-reasoning/classification-question-target.d.ts +22 -0
  50. package/dist/types/chess-reasoning/classification-reassessment.d.ts +31 -0
  51. package/dist/types/chess-reasoning/compact-search-preparation.d.ts +7 -0
  52. package/dist/types/chess-reasoning/concept-registry.d.ts +99 -0
  53. package/dist/types/chess-reasoning/conversation-context.d.ts +12 -0
  54. package/dist/types/chess-reasoning/engine-search-failure.d.ts +7 -0
  55. package/dist/types/chess-reasoning/evidence-citation-normalization.d.ts +9 -0
  56. package/dist/types/chess-reasoning/evidence-licenses.d.ts +57 -0
  57. package/dist/types/chess-reasoning/evidence-selector-contract.d.ts +5 -0
  58. package/dist/types/chess-reasoning/existing-analysis-context.d.ts +104 -0
  59. package/dist/types/chess-reasoning/fact-observation-provider.d.ts +15 -0
  60. package/dist/types/chess-reasoning/fact-provider.d.ts +111 -0
  61. package/dist/types/chess-reasoning/fresh-engine-pv-witnesses.d.ts +67 -0
  62. package/dist/types/chess-reasoning/historical-prose.d.ts +23 -0
  63. package/dist/types/chess-reasoning/idea-graph.d.ts +34 -0
  64. package/dist/types/chess-reasoning/initial-search-plan.d.ts +37 -0
  65. package/dist/types/chess-reasoning/lossless-evidence-sharing.d.ts +18 -0
  66. package/dist/types/chess-reasoning/matched-comparison.d.ts +10 -0
  67. package/dist/types/chess-reasoning/material-accounting.d.ts +80 -0
  68. package/dist/types/chess-reasoning/material-format.d.ts +4 -0
  69. package/dist/types/chess-reasoning/mechanical-fact-provider.d.ts +33 -0
  70. package/dist/types/chess-reasoning/mechanical-ray-facts.d.ts +19 -0
  71. package/dist/types/chess-reasoning/mechanism-presentation.d.ts +9 -0
  72. package/dist/types/chess-reasoning/move-mechanism-facts.d.ts +45 -0
  73. package/dist/types/chess-reasoning/native-answer-connector.d.ts +4 -0
  74. package/dist/types/chess-reasoning/opening-source-contracts.d.ts +54 -0
  75. package/dist/types/chess-reasoning/opening-source-credits.d.ts +16 -0
  76. package/dist/types/chess-reasoning/opponent-threat-preparation.d.ts +7 -0
  77. package/dist/types/chess-reasoning/piece-identities.d.ts +11 -0
  78. package/dist/types/chess-reasoning/poc-contracts.d.ts +1026 -0
  79. package/dist/types/chess-reasoning/poc-hypotheses.d.ts +74 -0
  80. package/dist/types/chess-reasoning/poc-move-witnesses.d.ts +13 -0
  81. package/dist/types/chess-reasoning/poc-observations.d.ts +23 -0
  82. package/dist/types/chess-reasoning/poc-piece-refs.d.ts +12 -0
  83. package/dist/types/chess-reasoning/poc-plan-model.d.ts +45 -0
  84. package/dist/types/chess-reasoning/poc-semantic-text.d.ts +8 -0
  85. package/dist/types/chess-reasoning/poc-tools.d.ts +81 -0
  86. package/dist/types/chess-reasoning/portable-answer-runtime.d.ts +291 -0
  87. package/dist/types/chess-reasoning/position-analysis-contracts.d.ts +31 -0
  88. package/dist/types/chess-reasoning/position-analysis-scope.d.ts +3 -0
  89. package/dist/types/chess-reasoning/preparation-consolidation.d.ts +21 -0
  90. package/dist/types/chess-reasoning/production-observation-provider.d.ts +16 -0
  91. package/dist/types/chess-reasoning/question-routing.d.ts +326 -0
  92. package/dist/types/chess-reasoning/recorded-main-line.d.ts +37 -0
  93. package/dist/types/chess-reasoning/recorded-position-tools.d.ts +11 -0
  94. package/dist/types/chess-reasoning/recorded-reference-context.d.ts +13 -0
  95. package/dist/types/chess-reasoning/recorded-selected-classification.d.ts +36 -0
  96. package/dist/types/chess-reasoning/reference-limits.d.ts +2 -0
  97. package/dist/types/chess-reasoning/research-exports.d.ts +197 -0
  98. package/dist/types/chess-reasoning/saved-score-presentation.d.ts +14 -0
  99. package/dist/types/chess-reasoning/score-comparison-notices.d.ts +33 -0
  100. package/dist/types/chess-reasoning/server-simple-policy.d.ts +31 -0
  101. package/dist/types/chess-reasoning/simple-agent-config.d.ts +73 -0
  102. package/dist/types/chess-reasoning/simple-board-analysis.d.ts +85 -0
  103. package/dist/types/chess-reasoning/simple-compact-evidence.d.ts +7 -0
  104. package/dist/types/chess-reasoning/simple-completion.d.ts +23 -0
  105. package/dist/types/chess-reasoning/simple-engine-presentation.d.ts +3 -0
  106. package/dist/types/chess-reasoning/simple-evidence-preparation.d.ts +17 -0
  107. package/dist/types/chess-reasoning/simple-evidence-view.d.ts +17 -0
  108. package/dist/types/chess-reasoning/simple-explanation-coverage.d.ts +90 -0
  109. package/dist/types/chess-reasoning/simple-fact-delivery.d.ts +43 -0
  110. package/dist/types/chess-reasoning/simple-fallback-premise.d.ts +5 -0
  111. package/dist/types/chess-reasoning/simple-prose-references.d.ts +108 -0
  112. package/dist/types/chess-reasoning/simple-runtime-version.d.ts +1 -0
  113. package/dist/types/chess-reasoning/stable.d.ts +2 -0
  114. package/dist/types/chess-reasoning/tool-input-loop-guard.d.ts +5 -0
  115. package/dist/types/chess-reasoning/types.d.ts +180 -0
  116. package/dist/types/chess-reasoning/visible-support-lines.d.ts +12 -0
  117. package/dist/types/chess-reasoning/wikibooks-index.d.ts +30 -0
  118. package/dist/types/chess-reasoning/wikibooks-tool-context.d.ts +10 -0
  119. package/dist/types/player-perspective.d.ts +16 -0
  120. package/dist/types/shared-exports.d.ts +22 -0
  121. package/docs/API.md +82 -0
  122. package/docs/OWNERSHIP-ADOPTION.md +35 -0
  123. package/docs/OWNERSHIP-DRIFT.json +55 -0
  124. package/docs/RESEARCH-API.md +28 -0
  125. package/licenses/third-party/INVENTORY.json +202 -0
  126. package/licenses/third-party/ai-7.0.85/LICENSE +13 -0
  127. package/licenses/third-party/ai-sdk--gateway-4.0.69/LICENSE +13 -0
  128. package/licenses/third-party/ai-sdk--provider-4.0.9/LICENSE +13 -0
  129. package/licenses/third-party/ai-sdk--provider-utils-5.0.34/APACHE-2.0.txt +73 -0
  130. package/licenses/third-party/ai-sdk--provider-utils-5.0.34/src/to-json-schema/zod3-to-json-schema/LICENSE +16 -0
  131. package/licenses/third-party/chess.js-1.4.0/LICENSE +23 -0
  132. package/licenses/third-party/eventsource-parser-3.1.1/LICENSE +21 -0
  133. package/licenses/third-party/json-schema-0.4.0/LICENSE +195 -0
  134. package/licenses/third-party/standard-schema--spec-1.1.0/LICENSE +21 -0
  135. package/licenses/third-party/types--json-schema-7.0.15/LICENSE +21 -0
  136. package/licenses/third-party/types--node-22.19.19/LICENSE +21 -0
  137. package/licenses/third-party/undici-7.30.0/LICENSE +21 -0
  138. package/licenses/third-party/undici-7.30.0/lib/web/fetch/LICENSE +21 -0
  139. package/licenses/third-party/undici-types-6.21.0/LICENSE +21 -0
  140. package/licenses/third-party/vercel--oidc-3.2.0/LICENSE +202 -0
  141. package/licenses/third-party/workflow--serde-4.1.0/APACHE-2.0.txt +73 -0
  142. package/licenses/third-party/workflow--serde-4.1.0/LICENSE.md +3 -0
  143. package/licenses/third-party/zod-4.6.5/LICENSE +21 -0
  144. package/npm-release.json +5 -0
  145. package/package.json +72 -4
  146. package/scripts/check-release-tooling.mjs +22 -0
  147. package/scripts/pack.mjs +68 -0
  148. package/scripts/publish-reviewed.mjs +72 -0
  149. package/scripts/release-tooling.json +13 -0
  150. package/scripts/update-dependency.mjs +44 -0
  151. package/scripts/verify-release.mjs +52 -0
  152. package/src/ai-request-failure.ts +34 -0
  153. package/src/ai-request-limits.ts +4 -0
  154. package/src/ai-routing-diagnostics.ts +33 -0
  155. package/src/chess-reasoning/ablation-adaptive-evidence-loop.ts +515 -0
  156. package/src/chess-reasoning/ablation-adaptive-runtime.ts +924 -0
  157. package/src/chess-reasoning/ablation-direct-synthesis.ts +1128 -0
  158. package/src/chess-reasoning/ablation-evidence-bundle.ts +726 -0
  159. package/src/chess-reasoning/ablation-evidence-capture.ts +513 -0
  160. package/src/chess-reasoning/ablation-lean-direct-runtime.ts +670 -0
  161. package/src/chess-reasoning/ablation-model-facing-evidence.ts +212 -0
  162. package/src/chess-reasoning/ablation-transport-correction.ts +106 -0
  163. package/src/chess-reasoning/ai-sdk-runtime-support.ts +381 -0
  164. package/src/chess-reasoning/answer-host-contracts.ts +10 -0
  165. package/src/chess-reasoning/answer-model-connector.ts +83 -0
  166. package/src/chess-reasoning/answer-pipeline-exports.ts +98 -0
  167. package/src/chess-reasoning/answer-pipeline.ts +195 -0
  168. package/src/chess-reasoning/answer-reference-validation.ts +124 -0
  169. package/src/chess-reasoning/answer-tool-host.ts +10 -0
  170. package/src/chess-reasoning/branch-expansion-policy.ts +32 -0
  171. package/src/chess-reasoning/branch-expansion.ts +80 -0
  172. package/src/chess-reasoning/causal-claim-checks.ts +256 -0
  173. package/src/chess-reasoning/chess-reference-dictionary.ts +21 -0
  174. package/src/chess-reasoning/classification-question-target.ts +30 -0
  175. package/src/chess-reasoning/classification-reassessment.ts +34 -0
  176. package/src/chess-reasoning/compact-search-preparation.ts +125 -0
  177. package/src/chess-reasoning/concept-registry.ts +275 -0
  178. package/src/chess-reasoning/conversation-context.ts +29 -0
  179. package/src/chess-reasoning/engine-search-failure.ts +11 -0
  180. package/src/chess-reasoning/evidence-citation-normalization.ts +15 -0
  181. package/src/chess-reasoning/evidence-licenses.ts +166 -0
  182. package/src/chess-reasoning/evidence-selector-contract.ts +5 -0
  183. package/src/chess-reasoning/existing-analysis-context.ts +544 -0
  184. package/src/chess-reasoning/fact-observation-provider.ts +134 -0
  185. package/src/chess-reasoning/fact-provider.ts +250 -0
  186. package/src/chess-reasoning/fresh-engine-pv-witnesses.ts +128 -0
  187. package/src/chess-reasoning/historical-prose.ts +116 -0
  188. package/src/chess-reasoning/idea-graph.ts +224 -0
  189. package/src/chess-reasoning/initial-search-plan.ts +115 -0
  190. package/src/chess-reasoning/lossless-evidence-sharing.ts +135 -0
  191. package/src/chess-reasoning/matched-comparison.ts +40 -0
  192. package/src/chess-reasoning/material-accounting.ts +55 -0
  193. package/src/chess-reasoning/material-format.ts +47 -0
  194. package/src/chess-reasoning/mechanical-fact-provider.ts +1602 -0
  195. package/src/chess-reasoning/mechanical-ray-facts.ts +123 -0
  196. package/src/chess-reasoning/mechanism-presentation.ts +94 -0
  197. package/src/chess-reasoning/move-mechanism-facts.ts +185 -0
  198. package/src/chess-reasoning/native-answer-connector.ts +13 -0
  199. package/src/chess-reasoning/opening-source-contracts.ts +47 -0
  200. package/src/chess-reasoning/opening-source-credits.ts +53 -0
  201. package/src/chess-reasoning/opponent-threat-preparation.ts +98 -0
  202. package/src/chess-reasoning/piece-identities.ts +159 -0
  203. package/src/chess-reasoning/poc-contracts.ts +1097 -0
  204. package/src/chess-reasoning/poc-hypotheses.ts +759 -0
  205. package/src/chess-reasoning/poc-move-witnesses.ts +170 -0
  206. package/src/chess-reasoning/poc-observations.ts +611 -0
  207. package/src/chess-reasoning/poc-piece-refs.ts +62 -0
  208. package/src/chess-reasoning/poc-plan-model.ts +345 -0
  209. package/src/chess-reasoning/poc-semantic-text.ts +10 -0
  210. package/src/chess-reasoning/poc-tools.ts +978 -0
  211. package/src/chess-reasoning/portable-answer-runtime.ts +684 -0
  212. package/src/chess-reasoning/position-analysis-contracts.ts +88 -0
  213. package/src/chess-reasoning/position-analysis-scope.ts +20 -0
  214. package/src/chess-reasoning/preparation-consolidation.ts +91 -0
  215. package/src/chess-reasoning/production-observation-provider.ts +41 -0
  216. package/src/chess-reasoning/question-routing.ts +332 -0
  217. package/src/chess-reasoning/recorded-main-line.ts +55 -0
  218. package/src/chess-reasoning/recorded-position-tools.ts +78 -0
  219. package/src/chess-reasoning/recorded-reference-context.ts +44 -0
  220. package/src/chess-reasoning/recorded-selected-classification.ts +54 -0
  221. package/src/chess-reasoning/reference-limits.ts +2 -0
  222. package/src/chess-reasoning/research-exports.ts +197 -0
  223. package/src/chess-reasoning/saved-score-presentation.ts +6 -0
  224. package/src/chess-reasoning/score-comparison-notices.ts +115 -0
  225. package/src/chess-reasoning/server-simple-policy.ts +75 -0
  226. package/src/chess-reasoning/simple-agent-config.ts +58 -0
  227. package/src/chess-reasoning/simple-board-analysis.ts +57 -0
  228. package/src/chess-reasoning/simple-compact-evidence.ts +79 -0
  229. package/src/chess-reasoning/simple-completion.ts +37 -0
  230. package/src/chess-reasoning/simple-engine-presentation.ts +95 -0
  231. package/src/chess-reasoning/simple-evidence-preparation.ts +32 -0
  232. package/src/chess-reasoning/simple-evidence-view.ts +69 -0
  233. package/src/chess-reasoning/simple-explanation-coverage.ts +118 -0
  234. package/src/chess-reasoning/simple-fact-delivery.ts +52 -0
  235. package/src/chess-reasoning/simple-fallback-premise.ts +65 -0
  236. package/src/chess-reasoning/simple-prose-references.ts +528 -0
  237. package/src/chess-reasoning/simple-runtime-version.ts +2 -0
  238. package/src/chess-reasoning/stable.ts +18 -0
  239. package/src/chess-reasoning/tool-input-loop-guard.ts +38 -0
  240. package/src/chess-reasoning/types.ts +195 -0
  241. package/src/chess-reasoning/visible-support-lines.ts +44 -0
  242. package/src/chess-reasoning/wikibooks-index.ts +56 -0
  243. package/src/chess-reasoning/wikibooks-tool-context.ts +91 -0
  244. package/src/player-perspective.ts +41 -0
  245. package/src/shared-exports.ts +23 -0
  246. package/test/answer-pipeline.test.mjs +375 -0
  247. package/test/owner/chess-reasoning-compact-search-preparation.test.mjs +111 -0
  248. package/test/owner/mechanism-encoding.test.mjs +78 -0
  249. package/test/owner/move-mechanism-facts.test.mjs +105 -0
  250. package/test/owner/pipeline-host-preparation.test.mjs +115 -0
  251. package/test/owner/position-preparation.test.mjs +22 -0
  252. package/test/owner/preparation-consolidation.test.mjs +74 -0
  253. package/test/owner/prose-reference-admission.test.mjs +26 -0
  254. package/test/owner/score-comparison-notices.test.mjs +116 -0
  255. package/test/owner/simple-engine-presentation.test.mjs +50 -0
  256. package/test/owner/tool-input-loop-guard.test.mjs +25 -0
  257. package/test-types.mts +23 -0
  258. package/tsconfig.json +22 -0
@@ -0,0 +1,345 @@
1
+ import {
2
+ DEFAULT_CONCEPT_REGISTRY,
3
+ type ConceptRegistry,
4
+ } from "./concept-registry";
5
+ import {
6
+ createIdeaGraph,
7
+ createSemanticEntity,
8
+ createSemanticRelation,
9
+ extendIdeaGraph,
10
+ } from "./idea-graph";
11
+ import { stableHash } from "./stable";
12
+ import { effectiveLicensesForRecord, licensesSemanticChessGrounding } from "./evidence-licenses";
13
+ import type {
14
+ ReasoningEvidenceRecord,
15
+ ReasoningPositionContext,
16
+ } from "./poc-contracts";
17
+ import type {
18
+ IdeaGraph,
19
+ ProofStrength,
20
+ SemanticAttributes,
21
+ SemanticParticipant,
22
+ SemanticRelation,
23
+ SemanticValue,
24
+ } from "./types";
25
+
26
+ export type PlanEntityHypothesis = {
27
+ hypothesisId: string;
28
+ localId: string;
29
+ entityType: string;
30
+ concepts: string[];
31
+ owner: "w" | "b" | null;
32
+ participants: SemanticParticipant[];
33
+ attributes: SemanticAttributes;
34
+ proofStrength: ProofStrength | null;
35
+ evidenceIds: string[];
36
+ sourceObservationIds: string[];
37
+ };
38
+
39
+ export type PlanRelationHypothesis = {
40
+ hypothesisRelationId: string;
41
+ relationType: string;
42
+ from: string;
43
+ to: string;
44
+ attributes: SemanticAttributes;
45
+ evidenceIds: string[];
46
+ };
47
+
48
+ export type PlanHypothesisSet = {
49
+ entities: PlanEntityHypothesis[];
50
+ relations: PlanRelationHypothesis[];
51
+ };
52
+
53
+ export type ExplainPlanRequest = {
54
+ hypothesisLedgerRevision: string;
55
+ hypotheses: PlanHypothesisSet;
56
+ startEntityId: string;
57
+ goalEntityId: string;
58
+ requiredEntityIds: string[];
59
+ maxEntities: number;
60
+ };
61
+
62
+ export type ResolvedPlanExplanation = {
63
+ hypothesisLedgerRevision: string;
64
+ graph: IdeaGraph;
65
+ localToGraphIds: Record<string, string>;
66
+ selectedEntityIds: string[];
67
+ selectedRelationIds: string[];
68
+ evidenceIds: string[];
69
+ };
70
+
71
+ const PROOF_RANK: Record<ProofStrength, number> = {
72
+ observed: 1,
73
+ supported: 2,
74
+ forced: 3,
75
+ };
76
+
77
+ const FORBIDDEN_SEMANTIC_KEYS = new Set(["isFork", "isSkewer", "isMainLine"]);
78
+
79
+ function isRecord(value: unknown): value is Record<string, unknown> {
80
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
81
+ }
82
+
83
+ function boundedString(value: unknown, label: string, max = 160): string {
84
+ if (typeof value !== "string" || !value.trim() || value.length > max) {
85
+ throw new Error(`${label} must be a non-empty string of at most ${max} characters`);
86
+ }
87
+ return value.trim();
88
+ }
89
+
90
+ function semanticValue(value: unknown, label: string, depth = 0): SemanticValue {
91
+ if (depth > 5) throw new Error(`${label} exceeds the semantic-value depth limit`);
92
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
93
+ if (typeof value === "number" && Number.isFinite(value)) return value;
94
+ if (Array.isArray(value)) {
95
+ if (value.length > 24) throw new Error(`${label} exceeds the semantic-value item limit`);
96
+ return value.map((item, index) => semanticValue(item, `${label}[${index}]`, depth + 1));
97
+ }
98
+ if (!isRecord(value)) throw new Error(`${label} is not a JSON semantic value`);
99
+ const entries = Object.entries(value);
100
+ if (entries.length > 24) throw new Error(`${label} exceeds the semantic-value field limit`);
101
+ return Object.fromEntries(entries.map(([key, item]) => {
102
+ if (FORBIDDEN_SEMANTIC_KEYS.has(key)) throw new Error(`${key} must be a registry value, not a semantic flag`);
103
+ return [boundedString(key, `${label} key`, 80), semanticValue(item, `${label}.${key}`, depth + 1)];
104
+ }));
105
+ }
106
+
107
+ function stringArray(value: unknown, label: string, max: number): string[] {
108
+ if (!Array.isArray(value) || value.length < 1 || value.length > max) {
109
+ throw new Error(`${label} must contain 1..${max} values`);
110
+ }
111
+ return [...new Set(value.map((item, index) => boundedString(item, `${label}[${index}]`)))];
112
+ }
113
+
114
+ function optionalStringArray(value: unknown, label: string, max: number): string[] {
115
+ if (value == null) return [];
116
+ if (!Array.isArray(value) || value.length > max) throw new Error(`${label} must contain at most ${max} values`);
117
+ return [...new Set(value.map((item, index) => boundedString(item, `${label}[${index}]`)))];
118
+ }
119
+
120
+ function attributes(value: unknown, label: string): SemanticAttributes {
121
+ if (value == null) return {};
122
+ const parsed = semanticValue(value, label);
123
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") throw new Error(`${label} must be an object`);
124
+ return parsed;
125
+ }
126
+
127
+ function participants(value: unknown, label: string): SemanticParticipant[] {
128
+ if (value == null) return [];
129
+ if (!Array.isArray(value) || value.length > 12) throw new Error(`${label} must contain at most 12 participants`);
130
+ return value.map((item, index) => {
131
+ if (!isRecord(item) || !isRecord(item.ref)) throw new Error(`${label}[${index}] is malformed`);
132
+ return {
133
+ role: boundedString(item.role, `${label}[${index}].role`),
134
+ ref: {
135
+ domain: boundedString(item.ref.domain, `${label}[${index}].ref.domain`),
136
+ id: boundedString(item.ref.id, `${label}[${index}].ref.id`),
137
+ },
138
+ };
139
+ });
140
+ }
141
+
142
+ function parseProofStrength(value: unknown): ProofStrength | null {
143
+ if (value == null) return null;
144
+ if (value === "observed" || value === "supported" || value === "forced") return value;
145
+ throw new Error("proofStrength must be observed, supported, forced, or null");
146
+ }
147
+
148
+ export function parseExplainPlanRequest(value: unknown): ExplainPlanRequest {
149
+ if (!isRecord(value) || !isRecord(value.hypotheses)) throw new Error("explain_plan requires hypotheses");
150
+ const hypothesisLedgerRevision = boundedString(value.hypothesisLedgerRevision, "hypothesisLedgerRevision", 200);
151
+ const rawEntities = value.hypotheses.entities;
152
+ const rawRelations = value.hypotheses.relations;
153
+ if (!Array.isArray(rawEntities) || rawEntities.length < 1 || rawEntities.length > 12) {
154
+ throw new Error("explain_plan requires 1..12 entity hypotheses");
155
+ }
156
+ if (!Array.isArray(rawRelations) || rawRelations.length > 20) {
157
+ throw new Error("explain_plan requires 0..20 relation hypotheses");
158
+ }
159
+ const entities = rawEntities.map((item, index): PlanEntityHypothesis => {
160
+ if (!isRecord(item)) throw new Error(`hypotheses.entities[${index}] is malformed`);
161
+ const owner = item.owner == null ? null : item.owner;
162
+ if (owner !== null && owner !== "w" && owner !== "b") throw new Error("entity owner must be w, b, or null");
163
+ return {
164
+ localId: boundedString(item.localId, `hypotheses.entities[${index}].localId`),
165
+ entityType: boundedString(item.entityType, `hypotheses.entities[${index}].entityType`),
166
+ concepts: stringArray(item.concepts, `hypotheses.entities[${index}].concepts`, 4),
167
+ owner,
168
+ participants: participants(item.participants, `hypotheses.entities[${index}].participants`),
169
+ attributes: attributes(item.attributes, `hypotheses.entities[${index}].attributes`),
170
+ proofStrength: parseProofStrength(item.proofStrength),
171
+ evidenceIds: stringArray(item.evidenceIds, `hypotheses.entities[${index}].evidenceIds`, 12),
172
+ hypothesisId: boundedString(item.hypothesisId, `hypotheses.entities[${index}].hypothesisId`, 200),
173
+ sourceObservationIds: optionalStringArray(item.sourceObservationIds, `hypotheses.entities[${index}].sourceObservationIds`, 12),
174
+ };
175
+ });
176
+ const relations = rawRelations.map((item, index): PlanRelationHypothesis => {
177
+ if (!isRecord(item)) throw new Error(`hypotheses.relations[${index}] is malformed`);
178
+ return {
179
+ hypothesisRelationId: boundedString(item.hypothesisRelationId, `hypotheses.relations[${index}].hypothesisRelationId`, 200),
180
+ relationType: boundedString(item.relationType, `hypotheses.relations[${index}].relationType`),
181
+ from: boundedString(item.from, `hypotheses.relations[${index}].from`),
182
+ to: boundedString(item.to, `hypotheses.relations[${index}].to`),
183
+ attributes: attributes(item.attributes, `hypotheses.relations[${index}].attributes`),
184
+ evidenceIds: stringArray(item.evidenceIds, `hypotheses.relations[${index}].evidenceIds`, 12),
185
+ };
186
+ });
187
+ const localIds = new Set(entities.map((entity) => entity.localId));
188
+ if (localIds.size !== entities.length) throw new Error("Entity hypothesis localIds must be unique");
189
+ for (const relation of relations) {
190
+ if (!localIds.has(relation.from) || !localIds.has(relation.to)) throw new Error("Relation hypothesis references an unknown localId");
191
+ }
192
+ const startEntityId = boundedString(value.startEntityId, "startEntityId");
193
+ const goalEntityId = boundedString(value.goalEntityId, "goalEntityId");
194
+ const requiredEntityIds = optionalStringArray(value.requiredEntityIds, "requiredEntityIds", 10);
195
+ for (const localId of [startEntityId, goalEntityId, ...requiredEntityIds]) {
196
+ if (!localIds.has(localId)) throw new Error(`Selected entity ${localId} does not exist in the hypotheses`);
197
+ }
198
+ const maxEntities = value.maxEntities == null ? 8 : Number(value.maxEntities);
199
+ if (!Number.isInteger(maxEntities) || maxEntities < 1 || maxEntities > 12) throw new Error("maxEntities must be an integer from 1 through 12");
200
+ return { hypothesisLedgerRevision, hypotheses: { entities, relations }, startEntityId, goalEntityId, requiredEntityIds, maxEntities };
201
+ }
202
+
203
+ function compatibleEvidence(
204
+ ids: string[],
205
+ evidence: Map<string, ReasoningEvidenceRecord>,
206
+ contextHash: string,
207
+ ): ReasoningEvidenceRecord[] {
208
+ const records = ids.map((id) => evidence.get(id));
209
+ if (records.some((record) => !record || record.contextHash !== contextHash)) {
210
+ throw new Error("Plan hypothesis references missing, stale, or incompatible evidence");
211
+ }
212
+ return records as ReasoningEvidenceRecord[];
213
+ }
214
+
215
+ function validateProof(localId: string, proof: ProofStrength | null, records: ReasoningEvidenceRecord[]) {
216
+ if (!proof) return;
217
+ const supported = records.some((record) => record.proofStrength && PROOF_RANK[record.proofStrength] >= PROOF_RANK[proof]);
218
+ if (!supported) throw new Error(`Plan hypothesis ${localId} overstates available proof as ${proof}`);
219
+ }
220
+
221
+ function validateSemanticLicense(localId: string, records: ReasoningEvidenceRecord[]) {
222
+ if (!records.some((record) => licensesSemanticChessGrounding(effectiveLicensesForRecord(record)))) {
223
+ throw new Error(`Plan hypothesis or relation ${localId} lacks evidence licensed for semantic chess grounding`);
224
+ }
225
+ }
226
+
227
+ function selectExplanationGraph(
228
+ graph: IdeaGraph,
229
+ start: string,
230
+ goal: string,
231
+ required: string[],
232
+ maxEntities: number,
233
+ ): { entityIds: string[]; relations: SemanticRelation[] } {
234
+ const terminals = new Set([start, goal, ...required]);
235
+ const entityIds = graph.entities.map((entity) => entity.id).sort();
236
+ if (entityIds.length > maxEntities) throw new Error("Selected explanation graph exceeds maxEntities");
237
+ if ([...terminals].some((terminal) => !entityIds.includes(terminal))) {
238
+ throw new Error("Selected explanation graph omits a required endpoint");
239
+ }
240
+ const relations = [...graph.relations].sort((left, right) => left.id.localeCompare(right.id));
241
+ const adjacency = new Map(entityIds.map((id) => [id, [] as string[]]));
242
+ for (const relation of relations) {
243
+ adjacency.get(relation.from)?.push(relation.to);
244
+ adjacency.get(relation.to)?.push(relation.from);
245
+ }
246
+ const componentFor = new Map<string, number>();
247
+ let component = 0;
248
+ for (const entityId of entityIds) {
249
+ if (componentFor.has(entityId)) continue;
250
+ const queue = [entityId];
251
+ componentFor.set(entityId, component);
252
+ for (let cursor = 0; cursor < queue.length; cursor += 1) {
253
+ for (const neighbor of adjacency.get(queue[cursor]) || []) {
254
+ if (componentFor.has(neighbor)) continue;
255
+ componentFor.set(neighbor, component);
256
+ queue.push(neighbor);
257
+ }
258
+ }
259
+ component += 1;
260
+ }
261
+ if (componentFor.get(start) !== componentFor.get(goal)) {
262
+ throw new Error("No connected primary explanation chain covers start and goal");
263
+ }
264
+ const terminalComponents = new Set([...terminals].map((terminal) => componentFor.get(terminal)));
265
+ if ([...new Set(componentFor.values())].some((id) => !terminalComponents.has(id))) {
266
+ throw new Error("Selected explanation graph contains a disconnected optional component");
267
+ }
268
+ return { entityIds, relations };
269
+ }
270
+
271
+ export function resolveAndSelectPlan(
272
+ rawRequest: unknown,
273
+ context: ReasoningPositionContext,
274
+ contextHash: string,
275
+ availableEvidence: Iterable<ReasoningEvidenceRecord>,
276
+ registry: ConceptRegistry = DEFAULT_CONCEPT_REGISTRY,
277
+ ): ResolvedPlanExplanation {
278
+ const request = parseExplainPlanRequest(rawRequest);
279
+ const evidence = new Map([...availableEvidence].map((record) => [record.id, record]));
280
+ const branchId = `reasoning:${context.nodeId}`;
281
+ const ply = context.historyUci.length + 1;
282
+ const localToGraphIds: Record<string, string> = {};
283
+ const entities = request.hypotheses.entities.map((hypothesis) => {
284
+ for (const concept of hypothesis.concepts) {
285
+ if (!registry[concept]) throw new Error(`Unknown concept key: ${concept}`);
286
+ }
287
+ const records = compatibleEvidence(hypothesis.evidenceIds, evidence, contextHash);
288
+ validateSemanticLicense(hypothesis.localId, records);
289
+ validateProof(hypothesis.localId, hypothesis.proofStrength, records);
290
+ const id = `entity:${branchId}:${ply}:${stableHash({ contextHash, hypothesis })}`;
291
+ localToGraphIds[hypothesis.localId] = id;
292
+ return createSemanticEntity({
293
+ id,
294
+ branchId,
295
+ entityType: hypothesis.entityType,
296
+ concepts: hypothesis.concepts,
297
+ owner: hypothesis.owner,
298
+ createdByPly: ply,
299
+ createdByUci: context.moveUci,
300
+ participants: hypothesis.participants,
301
+ attributes: {
302
+ ...hypothesis.attributes,
303
+ sourceHypothesisIds: [hypothesis.hypothesisId],
304
+ sourceObservationIds: hypothesis.sourceObservationIds,
305
+ },
306
+ proofStrength: hypothesis.proofStrength,
307
+ state: hypothesis.proofStrength ? "supported" : "proposed",
308
+ evidence: hypothesis.evidenceIds,
309
+ }, registry);
310
+ });
311
+ const relations = request.hypotheses.relations.map((hypothesis) => {
312
+ const records = compatibleEvidence(hypothesis.evidenceIds, evidence, contextHash);
313
+ validateSemanticLicense(hypothesis.hypothesisRelationId, records);
314
+ return createSemanticRelation({
315
+ branchId,
316
+ relationType: hypothesis.relationType,
317
+ from: localToGraphIds[hypothesis.from],
318
+ to: localToGraphIds[hypothesis.to],
319
+ createdAtPly: ply,
320
+ attributes: { ...hypothesis.attributes, sourceHypothesisRelationIds: [hypothesis.hypothesisRelationId] },
321
+ evidence: hypothesis.evidenceIds,
322
+ });
323
+ });
324
+ const graph = extendIdeaGraph(createIdeaGraph(branchId, ply), entities, relations, ply);
325
+ const selected = selectExplanationGraph(
326
+ graph,
327
+ localToGraphIds[request.startEntityId],
328
+ localToGraphIds[request.goalEntityId],
329
+ request.requiredEntityIds.map((id) => localToGraphIds[id]),
330
+ request.maxEntities,
331
+ );
332
+ const selectedEntities = new Set(selected.entityIds);
333
+ const evidenceIds = [...new Set([
334
+ ...graph.entities.filter((entity) => selectedEntities.has(entity.id)).flatMap((entity) => entity.evidence),
335
+ ...selected.relations.flatMap((relation) => relation.evidence),
336
+ ])].sort();
337
+ return {
338
+ hypothesisLedgerRevision: request.hypothesisLedgerRevision,
339
+ graph,
340
+ localToGraphIds,
341
+ selectedEntityIds: selected.entityIds,
342
+ selectedRelationIds: selected.relations.map((relation) => relation.id),
343
+ evidenceIds,
344
+ };
345
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Board objects must travel as typed narrative segments. This pattern catches
3
+ * piece names, coordinates, UCI, common SAN (including captures and
4
+ * disambiguation), and castling when they leak into opaque text.
5
+ */
6
+ export const RAW_CHESS_TEXT_PATTERN_SOURCE = String.raw`(?:\b(?:(?:[Ww]hite|[Bb]lack)(?:['’]s)?\s+)?(?:[Bb]ishop|[Kk]night(?!['’]s\s+[Ee]ye\b)|[Rr]ook|[Qq]ueen(?!-[Ss]ide\b)|[Kk]ing(?!-[Ss]ide\b)|[Pp]awn)\b|\b(?:[a-h][1-8][a-h][1-8][qrbn]?|[KQRBN][a-h1-8]?x?[a-h][1-8](?:=[QRBN])?[+#]?|[a-h]x[a-h][1-8](?:=[QRBN])?[+#]?|O-O(?:-O)?|[a-h][1-8])\b)`;
7
+
8
+ export const RAW_CHESS_TEXT = new RegExp(RAW_CHESS_TEXT_PATTERN_SOURCE, "i");
9
+
10
+ export const SEMANTIC_TEXT_JSON_PATTERN = `^(?![\\s\\S]*${RAW_CHESS_TEXT_PATTERN_SOURCE})[\\s\\S]{1,600}$`;