@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.
- package/LICENSE +235 -0
- package/LICENSING.md +26 -0
- package/NOTICE +5 -0
- package/NPM-PUBLISHING.md +24 -0
- package/README.md +141 -2
- package/SOURCE-ORIGIN.json +16 -0
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/build.mjs +22 -0
- package/dist/answer-pipeline.test.mjs +375 -0
- package/dist/chunk-PLKNJRPF.js +10662 -0
- package/dist/chunk-XQDZJOYD.js +2515 -0
- package/dist/index.js +146 -0
- package/dist/owner-tests/chess-reasoning-compact-search-preparation.test.mjs +963 -0
- package/dist/owner-tests/mechanism-encoding.test.mjs +529 -0
- package/dist/owner-tests/move-mechanism-facts.test.mjs +396 -0
- package/dist/owner-tests/pipeline-host-preparation.test.mjs +10556 -0
- package/dist/owner-tests/position-preparation.test.mjs +625 -0
- package/dist/owner-tests/preparation-consolidation.test.mjs +1052 -0
- package/dist/owner-tests/prose-reference-admission.test.mjs +1770 -0
- package/dist/owner-tests/score-comparison-notices.test.mjs +240 -0
- package/dist/owner-tests/simple-engine-presentation.test.mjs +233 -0
- package/dist/owner-tests/tool-input-loop-guard.test.mjs +78 -0
- package/dist/research.js +2274 -0
- package/dist/runtime-closure.json +103 -0
- package/dist/shared-runtime-closure.json +39 -0
- package/dist/shared.js +152 -0
- package/dist/types/ai-request-failure.d.ts +14 -0
- package/dist/types/ai-request-limits.d.ts +4 -0
- package/dist/types/ai-routing-diagnostics.d.ts +14 -0
- package/dist/types/chess-reasoning/ablation-adaptive-evidence-loop.d.ts +127 -0
- package/dist/types/chess-reasoning/ablation-adaptive-runtime.d.ts +178 -0
- package/dist/types/chess-reasoning/ablation-direct-synthesis.d.ts +1030 -0
- package/dist/types/chess-reasoning/ablation-evidence-bundle.d.ts +185 -0
- package/dist/types/chess-reasoning/ablation-evidence-capture.d.ts +37 -0
- package/dist/types/chess-reasoning/ablation-lean-direct-runtime.d.ts +193 -0
- package/dist/types/chess-reasoning/ablation-model-facing-evidence.d.ts +38 -0
- package/dist/types/chess-reasoning/ablation-transport-correction.d.ts +13 -0
- package/dist/types/chess-reasoning/ai-sdk-runtime-support.d.ts +93 -0
- package/dist/types/chess-reasoning/answer-host-contracts.d.ts +59 -0
- package/dist/types/chess-reasoning/answer-model-connector.d.ts +68 -0
- package/dist/types/chess-reasoning/answer-pipeline-exports.d.ts +98 -0
- package/dist/types/chess-reasoning/answer-pipeline.d.ts +41 -0
- package/dist/types/chess-reasoning/answer-reference-validation.d.ts +40 -0
- package/dist/types/chess-reasoning/answer-tool-host.d.ts +8 -0
- package/dist/types/chess-reasoning/branch-expansion-policy.d.ts +13 -0
- package/dist/types/chess-reasoning/branch-expansion.d.ts +39 -0
- package/dist/types/chess-reasoning/causal-claim-checks.d.ts +21 -0
- package/dist/types/chess-reasoning/chess-reference-dictionary.d.ts +13 -0
- package/dist/types/chess-reasoning/classification-question-target.d.ts +22 -0
- package/dist/types/chess-reasoning/classification-reassessment.d.ts +31 -0
- package/dist/types/chess-reasoning/compact-search-preparation.d.ts +7 -0
- package/dist/types/chess-reasoning/concept-registry.d.ts +99 -0
- package/dist/types/chess-reasoning/conversation-context.d.ts +12 -0
- package/dist/types/chess-reasoning/engine-search-failure.d.ts +7 -0
- package/dist/types/chess-reasoning/evidence-citation-normalization.d.ts +9 -0
- package/dist/types/chess-reasoning/evidence-licenses.d.ts +57 -0
- package/dist/types/chess-reasoning/evidence-selector-contract.d.ts +5 -0
- package/dist/types/chess-reasoning/existing-analysis-context.d.ts +104 -0
- package/dist/types/chess-reasoning/fact-observation-provider.d.ts +15 -0
- package/dist/types/chess-reasoning/fact-provider.d.ts +111 -0
- package/dist/types/chess-reasoning/fresh-engine-pv-witnesses.d.ts +67 -0
- package/dist/types/chess-reasoning/historical-prose.d.ts +23 -0
- package/dist/types/chess-reasoning/idea-graph.d.ts +34 -0
- package/dist/types/chess-reasoning/initial-search-plan.d.ts +37 -0
- package/dist/types/chess-reasoning/lossless-evidence-sharing.d.ts +18 -0
- package/dist/types/chess-reasoning/matched-comparison.d.ts +10 -0
- package/dist/types/chess-reasoning/material-accounting.d.ts +80 -0
- package/dist/types/chess-reasoning/material-format.d.ts +4 -0
- package/dist/types/chess-reasoning/mechanical-fact-provider.d.ts +33 -0
- package/dist/types/chess-reasoning/mechanical-ray-facts.d.ts +19 -0
- package/dist/types/chess-reasoning/mechanism-presentation.d.ts +9 -0
- package/dist/types/chess-reasoning/move-mechanism-facts.d.ts +45 -0
- package/dist/types/chess-reasoning/native-answer-connector.d.ts +4 -0
- package/dist/types/chess-reasoning/opening-source-contracts.d.ts +54 -0
- package/dist/types/chess-reasoning/opening-source-credits.d.ts +16 -0
- package/dist/types/chess-reasoning/opponent-threat-preparation.d.ts +7 -0
- package/dist/types/chess-reasoning/piece-identities.d.ts +11 -0
- package/dist/types/chess-reasoning/poc-contracts.d.ts +1026 -0
- package/dist/types/chess-reasoning/poc-hypotheses.d.ts +74 -0
- package/dist/types/chess-reasoning/poc-move-witnesses.d.ts +13 -0
- package/dist/types/chess-reasoning/poc-observations.d.ts +23 -0
- package/dist/types/chess-reasoning/poc-piece-refs.d.ts +12 -0
- package/dist/types/chess-reasoning/poc-plan-model.d.ts +45 -0
- package/dist/types/chess-reasoning/poc-semantic-text.d.ts +8 -0
- package/dist/types/chess-reasoning/poc-tools.d.ts +81 -0
- package/dist/types/chess-reasoning/portable-answer-runtime.d.ts +291 -0
- package/dist/types/chess-reasoning/position-analysis-contracts.d.ts +31 -0
- package/dist/types/chess-reasoning/position-analysis-scope.d.ts +3 -0
- package/dist/types/chess-reasoning/preparation-consolidation.d.ts +21 -0
- package/dist/types/chess-reasoning/production-observation-provider.d.ts +16 -0
- package/dist/types/chess-reasoning/question-routing.d.ts +326 -0
- package/dist/types/chess-reasoning/recorded-main-line.d.ts +37 -0
- package/dist/types/chess-reasoning/recorded-position-tools.d.ts +11 -0
- package/dist/types/chess-reasoning/recorded-reference-context.d.ts +13 -0
- package/dist/types/chess-reasoning/recorded-selected-classification.d.ts +36 -0
- package/dist/types/chess-reasoning/reference-limits.d.ts +2 -0
- package/dist/types/chess-reasoning/research-exports.d.ts +197 -0
- package/dist/types/chess-reasoning/saved-score-presentation.d.ts +14 -0
- package/dist/types/chess-reasoning/score-comparison-notices.d.ts +33 -0
- package/dist/types/chess-reasoning/server-simple-policy.d.ts +31 -0
- package/dist/types/chess-reasoning/simple-agent-config.d.ts +73 -0
- package/dist/types/chess-reasoning/simple-board-analysis.d.ts +85 -0
- package/dist/types/chess-reasoning/simple-compact-evidence.d.ts +7 -0
- package/dist/types/chess-reasoning/simple-completion.d.ts +23 -0
- package/dist/types/chess-reasoning/simple-engine-presentation.d.ts +3 -0
- package/dist/types/chess-reasoning/simple-evidence-preparation.d.ts +17 -0
- package/dist/types/chess-reasoning/simple-evidence-view.d.ts +17 -0
- package/dist/types/chess-reasoning/simple-explanation-coverage.d.ts +90 -0
- package/dist/types/chess-reasoning/simple-fact-delivery.d.ts +43 -0
- package/dist/types/chess-reasoning/simple-fallback-premise.d.ts +5 -0
- package/dist/types/chess-reasoning/simple-prose-references.d.ts +108 -0
- package/dist/types/chess-reasoning/simple-runtime-version.d.ts +1 -0
- package/dist/types/chess-reasoning/stable.d.ts +2 -0
- package/dist/types/chess-reasoning/tool-input-loop-guard.d.ts +5 -0
- package/dist/types/chess-reasoning/types.d.ts +180 -0
- package/dist/types/chess-reasoning/visible-support-lines.d.ts +12 -0
- package/dist/types/chess-reasoning/wikibooks-index.d.ts +30 -0
- package/dist/types/chess-reasoning/wikibooks-tool-context.d.ts +10 -0
- package/dist/types/player-perspective.d.ts +16 -0
- package/dist/types/shared-exports.d.ts +22 -0
- package/docs/API.md +82 -0
- package/docs/OWNERSHIP-ADOPTION.md +35 -0
- package/docs/OWNERSHIP-DRIFT.json +55 -0
- package/docs/RESEARCH-API.md +28 -0
- package/licenses/third-party/INVENTORY.json +202 -0
- package/licenses/third-party/ai-7.0.85/LICENSE +13 -0
- package/licenses/third-party/ai-sdk--gateway-4.0.69/LICENSE +13 -0
- package/licenses/third-party/ai-sdk--provider-4.0.9/LICENSE +13 -0
- package/licenses/third-party/ai-sdk--provider-utils-5.0.34/APACHE-2.0.txt +73 -0
- package/licenses/third-party/ai-sdk--provider-utils-5.0.34/src/to-json-schema/zod3-to-json-schema/LICENSE +16 -0
- package/licenses/third-party/chess.js-1.4.0/LICENSE +23 -0
- package/licenses/third-party/eventsource-parser-3.1.1/LICENSE +21 -0
- package/licenses/third-party/json-schema-0.4.0/LICENSE +195 -0
- package/licenses/third-party/standard-schema--spec-1.1.0/LICENSE +21 -0
- package/licenses/third-party/types--json-schema-7.0.15/LICENSE +21 -0
- package/licenses/third-party/types--node-22.19.19/LICENSE +21 -0
- package/licenses/third-party/undici-7.30.0/LICENSE +21 -0
- package/licenses/third-party/undici-7.30.0/lib/web/fetch/LICENSE +21 -0
- package/licenses/third-party/undici-types-6.21.0/LICENSE +21 -0
- package/licenses/third-party/vercel--oidc-3.2.0/LICENSE +202 -0
- package/licenses/third-party/workflow--serde-4.1.0/APACHE-2.0.txt +73 -0
- package/licenses/third-party/workflow--serde-4.1.0/LICENSE.md +3 -0
- package/licenses/third-party/zod-4.6.5/LICENSE +21 -0
- package/npm-release.json +5 -0
- package/package.json +72 -4
- package/scripts/check-release-tooling.mjs +22 -0
- package/scripts/pack.mjs +68 -0
- package/scripts/publish-reviewed.mjs +72 -0
- package/scripts/release-tooling.json +13 -0
- package/scripts/update-dependency.mjs +44 -0
- package/scripts/verify-release.mjs +52 -0
- package/src/ai-request-failure.ts +34 -0
- package/src/ai-request-limits.ts +4 -0
- package/src/ai-routing-diagnostics.ts +33 -0
- package/src/chess-reasoning/ablation-adaptive-evidence-loop.ts +515 -0
- package/src/chess-reasoning/ablation-adaptive-runtime.ts +924 -0
- package/src/chess-reasoning/ablation-direct-synthesis.ts +1128 -0
- package/src/chess-reasoning/ablation-evidence-bundle.ts +726 -0
- package/src/chess-reasoning/ablation-evidence-capture.ts +513 -0
- package/src/chess-reasoning/ablation-lean-direct-runtime.ts +670 -0
- package/src/chess-reasoning/ablation-model-facing-evidence.ts +212 -0
- package/src/chess-reasoning/ablation-transport-correction.ts +106 -0
- package/src/chess-reasoning/ai-sdk-runtime-support.ts +381 -0
- package/src/chess-reasoning/answer-host-contracts.ts +10 -0
- package/src/chess-reasoning/answer-model-connector.ts +83 -0
- package/src/chess-reasoning/answer-pipeline-exports.ts +98 -0
- package/src/chess-reasoning/answer-pipeline.ts +195 -0
- package/src/chess-reasoning/answer-reference-validation.ts +124 -0
- package/src/chess-reasoning/answer-tool-host.ts +10 -0
- package/src/chess-reasoning/branch-expansion-policy.ts +32 -0
- package/src/chess-reasoning/branch-expansion.ts +80 -0
- package/src/chess-reasoning/causal-claim-checks.ts +256 -0
- package/src/chess-reasoning/chess-reference-dictionary.ts +21 -0
- package/src/chess-reasoning/classification-question-target.ts +30 -0
- package/src/chess-reasoning/classification-reassessment.ts +34 -0
- package/src/chess-reasoning/compact-search-preparation.ts +125 -0
- package/src/chess-reasoning/concept-registry.ts +275 -0
- package/src/chess-reasoning/conversation-context.ts +29 -0
- package/src/chess-reasoning/engine-search-failure.ts +11 -0
- package/src/chess-reasoning/evidence-citation-normalization.ts +15 -0
- package/src/chess-reasoning/evidence-licenses.ts +166 -0
- package/src/chess-reasoning/evidence-selector-contract.ts +5 -0
- package/src/chess-reasoning/existing-analysis-context.ts +544 -0
- package/src/chess-reasoning/fact-observation-provider.ts +134 -0
- package/src/chess-reasoning/fact-provider.ts +250 -0
- package/src/chess-reasoning/fresh-engine-pv-witnesses.ts +128 -0
- package/src/chess-reasoning/historical-prose.ts +116 -0
- package/src/chess-reasoning/idea-graph.ts +224 -0
- package/src/chess-reasoning/initial-search-plan.ts +115 -0
- package/src/chess-reasoning/lossless-evidence-sharing.ts +135 -0
- package/src/chess-reasoning/matched-comparison.ts +40 -0
- package/src/chess-reasoning/material-accounting.ts +55 -0
- package/src/chess-reasoning/material-format.ts +47 -0
- package/src/chess-reasoning/mechanical-fact-provider.ts +1602 -0
- package/src/chess-reasoning/mechanical-ray-facts.ts +123 -0
- package/src/chess-reasoning/mechanism-presentation.ts +94 -0
- package/src/chess-reasoning/move-mechanism-facts.ts +185 -0
- package/src/chess-reasoning/native-answer-connector.ts +13 -0
- package/src/chess-reasoning/opening-source-contracts.ts +47 -0
- package/src/chess-reasoning/opening-source-credits.ts +53 -0
- package/src/chess-reasoning/opponent-threat-preparation.ts +98 -0
- package/src/chess-reasoning/piece-identities.ts +159 -0
- package/src/chess-reasoning/poc-contracts.ts +1097 -0
- package/src/chess-reasoning/poc-hypotheses.ts +759 -0
- package/src/chess-reasoning/poc-move-witnesses.ts +170 -0
- package/src/chess-reasoning/poc-observations.ts +611 -0
- package/src/chess-reasoning/poc-piece-refs.ts +62 -0
- package/src/chess-reasoning/poc-plan-model.ts +345 -0
- package/src/chess-reasoning/poc-semantic-text.ts +10 -0
- package/src/chess-reasoning/poc-tools.ts +978 -0
- package/src/chess-reasoning/portable-answer-runtime.ts +684 -0
- package/src/chess-reasoning/position-analysis-contracts.ts +88 -0
- package/src/chess-reasoning/position-analysis-scope.ts +20 -0
- package/src/chess-reasoning/preparation-consolidation.ts +91 -0
- package/src/chess-reasoning/production-observation-provider.ts +41 -0
- package/src/chess-reasoning/question-routing.ts +332 -0
- package/src/chess-reasoning/recorded-main-line.ts +55 -0
- package/src/chess-reasoning/recorded-position-tools.ts +78 -0
- package/src/chess-reasoning/recorded-reference-context.ts +44 -0
- package/src/chess-reasoning/recorded-selected-classification.ts +54 -0
- package/src/chess-reasoning/reference-limits.ts +2 -0
- package/src/chess-reasoning/research-exports.ts +197 -0
- package/src/chess-reasoning/saved-score-presentation.ts +6 -0
- package/src/chess-reasoning/score-comparison-notices.ts +115 -0
- package/src/chess-reasoning/server-simple-policy.ts +75 -0
- package/src/chess-reasoning/simple-agent-config.ts +58 -0
- package/src/chess-reasoning/simple-board-analysis.ts +57 -0
- package/src/chess-reasoning/simple-compact-evidence.ts +79 -0
- package/src/chess-reasoning/simple-completion.ts +37 -0
- package/src/chess-reasoning/simple-engine-presentation.ts +95 -0
- package/src/chess-reasoning/simple-evidence-preparation.ts +32 -0
- package/src/chess-reasoning/simple-evidence-view.ts +69 -0
- package/src/chess-reasoning/simple-explanation-coverage.ts +118 -0
- package/src/chess-reasoning/simple-fact-delivery.ts +52 -0
- package/src/chess-reasoning/simple-fallback-premise.ts +65 -0
- package/src/chess-reasoning/simple-prose-references.ts +528 -0
- package/src/chess-reasoning/simple-runtime-version.ts +2 -0
- package/src/chess-reasoning/stable.ts +18 -0
- package/src/chess-reasoning/tool-input-loop-guard.ts +38 -0
- package/src/chess-reasoning/types.ts +195 -0
- package/src/chess-reasoning/visible-support-lines.ts +44 -0
- package/src/chess-reasoning/wikibooks-index.ts +56 -0
- package/src/chess-reasoning/wikibooks-tool-context.ts +91 -0
- package/src/player-perspective.ts +41 -0
- package/src/shared-exports.ts +23 -0
- package/test/answer-pipeline.test.mjs +375 -0
- package/test/owner/chess-reasoning-compact-search-preparation.test.mjs +111 -0
- package/test/owner/mechanism-encoding.test.mjs +78 -0
- package/test/owner/move-mechanism-facts.test.mjs +105 -0
- package/test/owner/pipeline-host-preparation.test.mjs +115 -0
- package/test/owner/position-preparation.test.mjs +22 -0
- package/test/owner/preparation-consolidation.test.mjs +74 -0
- package/test/owner/prose-reference-admission.test.mjs +26 -0
- package/test/owner/score-comparison-notices.test.mjs +116 -0
- package/test/owner/simple-engine-presentation.test.mjs +50 -0
- package/test/owner/tool-input-loop-guard.test.mjs +25 -0
- package/test-types.mts +23 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { presentMoveMechanisms } from './mechanism-presentation';
|
|
2
|
+
import {Chess, type Square} from 'chess.js';
|
|
3
|
+
import {reasoningContextHash,type ReasoningEvidenceRecord,type ReasoningPositionContext} from './poc-contracts';
|
|
4
|
+
const names:Record<string,string>={p:'pawn',n:'knight',b:'bishop',r:'rook',q:'queen',k:'king'};
|
|
5
|
+
const play=(b:Chess,u:string)=>b.move({from:u.slice(0,2),to:u.slice(2,4),promotion:u.slice(4)||undefined});
|
|
6
|
+
const obj=(v:unknown):Record<string,unknown>=>v&&typeof v==='object'&&!Array.isArray(v)?v as Record<string,unknown>:{};
|
|
7
|
+
export const EXPLANATION_FAMILIES=['accept-offer','central-break','kingside-play','queenside-play','exchange-defender','preserve-piece','direct-tactics','quiet-improvement'] as const;
|
|
8
|
+
export const EXPLANATION_REVIEW_SCHEMA:Record<string,unknown>={type:'object',additionalProperties:false,required:['mode','mechanism','challenges','evidenceIds','unresolved'],properties:{
|
|
9
|
+
mode:{type:'string',enum:['positional','tactical','direct','not-explanation']},
|
|
10
|
+
mechanism:{type:'string',maxLength:1200},
|
|
11
|
+
challenges:{type:'array',maxItems:6,items:{type:'string',maxLength:300}},
|
|
12
|
+
evidenceIds:{type:'array',maxItems:8,items:{type:'string',maxLength:300}},
|
|
13
|
+
unresolved:{type:'array',maxItems:6,items:{type:'string',maxLength:300},description:'Concrete unanswered causal questions material to this answer, not generic bounded-search or uniqueness limits.'},
|
|
14
|
+
limitations:{type:'array',maxItems:6,items:{type:'string',maxLength:300},description:'Scope limits to disclose without blocking completion, such as non-exhaustive ranking or no proof of permanent control.'},
|
|
15
|
+
shortLineReasons:{type:'array',maxItems:6,items:{type:'string',maxLength:300},description:'Deprecated compatibility field; omit. Continuation length is reported by the engine tool and never requires a review justification.'},
|
|
16
|
+
}};
|
|
17
|
+
export type ExplanationReview={mode:'positional'|'tactical'|'direct'|'not-explanation';mechanism:string;challenges:string[];evidenceIds:string[];unresolved:string[];limitations?:string[];shortLineReasons?:string[]};
|
|
18
|
+
export function validateExplanationReview(input:Record<string,unknown>,context:ReasoningPositionContext,known:Map<string,ReasoningEvidenceRecord>):ExplanationReview{
|
|
19
|
+
const fail=(code:string,field:string,expected:string,choices?:string[]):never=>{
|
|
20
|
+
throw Object.assign(new Error(`${field}: ${expected}`),{code,field,expected,...(choices?{choices:choices.slice(0,32)}:{})});
|
|
21
|
+
};
|
|
22
|
+
const modes=['positional','tactical','direct','not-explanation'];
|
|
23
|
+
if(typeof input.mode!=='string'||!modes.includes(input.mode))fail('invalid-explanation-review','mode','Choose one of the supported review modes.',modes);
|
|
24
|
+
if(typeof input.mechanism!=='string'||input.mechanism.length>1200)fail('invalid-explanation-review','mechanism','Use a string of at most 1200 characters.');
|
|
25
|
+
for(const [key,max]of [['challenges',6],['evidenceIds',8],['unresolved',6],['limitations',6]] as const){
|
|
26
|
+
if(key==='limitations'&&input[key]===undefined)continue;
|
|
27
|
+
const value=input[key];
|
|
28
|
+
if(!Array.isArray(value)||value.length>max)fail('invalid-explanation-'+key,key,`Use an array of at most ${max} strings.`);
|
|
29
|
+
(value as unknown[]).forEach((x,i)=>{if(typeof x!=='string'||x.length>300)fail('invalid-explanation-'+key,`${key}[${i}]`,'Use a string of at most 300 characters.');});
|
|
30
|
+
}
|
|
31
|
+
const legal=new Set(new Chess(context.fenAfter).moves({verbose:true}).map(m=>m.from+m.to+(m.promotion||'')));
|
|
32
|
+
for(const [i,entry] of (input.challenges as string[]).entries()){
|
|
33
|
+
const [family,uci,...rest]=entry.split('|');
|
|
34
|
+
if(rest.length||!family.trim()||!legal.has(uci))fail('invalid-explanation-challenge',`challenges[${i}]`,'Use a descriptive family label followed by | and a legal UCI reply from the actual after-position.',[...legal]);
|
|
35
|
+
}
|
|
36
|
+
const unknown=(input.evidenceIds as string[]).filter(id=>!known.has(id));
|
|
37
|
+
if(unknown.length)fail('unknown-explanation-evidence','evidenceIds',`Unknown evidence IDs: ${unknown.join(', ')}. Use acquired evidence IDs.`,[...known.keys()]);
|
|
38
|
+
const normalized:Record<string,unknown> = { ...input,
|
|
39
|
+
challenges:[...new Set(input.challenges as string[])],
|
|
40
|
+
evidenceIds:[...new Set(input.evidenceIds as string[])],
|
|
41
|
+
};
|
|
42
|
+
delete normalized.shortLineReasons;
|
|
43
|
+
return normalized as unknown as ExplanationReview;
|
|
44
|
+
}
|
|
45
|
+
// Retain the longest verified continuation per actual opponent root. Never count
|
|
46
|
+
// synthetic pass leads or separate before-position candidate alternatives.
|
|
47
|
+
export function explanationBranches(context:ReasoningPositionContext,records:Iterable<ReasoningEvidenceRecord>){
|
|
48
|
+
const roots=new Map<string,{root:string;id:string;pv:string[];san:string[];depth:number;score:unknown;prefixLength:number;prefixEvidenceId:string|null;pvCompletion:unknown;rawPv:unknown;continuationCovered:boolean;unmetHorizonRequests:{evidenceId:string;requested:number;prefixLength:number}[]}>();
|
|
49
|
+
const hash=reasoningContextHash(context),all=[...records];
|
|
50
|
+
// Root searches first; conditional endpoint searches may extend only an already
|
|
51
|
+
// acquired engine prefix, or start after exactly one selected opponent move.
|
|
52
|
+
for(const pass of [0,1])for(const e of all){if(e.kind!=='probe'||e.contextHash!==hash)continue;const d=obj(e.data);if(d.phase!=='after'||!Array.isArray(d.lines)||d.authority!=='bounded-engine'||!e.licenses?.some(x=>x==='engine.bounded-score'||x==='engine.bounded-comparison'))continue;
|
|
53
|
+
const prefix=Array.isArray(d.played)?d.played.map((x:{uci:string})=>x.uci):[];
|
|
54
|
+
if((prefix.length?1:0)!==pass)continue;
|
|
55
|
+
const prior=prefix.length?roots.get(prefix[0]):undefined;
|
|
56
|
+
const licensedPrefix=prior&&prefix.every((u:string,i:number)=>prior.pv[i]===u);
|
|
57
|
+
if(prefix.length>1&&!licensedPrefix)continue;
|
|
58
|
+
const expected=new Chess(context.fenAfter);try{for(const u of prefix)play(expected,u)}catch{continue}
|
|
59
|
+
if(e.replay?.fen!==expected.fen())continue;
|
|
60
|
+
for(const line of d.lines){if(!Array.isArray(line.pv)||!line.pv.length||line.rootMove!==line.pv[0])continue;
|
|
61
|
+
const pv=[...prefix,...line.pv];const board=new Chess(context.fenAfter);let san:string[];
|
|
62
|
+
try{san=pv.map(u=>play(board,u).san)}catch{continue}
|
|
63
|
+
const continuationCovered=(!e.coverage?.unresolved.includes(line.rootMove)) && (line.pv.length>=Number(d.minimumPvPlies||obj(d.searchScope).minimumPvPlies||1)||board.isCheckmate()||board.isStalemate());
|
|
64
|
+
const old=roots.get(pv[0]);const unmetHorizonRequests=[...(old?.unmetHorizonRequests||[])];
|
|
65
|
+
if(!continuationCovered&&!unmetHorizonRequests.some(r=>r.evidenceId===e.id))unmetHorizonRequests.push({evidenceId:e.id,requested:Number(d.minimumPvPlies||obj(d.searchScope).minimumPvPlies||1),prefixLength:prefix.length});
|
|
66
|
+
if(!old||pv.length>=old.pv.length)roots.set(pv[0],{root:pv[0],id:e.id,pv,san,depth:line.depth,continuationCovered,unmetHorizonRequests,prefixLength:prefix.length,prefixEvidenceId:licensedPrefix?prior!.id:null,pvCompletion:line.pvCompletion??null,rawPv:line.rawPv??null,score:{scoreCp:line.scoreCp,matePly:line.matePly,scoreScope:line.scoreScope,scope:d.searchScope,evaluatedFromPly:prefix.length}});else old.unmetHorizonRequests=unmetHorizonRequests;
|
|
67
|
+
}}return [...roots.values()];
|
|
68
|
+
}
|
|
69
|
+
export function legalInvestigationRoots(context:ReasoningPositionContext){
|
|
70
|
+
return Object.fromEntries(['before','after'].map(phase=>[phase,new Chess(phase==='before'?context.fenBefore:context.fenAfter).moves({verbose:true}).map(m=>({uci:m.from+m.to+(m.promotion||''),san:m.san}))]));
|
|
71
|
+
}
|
|
72
|
+
function structureAfterCaptures(context:ReasoningPositionContext,pv:string[]){
|
|
73
|
+
const b=new Chess(context.fenAfter),events:unknown[]=[];
|
|
74
|
+
pv.forEach((u,i)=>{const m=play(b,u);if(!m.captured||events.length>=4)return;
|
|
75
|
+
const pieces=b.board().flat().filter(p=>p!==null);
|
|
76
|
+
const bishops=pieces.filter(p=>p!.type==='b').map(p=>({color:p!.color,square:p!.square,squareColor:b.squareColor(p!.square)}));
|
|
77
|
+
events.push({ply:i+1,move:m.san,...(m.piece==='p'?{pawnTransformation:{color:m.color,from:m.from,to:m.to}}:{}),capturedPiece:names[m.captured],remainingBishops:bishops,openFiles:'abcdefgh'.split('').filter(f=>!pieces.some(p=>p!.type==='p'&&p!.square[0]===f)),scope:'exact position after this conditional capture; open file is not proof of productive rook access'});
|
|
78
|
+
});return events;
|
|
79
|
+
}
|
|
80
|
+
export function explanationSummary(context:ReasoningPositionContext,records:Iterable<ReasoningEvidenceRecord>){
|
|
81
|
+
const branches=explanationBranches(context,records);const owner=new Chess(context.fenBefore).turn();
|
|
82
|
+
const motifs=new Map<string,{piece:string;start:string;destination:string;roots:string[];witnesses:unknown[]}>();
|
|
83
|
+
for(const branch of branches){const board=new Chess(context.fenAfter),ids=new Map<string,string>();
|
|
84
|
+
for(const row of board.board())for(const p of row)if(p)ids.set(p.square,p.square);
|
|
85
|
+
const paths=new Map<string,string[]>();
|
|
86
|
+
branch.pv.forEach((u,index)=>{const from=u.slice(0,2),to=u.slice(2,4),id=ids.get(from)||from;const m=play(board,u);ids.delete(from);ids.set(to,id);if(m.flags.includes('e'))ids.delete(to[0]+from[1]);
|
|
87
|
+
if(m.flags.includes('k')||m.flags.includes('q')){const rfrom=(m.flags.includes('k')?'h':'a')+from[1],rto=(m.flags.includes('k')?'f':'d')+from[1];const rid=ids.get(rfrom);ids.delete(rfrom);if(rid)ids.set(rto,rid)}
|
|
88
|
+
const path=paths.get(id)||[from];path.push(to);paths.set(id,path);
|
|
89
|
+
if(m.color!==owner||m.piece==='p'||m.piece==='k')return;
|
|
90
|
+
const key=id+':'+to;let motif=motifs.get(key);if(!motif){motif={piece:names[m.piece],start:id,destination:to,roots:[],witnesses:[]};motifs.set(key,motif)}
|
|
91
|
+
if(motif.roots.includes(branch.root))return;motif.roots.push(branch.root);
|
|
92
|
+
const pawns=board.board().flat().filter(p=>p?.type==='p');
|
|
93
|
+
const attackers=(color:string)=>pawns.filter(p=>p?.color===color&&Math.abs(p.square.charCodeAt(0)-to.charCodeAt(0))===1&&Number(to[1])-Number(p.square[1])===(color==='w'?1:-1)).map(p=>p!.square);
|
|
94
|
+
const enemy=owner==='w'?'b':'w';const front=to[0]+String(Number(to[1])+(enemy==='w'?-1:1));const blocked=board.get(front as Square);
|
|
95
|
+
motif.witnesses.push({root:branch.root,evidenceId:branch.id,ply:index+1,route:path.join('-'),pawnDefenders:attackers(owner),opponentPawnAttackers:attackers(enemy),...(blocked?.type==='p'&&blocked.color===enemy?{opponentPawnDirectlyBehind:front}:{}),scope:'geometry at arrival only; exchanges and future pawn changes remain possible'});
|
|
96
|
+
});}
|
|
97
|
+
return {authority:'heuristic summary of legally replayed bounded engine lines; recurrence is not causality or force',branches:branches.map(b=>({root:b.root,evidenceId:b.id,plies:b.pv.length,depth:b.depth,continuationCoverage:b.continuationCovered?'selected-record-horizon-covered':'short-or-unresolved',unmetHorizonRequests:b.unmetHorizonRequests,prefixLength:b.prefixLength,prefixEvidenceId:b.prefixEvidenceId,pvCompletion:b.pvCompletion,rawPv:b.rawPv,san:b.san.join(' '),score:b.score,structuralEvents:structureAfterCaptures(context,b.pv)})),observedRoutes:[...motifs.values()].filter(m=>m.roots.length>=2).sort((a,b)=>b.roots.length-a.roots.length).slice(0,24),distinctOpponentRoots:branches.length};
|
|
98
|
+
}
|
|
99
|
+
export function explanationCoverage(context:ReasoningPositionContext,records:Iterable<ReasoningEvidenceRecord>,review:ExplanationReview|null){
|
|
100
|
+
const branches=explanationBranches(context,records),searched=new Set(branches.map(b=>b.root));const unresolved:string[]=[];
|
|
101
|
+
if(!review)return {status:'unreviewed',unresolved:[],investigated:[],modelJudgment:null,limitation:'Review is optional; absence of a review does not imply evidence is sufficient or insufficient.'};
|
|
102
|
+
const investigated=review.challenges.filter(c=>searched.has(c.split('|')[1]));
|
|
103
|
+
if(review.mode!=='not-explanation'){
|
|
104
|
+
if(!review.mechanism.trim())unresolved.push('causal-mechanism');
|
|
105
|
+
if(!review.evidenceIds.length)unresolved.push('mechanism-support');
|
|
106
|
+
unresolved.push(...review.unresolved);
|
|
107
|
+
if(review.mode==='positional'&&!new Chess(context.fenAfter).isGameOver()){
|
|
108
|
+
for(const c of review.challenges){if(!investigated.includes(c))unresolved.push('unsearched:'+c);}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return {status:unresolved.length?'unresolved':'reviewed-for-current-answer',unresolved:[...new Set(unresolved)],investigated,modelJudgment:review,limitation:'Model-selected families and causal interpretation are not independently proven; root acquisition is checked. Coverage is advisory, not a proof of explanation correctness or a completion gate.'};
|
|
112
|
+
}
|
|
113
|
+
/** Remove only expensive per-ply geometry from planning copies; original evidence stays in known. */
|
|
114
|
+
export function planningEvidence(result:{contextHash:string;evidence:ReasoningEvidenceRecord[];limitations:readonly string[]}){
|
|
115
|
+
return {...result,evidence:result.evidence.map(e=>{const d=obj(e.data);if(!Array.isArray(d.lines))return e;const {continuationMoveFacts,rootMoveFacts,...data}=d;void rootMoveFacts;
|
|
116
|
+
const lines=d.lines.map((line:Record<string,unknown>)=>{if(!Array.isArray(line.pv))return line;const board=new Chess(String(e.replay!.fen));let san:string[]=[];try{san=line.pv.map((u:string)=>play(board,u).san)}catch{return line}const continuation=obj(Array.isArray(continuationMoveFacts)?continuationMoveFacts.find((f:unknown)=>obj(f).rootMove===line.rootMove):undefined);const mechanismFacts=continuation&&Array.isArray(continuation.moves)?presentMoveMechanisms(continuation.moves):[];return {...line,san:san.join(' '),...(mechanismFacts.length?{mechanismFacts}:{})}});
|
|
117
|
+
return {...e,data:{...data,lines,planningProjection:{omitted:['continuationMoveFacts','rootMoveFacts'],retrieveFullEvidenceId:e.id,limitation:'First-two-ply mechanism measurements retained; deeper/full exact geometry remains retrievable; no strategic concept proven by this projection.'}}} as ReasoningEvidenceRecord;})};
|
|
118
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ReasoningEvidenceRecord } from "./poc-contracts";
|
|
2
|
+
import { projectCompactEngineEvidence } from "./simple-compact-evidence";
|
|
3
|
+
|
|
4
|
+
const object = (v: unknown): v is Record<string, unknown> => !!v && typeof v === "object" && !Array.isArray(v);
|
|
5
|
+
export const FACT_DELIVERY_INSTRUCTIONS = `{$priorFact:[toolCallId,evidenceId,variation,ply]} reuses an identical tactical fact from an earlier tool result in this CURRENT_QUESTION only. Locate the native result for toolCallId, then that evidence record's continuationMoveFacts[variation].moves[ply-1]; variation=-1 instead means rootMoveFacts[ply-1]. Expand its same-message encoding/metadata. Position, ordered prefix and the complete fact were checked for equality; this does NOT reuse scores or search provenance. Every current comparison retains its own ranking, settings and coverage. No references point to previous chat exchanges.`;
|
|
6
|
+
|
|
7
|
+
/** Request-local reuse, valid only while the loop's earlier tool messages remain. */
|
|
8
|
+
export class SimpleFactDelivery {
|
|
9
|
+
private delivered = new Map<string, { pointer: unknown; size: number }>();
|
|
10
|
+
private pending = new Map<string, { pointer: unknown; size: number }>();
|
|
11
|
+
/** Called only after complete native response messages have entered context. */
|
|
12
|
+
commit() { for (const [key, value] of this.pending) if (!this.delivered.has(key)) this.delivered.set(key, value); this.pending.clear(); }
|
|
13
|
+
constructor(private readonly contextHash: string) {}
|
|
14
|
+
|
|
15
|
+
project(callId: string, result: { contextHash: string; evidence: readonly ReasoningEvidenceRecord[]; limitations: readonly string[] }) {
|
|
16
|
+
if (result.contextHash !== this.contextHash) throw Error("cross-context-fact-delivery");
|
|
17
|
+
if (JSON.stringify(result).includes('"$priorFact"')) throw Error("reserved-fact-reference");
|
|
18
|
+
const evidence = result.evidence.map(record => {
|
|
19
|
+
if (record.contextHash !== this.contextHash) throw Error("cross-context-fact-delivery");
|
|
20
|
+
const data = record.data;
|
|
21
|
+
const search = object(data.searchScope) ? data.searchScope : null;
|
|
22
|
+
const fen = search?.fen ?? record.replay?.fen;
|
|
23
|
+
if (typeof fen !== "string" || !["before", "after"].includes(String(data.phase))) return record;
|
|
24
|
+
const fact = (value: unknown, prefix: string[], variation: number, ply: number) => {
|
|
25
|
+
if (!object(value) || typeof value.uci !== "string") return value;
|
|
26
|
+
const key = JSON.stringify({ contextHash: this.contextHash, fen, phase: data.phase, prefix, fact: value });
|
|
27
|
+
const previous = this.delivered.get(key);
|
|
28
|
+
const encoded = projectCompactEngineEvidence(value);
|
|
29
|
+
const size = JSON.stringify(object(encoded) && encoded.encoding ? encoded.content : encoded).length;
|
|
30
|
+
// Retain the first literal occurrence, so no pointer chain is introduced.
|
|
31
|
+
if (previous && previous.size < size / 2) return previous.pointer;
|
|
32
|
+
if (!previous) {
|
|
33
|
+
const pointer = { $priorFact: [callId, record.id, variation, ply] };
|
|
34
|
+
if (!this.pending.has(key)) this.pending.set(key, { pointer, size: JSON.stringify(pointer).length });
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
};
|
|
38
|
+
const continuationMoveFacts = Array.isArray(data.continuationMoveFacts) ? data.continuationMoveFacts.map((line, variation) => {
|
|
39
|
+
if (!object(line) || !Array.isArray(line.moves)) return line;
|
|
40
|
+
const prefix: string[] = [];
|
|
41
|
+
return { ...line, moves: line.moves.map((move, index) => {
|
|
42
|
+
const projected = fact(move, [...prefix], variation, index + 1);
|
|
43
|
+
if (object(move) && typeof move.uci === "string") prefix.push(move.uci);
|
|
44
|
+
return projected;
|
|
45
|
+
}) };
|
|
46
|
+
}) : undefined;
|
|
47
|
+
const rootMoveFacts = Array.isArray(data.rootMoveFacts) ? data.rootMoveFacts.map((move, i) => fact(move, [], -1, i + 1)) : undefined;
|
|
48
|
+
return { ...record, data: { ...data, ...(continuationMoveFacts ? { continuationMoveFacts } : {}), ...(rootMoveFacts ? { rootMoveFacts } : {}) } };
|
|
49
|
+
});
|
|
50
|
+
return { ...result, evidence };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Chess } from 'chess.js';
|
|
2
|
+
import type { ReasoningEvidenceRecord, ReasoningPositionContext } from './poc-contracts';
|
|
3
|
+
|
|
4
|
+
const object = (value: unknown): Record<string, unknown> | null =>
|
|
5
|
+
value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : null;
|
|
6
|
+
const uci = /^[a-h][1-8][a-h][1-8][qrbn]?$/;
|
|
7
|
+
|
|
8
|
+
/** A deliberately narrow correction for an asserted forced win. The finite
|
|
9
|
+
* search only limits what this fallback can support; it is no proof that a
|
|
10
|
+
* different search could never find a forced win. */
|
|
11
|
+
export function forcedWinPremiseFallback(question: string, context: ReasoningPositionContext | undefined,
|
|
12
|
+
records: readonly ReasoningEvidenceRecord[], selectedContextAvailable = true): string | null {
|
|
13
|
+
if (!selectedContextAvailable || !context || context.kind === 'position') return null;
|
|
14
|
+
const q = question.trim();
|
|
15
|
+
if (!/\b(?:wins? by force|forced win|guaranteed win|forces? (?:a )?win)\b/i.test(q)
|
|
16
|
+
|| /\b(?:does(?:n't| not)|is(?:n't| not)|cannot|can't)\b.{0,50}\b(?:win by force|forced win)\b/i.test(q)
|
|
17
|
+
|| /\b(?:not|never) win by force\b/i.test(q)) return null;
|
|
18
|
+
const san = context.moveSan.replace(/[+#?!]+$/g, '');
|
|
19
|
+
if (!san || !new RegExp(`(^|[^a-z0-9])${san.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![a-z0-9])`, 'i').test(q)) return null;
|
|
20
|
+
let after: Chess;
|
|
21
|
+
try {
|
|
22
|
+
const before = new Chess(context.fenBefore);
|
|
23
|
+
if (!uci.test(context.moveUci)) return null;
|
|
24
|
+
const played = before.move({from:context.moveUci.slice(0,2),to:context.moveUci.slice(2,4),promotion:context.moveUci.slice(4)||undefined});
|
|
25
|
+
if (!played || played.san.replace(/[+#?!]+$/g, '') !== san || before.fen() !== context.fenAfter) return null;
|
|
26
|
+
after = new Chess(context.fenAfter);
|
|
27
|
+
} catch { return null; }
|
|
28
|
+
const opponent = after.turn() === 'w' ? 'White' : 'Black';
|
|
29
|
+
const fen = after.fen();
|
|
30
|
+
const candidates: Array<{san:string; scoreCp:number; depth:number}> = [];
|
|
31
|
+
for (const record of records) {
|
|
32
|
+
if (record.kind !== 'probe' || !record.source.endsWith('/compare_moves') || record.coverage?.complete !== true) continue;
|
|
33
|
+
const wrapper = object(record.data);
|
|
34
|
+
const data = object(wrapper?.details) ?? wrapper;
|
|
35
|
+
const search = object(data?.searchScope);
|
|
36
|
+
const roots = Array.isArray(search?.requestedRootMoves) ? search.requestedRootMoves : [];
|
|
37
|
+
if (search?.fen !== fen || roots.length < 2 || !roots.every(root => typeof root === 'string' && record.coverage?.covered.includes(root))) continue;
|
|
38
|
+
const perspective = opponent.toLowerCase();
|
|
39
|
+
for (const value of Array.isArray(data?.lines) ? data.lines : []) {
|
|
40
|
+
const line = object(value), scope = object(line?.scoreScope), provenance = object(line?.scoreProvenance);
|
|
41
|
+
if (!line || !roots.includes(line.rootMove) || !record.coverage.covered.includes(String(line.rootMove))
|
|
42
|
+
|| scope?.fen !== fen || scope.perspective !== perspective || scope.positiveMeans !== 'advantage for perspective color'
|
|
43
|
+
|| scope.scoreCpUnit !== 'centipawns' || line.matePly !== null
|
|
44
|
+
|| typeof line.scoreCp !== 'number' || !Number.isFinite(line.scoreCp) || line.scoreCp < 100 || line.scoreCp >= 100000
|
|
45
|
+
|| !Number.isInteger(line.depth) || (line.depth as number) < 8 || !Array.isArray(line.pv) || line.pv[0] !== line.rootMove) continue;
|
|
46
|
+
if (provenance ? provenance.kind !== 'root-search' || provenance.fen !== fen || provenance.originatingDepth !== line.depth
|
|
47
|
+
: search?.sourceCacheProvenance !== 'engine') continue;
|
|
48
|
+
try {
|
|
49
|
+
const board = new Chess(fen);
|
|
50
|
+
let reply = '';
|
|
51
|
+
for (const [i, move] of line.pv.entries()) {
|
|
52
|
+
if (typeof move !== 'string' || !uci.test(move)) throw Error('invalid continuation');
|
|
53
|
+
const played = board.move({from:move.slice(0,2),to:move.slice(2,4),promotion:move.slice(4)||undefined});
|
|
54
|
+
if (!played) throw Error('illegal continuation');
|
|
55
|
+
if (i === 0) reply = played.san;
|
|
56
|
+
}
|
|
57
|
+
candidates.push({san:reply,scoreCp:line.scoreCp,depth:line.depth as number});
|
|
58
|
+
} catch { /* A malformed PV cannot support a prose correction. */ }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!candidates.length) return null;
|
|
62
|
+
candidates.sort((a,b) => b.scoreCp-a.scoreCp || b.depth-a.depth);
|
|
63
|
+
const best = candidates[0];
|
|
64
|
+
return `The available analysis does not support the premise that ${san} wins by force. At the selected position after ${san}, ${opponent} can answer ${best.san}; the recorded depth-${best.depth} comparison scores that continuation +${(best.scoreCp/100).toFixed(2)} from ${opponent}'s perspective. This is a bounded engine result, not proof that no forced win exists; a mating line after a different reply does not make that reply compulsory.`;
|
|
65
|
+
}
|