2026-06-27 00:18:41 -04:00
import fs from "fs/promises" ;
import path from "path" ;
import { fileURLToPath } from "url" ;
const _ _filename = fileURLToPath ( import . meta . url ) ;
const _ _dirname = path . dirname ( _ _filename ) ;
const repoRoot = path . resolve ( _ _dirname , ".." ) ;
const kbRoot = path . join ( repoRoot , "docs" , "kb" ) ;
2026-06-27 01:12:35 -04:00
const requestTagCatalogPath = path . join ( kbRoot , "tags.json" ) ;
2026-06-27 01:44:11 -04:00
const terminologyCatalogPath = path . join ( kbRoot , "terminology.json" ) ;
const moduleIndexPath = path . join ( kbRoot , "modules.json" ) ;
const editorCapabilitiesPath = path . join ( kbRoot , "editor-capabilities.md" ) ;
const queueWorkflowPath = path . join ( kbRoot , "queue-workflow.md" ) ;
2026-06-27 00:18:41 -04:00
const DEFAULT _DEEPSEEK _BASE _URL = "https://api.deepseek.com" ;
const DEFAULT _DEEPSEEK _MODEL = "deepseek-v4-flash" ;
const DEFAULT _PROVIDER = process . env . REQUEST _ANALYZER _PROVIDER
|| ( String ( process . env . DEEPSEEK _API _KEY || "" ) . trim ( ) ? "deepseek" : "openai-compatible" ) ;
const DEFAULTS = {
apiBase : process . env . REQUEST _ANALYZER _API _BASE || ` http://127.0.0.1: ${ process . env . PORT || "5180" } ` ,
provider : DEFAULT _PROVIDER ,
modelBaseUrl : process . env . REQUEST _ANALYZER _MODEL _BASE _URL || "" ,
model : process . env . REQUEST _ANALYZER _MODEL || "" ,
apiKey : process . env . REQUEST _ANALYZER _API _KEY || process . env . DEEPSEEK _API _KEY || "" ,
2026-06-27 00:51:20 -04:00
adminPassword : process . env . REQUEST _ANALYZER _ADMIN _PASSWORD || process . env . LAUNCHER _ADMIN _PASSWORD || "" ,
2026-06-27 00:18:41 -04:00
limit : Number ( process . env . REQUEST _ANALYZER _LIMIT || 5 ) ,
promoteThreshold : Number ( process . env . REQUEST _ANALYZER _PROMOTE _THRESHOLD || 0.85 ) ,
maxTokens : Math . max ( 512 , Number ( process . env . REQUEST _ANALYZER _MAX _TOKENS || 4000 ) ) ,
thinking : String ( process . env . REQUEST _ANALYZER _THINKING || "disabled" ) . trim ( ) . toLowerCase ( ) === "enabled"
? "enabled"
: "disabled" ,
poll : false ,
intervalMs : Number ( process . env . REQUEST _ANALYZER _INTERVAL _MS || 30000 ) ,
dryRun : false ,
requestId : "" ,
} ;
function printHelp ( ) {
console . log ( ` Worldshaper request analysis worker
Usage :
npm run analyze : requests -- [ options ]
Options :
-- provider < name > Model provider : deepseek or openai - compatible .
-- request - id < id > Analyze one pending request only .
-- limit < n > Maximum pending requests to process in one pass .
-- poll Keep polling for new pending requests .
-- interval - ms < ms > Poll interval when -- poll is enabled .
-- dry - run Do not write anything back to the API .
-- api - base < url > Worldshaper API base URL .
-- model - base - url < url > Model API base URL . Defaults to DeepSeek when provider = deepseek .
-- model < id > Model id to use . Defaults to $ { DEFAULT _DEEPSEEK _MODEL } for DeepSeek .
-- api - key < key > API key for the model endpoint .
-- max - tokens < n > Max output tokens for the model response .
-- thinking < mode > DeepSeek thinking mode : enabled or disabled .
-- promote - threshold < n > Minimum per - item confidence for auto - promotion .
-- help Show this message .
Environment variables :
REQUEST _ANALYZER _API _BASE
REQUEST _ANALYZER _PROVIDER
REQUEST _ANALYZER _MODEL _BASE _URL
REQUEST _ANALYZER _MODEL
REQUEST _ANALYZER _API _KEY
2026-06-27 00:51:20 -04:00
REQUEST _ANALYZER _ADMIN _PASSWORD
2026-06-27 00:18:41 -04:00
REQUEST _ANALYZER _MAX _TOKENS
REQUEST _ANALYZER _THINKING
REQUEST _ANALYZER _LIMIT
REQUEST _ANALYZER _INTERVAL _MS
REQUEST _ANALYZER _PROMOTE _THRESHOLD
DEEPSEEK _API _KEY
2026-06-27 00:51:20 -04:00
LAUNCHER _ADMIN _PASSWORD
2026-06-27 00:18:41 -04:00
Notes :
- DeepSeek uses $ { DEFAULT _DEEPSEEK _BASE _URL } / chat / completions .
- DeepSeek JSON mode is enabled automatically for this worker .
- Run the Worldshaper API server first so the worker can read and patch requests .
` );
}
function parseArgs ( argv ) {
const config = { ... DEFAULTS } ;
for ( let index = 0 ; index < argv . length ; index += 1 ) {
const arg = String ( argv [ index ] || "" ) . trim ( ) ;
if ( ! arg ) {
continue ;
}
if ( arg === "--help" || arg === "-h" ) {
config . help = true ;
continue ;
}
if ( arg === "--provider" ) {
config . provider = String ( argv [ index + 1 ] || "" ) . trim ( ) . toLowerCase ( ) || config . provider ;
index += 1 ;
continue ;
}
if ( arg === "--poll" ) {
config . poll = true ;
continue ;
}
if ( arg === "--dry-run" ) {
config . dryRun = true ;
continue ;
}
if ( arg === "--request-id" ) {
config . requestId = String ( argv [ index + 1 ] || "" ) . trim ( ) ;
index += 1 ;
continue ;
}
if ( arg === "--limit" ) {
config . limit = Math . max ( 1 , Math . floor ( Number ( argv [ index + 1 ] ) || config . limit ) ) ;
index += 1 ;
continue ;
}
if ( arg === "--interval-ms" ) {
config . intervalMs = Math . max ( 1000 , Math . floor ( Number ( argv [ index + 1 ] ) || config . intervalMs ) ) ;
index += 1 ;
continue ;
}
if ( arg === "--api-base" ) {
config . apiBase = String ( argv [ index + 1 ] || "" ) . trim ( ) || config . apiBase ;
index += 1 ;
continue ;
}
if ( arg === "--model-base-url" ) {
config . modelBaseUrl = String ( argv [ index + 1 ] || "" ) . trim ( ) || config . modelBaseUrl ;
index += 1 ;
continue ;
}
if ( arg === "--model" ) {
config . model = String ( argv [ index + 1 ] || "" ) . trim ( ) || config . model ;
index += 1 ;
continue ;
}
if ( arg === "--api-key" ) {
config . apiKey = String ( argv [ index + 1 ] || "" ) . trim ( ) || config . apiKey ;
index += 1 ;
continue ;
}
if ( arg === "--max-tokens" ) {
config . maxTokens = Math . max ( 512 , Math . floor ( Number ( argv [ index + 1 ] ) || config . maxTokens ) ) ;
index += 1 ;
continue ;
}
if ( arg === "--thinking" ) {
const thinkingValue = String ( argv [ index + 1 ] || "" ) . trim ( ) . toLowerCase ( ) ;
config . thinking = thinkingValue === "enabled" ? "enabled" : "disabled" ;
index += 1 ;
continue ;
}
if ( arg === "--promote-threshold" ) {
const parsed = Number ( argv [ index + 1 ] ) ;
if ( Number . isFinite ( parsed ) ) {
config . promoteThreshold = Math . max ( 0 , Math . min ( 1 , parsed ) ) ;
}
index += 1 ;
continue ;
}
throw new Error ( ` Unknown option: ${ arg } ` ) ;
}
return config ;
}
function finalizeConfig ( config ) {
const provider = String ( config . provider || "" ) . trim ( ) . toLowerCase ( ) ;
const nextConfig = {
... config ,
provider : provider === "deepseek" ? "deepseek" : "openai-compatible" ,
} ;
if ( ! String ( nextConfig . modelBaseUrl || "" ) . trim ( ) ) {
nextConfig . modelBaseUrl = nextConfig . provider === "deepseek"
? DEFAULT _DEEPSEEK _BASE _URL
: "http://127.0.0.1:1234/v1" ;
}
if ( ! String ( nextConfig . model || "" ) . trim ( ) && nextConfig . provider === "deepseek" ) {
nextConfig . model = DEFAULT _DEEPSEEK _MODEL ;
}
return nextConfig ;
}
function sleep ( ms ) {
return new Promise ( ( resolve ) => {
setTimeout ( resolve , ms ) ;
} ) ;
}
function withTrailingSlash ( value ) {
return String ( value || "" ) . endsWith ( "/" ) ? String ( value || "" ) : ` ${ String ( value || "" ) } / ` ;
}
function buildUrl ( base , pathname ) {
return new URL ( String ( pathname || "" ) . replace ( /^\// , "" ) , withTrailingSlash ( base ) ) . toString ( ) ;
}
async function fetchJson ( url , init = { } ) {
const response = await fetch ( url , init ) ;
if ( ! response . ok ) {
const responseText = await response . text ( ) . catch ( ( ) => "" ) ;
throw new Error ( ` ${ response . status } ${ response . statusText } ${ responseText ? ` : ${ responseText . slice ( 0 , 240 ) } ` : "" } ` ) ;
}
return response . json ( ) ;
}
2026-06-27 00:51:20 -04:00
function buildAdminHeaders ( config , baseHeaders = { } ) {
const nextHeaders = {
... baseHeaders ,
} ;
const adminPassword = String ( config ? . adminPassword || "" ) . trim ( ) ;
if ( adminPassword ) {
nextHeaders [ "x-worldshaper-admin-password" ] = adminPassword ;
}
return nextHeaders ;
}
2026-06-27 00:18:41 -04:00
function tokenize ( value ) {
return String ( value || "" )
. toLowerCase ( )
. split ( /[^a-z0-9_/-]+/i )
. map ( ( entry ) => entry . trim ( ) )
. filter ( ( entry ) => entry . length >= 2 ) ;
}
function uniqueStrings ( values ) {
const seen = new Set ( ) ;
const next = [ ] ;
for ( const value of Array . isArray ( values ) ? values : [ ] ) {
const normalized = String ( value || "" ) . replace ( /\s+/g , " " ) . trim ( ) ;
const key = normalized . toLowerCase ( ) ;
if ( ! normalized || seen . has ( key ) ) {
continue ;
}
seen . add ( key ) ;
next . push ( normalized ) ;
}
return next ;
}
2026-06-27 01:12:35 -04:00
function normalizeRequestTagLookupValue ( value ) {
return String ( value || "" ) . replace ( /\s+/g , " " ) . trim ( ) . toLowerCase ( ) ;
}
async function loadRequestTagCatalog ( ) {
const payload = JSON . parse ( await fs . readFile ( requestTagCatalogPath , "utf8" ) ) ;
const tags = Array . isArray ( payload ? . tags )
? payload . tags
. map ( ( entry ) => ( {
id : String ( entry ? . id || "" ) . trim ( ) ,
label : String ( entry ? . label || "" ) . trim ( ) ,
aliases : Array . isArray ( entry ? . aliases ) ? entry . aliases . map ( ( alias ) => String ( alias || "" ) . trim ( ) ) . filter ( Boolean ) : [ ] ,
} ) )
. filter ( ( entry ) => entry . id && entry . label )
: [ ] ;
if ( tags . length === 0 ) {
throw new Error ( "Request tag catalog did not contain any valid tags." ) ;
}
return tags ;
}
2026-06-27 01:44:11 -04:00
async function loadTerminologyCatalog ( ) {
const payload = JSON . parse ( await fs . readFile ( terminologyCatalogPath , "utf8" ) ) ;
const terms = Array . isArray ( payload ? . terms )
? payload . terms
. map ( ( entry ) => ( {
canonical : String ( entry ? . canonical || "" ) . trim ( ) ,
aliases : Array . isArray ( entry ? . aliases ) ? entry . aliases . map ( ( alias ) => String ( alias || "" ) . trim ( ) ) . filter ( Boolean ) : [ ] ,
tags : Array . isArray ( entry ? . tags ) ? entry . tags . map ( ( tag ) => String ( tag || "" ) . trim ( ) ) . filter ( Boolean ) : [ ] ,
systemIds : Array . isArray ( entry ? . systemIds ) ? entry . systemIds . map ( ( systemId ) => String ( systemId || "" ) . trim ( ) ) . filter ( Boolean ) : [ ] ,
} ) )
. filter ( ( entry ) => entry . canonical )
: [ ] ;
return {
schemaVersion : Number ( payload ? . schemaVersion ) || 1 ,
terms ,
} ;
}
async function loadModuleIndex ( ) {
const payload = JSON . parse ( await fs . readFile ( moduleIndexPath , "utf8" ) ) ;
const modules = Array . isArray ( payload ? . modules )
? payload . modules
. map ( ( entry ) => ( {
id : String ( entry ? . id || "" ) . trim ( ) ,
name : String ( entry ? . name || "" ) . trim ( ) ,
docPath : String ( entry ? . docPath || "" ) . trim ( ) ,
tags : Array . isArray ( entry ? . tags ) ? entry . tags . map ( ( tag ) => String ( tag || "" ) . trim ( ) ) . filter ( Boolean ) : [ ] ,
systemIds : Array . isArray ( entry ? . systemIds ) ? entry . systemIds . map ( ( systemId ) => String ( systemId || "" ) . trim ( ) ) . filter ( Boolean ) : [ ] ,
aliases : Array . isArray ( entry ? . aliases ) ? entry . aliases . map ( ( alias ) => String ( alias || "" ) . trim ( ) ) . filter ( Boolean ) : [ ] ,
priority : String ( entry ? . priority || "medium" ) . trim ( ) . toLowerCase ( ) ,
} ) )
. filter ( ( entry ) => entry . id && entry . name && entry . docPath )
: [ ] ;
return {
schemaVersion : Number ( payload ? . schemaVersion ) || 1 ,
modules ,
} ;
}
2026-06-27 01:12:35 -04:00
function buildRequestTagLookup ( tagDefinitions ) {
return new Map (
( Array . isArray ( tagDefinitions ) ? tagDefinitions : [ ] ) . flatMap ( ( entry ) => [
[ normalizeRequestTagLookupValue ( entry . label ) , entry . label ] ,
... ( Array . isArray ( entry . aliases ) ? entry . aliases . map ( ( alias ) => [ normalizeRequestTagLookupValue ( alias ) , entry . label ] ) : [ ] ) ,
] ) ,
) ;
}
function normalizeRequestTags ( values , tagLookup , fallback = [ ] ) {
const normalized = uniqueStrings ( Array . isArray ( values ) ? values : [ ] )
. map ( ( entry ) => tagLookup . get ( normalizeRequestTagLookupValue ( entry ) ) || "" )
. filter ( Boolean ) ;
if ( normalized . length > 0 ) {
return uniqueStrings ( normalized ) ;
}
return uniqueStrings ( Array . isArray ( fallback ) ? fallback : [ ] )
. map ( ( entry ) => tagLookup . get ( normalizeRequestTagLookupValue ( entry ) ) || "" )
. filter ( Boolean ) ;
}
2026-06-27 00:18:41 -04:00
function clampConfidence ( value ) {
const parsed = Number ( value ) ;
if ( ! Number . isFinite ( parsed ) ) {
return null ;
}
return Math . max ( 0 , Math . min ( 1 , parsed ) ) ;
}
function normalizeItemStatusRecommendation ( value ) {
const normalized = String ( value || "" ) . trim ( ) . toLowerCase ( ) ;
if ( normalized === "active" || normalized === "duplicate" || normalized === "blocked" || normalized === "needs_review" ) {
return normalized ;
}
return "needs_review" ;
}
function normalizeProblemType ( value ) {
const normalized = String ( value || "" ) . trim ( ) . toLowerCase ( ) ;
if ( normalized === "feature" || normalized === "bug" || normalized === "workflow" || normalized === "performance" || normalized === "ux" || normalized === "content" ) {
return normalized ;
}
return "unknown" ;
}
function buildFallbackTitle ( text , fallback = "Pending request" ) {
const normalized = String ( text || "" ) . replace ( /\s+/g , " " ) . trim ( ) ;
if ( ! normalized ) {
return fallback ;
}
const firstSentence = normalized . split ( /[\r\n.!?]+/ ) . map ( ( entry ) => entry . trim ( ) ) . find ( Boolean ) || normalized ;
return firstSentence . length > 72 ? ` ${ firstSentence . slice ( 0 , 69 ) . trim ( ) } ... ` : firstSentence ;
}
2026-06-27 01:12:35 -04:00
function normalizeAnalysisItem ( rawItem , index = 0 , request = null , relevantSystems = [ ] , kb = null ) {
2026-06-27 00:18:41 -04:00
const source = rawItem && typeof rawItem === "object" && ! Array . isArray ( rawItem )
? rawItem
: null ;
if ( ! source ) {
return null ;
}
const fallbackText = String ( source . rawExcerpt || request ? . sourceText || "" ) . trim ( ) ;
const title = String ( source . title || "" ) . trim ( ) || buildFallbackTitle ( fallbackText , ` Analyzed request ${ index + 1 } ` ) ;
const parsedInterpretation = String ( source . parsedInterpretation || source . summary || "" ) . trim ( ) ;
const implementationApproach = String ( source . implementationApproach || source . implementationNotes || "" ) . trim ( ) ;
if ( ! title || ! parsedInterpretation || ! implementationApproach ) {
return null ;
}
const primaryCategory = String ( source . primaryCategory || source . category || "" ) . trim ( )
|| String ( relevantSystems [ 0 ] ? . name || "Unsorted" ) ;
const affectedSystems = uniqueStrings ( source . affectedSystems ) ;
2026-06-27 01:12:35 -04:00
const defaultTags = [
2026-06-27 00:18:41 -04:00
primaryCategory ,
... affectedSystems ,
2026-06-27 01:12:35 -04:00
] ;
const tags = normalizeRequestTags (
Array . isArray ( source . tags ) ? source . tags : defaultTags ,
kb ? . requestTagLookup || new Map ( ) ,
defaultTags ,
) ;
const reviewRationale = String ( source . reviewRationale || source . reviewReason || source . notes || "" ) . trim ( ) ;
const reviewOptions = uniqueStrings ( source . reviewOptions ) ;
2026-06-27 00:18:41 -04:00
return {
title ,
primaryCategory ,
2026-06-27 01:12:35 -04:00
tags : tags . length > 0 ? tags : [ "General" ] ,
2026-06-27 00:18:41 -04:00
statusRecommendation : normalizeItemStatusRecommendation ( source . statusRecommendation || source . status ) ,
parsedInterpretation ,
implementationApproach ,
affectedSystems ,
affectedFiles : uniqueStrings ( source . affectedFiles ) ,
problemType : normalizeProblemType ( source . problemType ) ,
rawExcerpt : String ( source . rawExcerpt || "" ) . trim ( ) ,
confidence : clampConfidence ( source . confidence ) ,
2026-06-27 01:12:35 -04:00
reviewRationale ,
reviewOptions ,
2026-06-27 00:18:41 -04:00
notes : String ( source . notes || "" ) . trim ( ) ,
} ;
}
2026-06-27 01:12:35 -04:00
function normalizeAnalysisResult ( rawResult , request , relevantSystems , kb = null ) {
2026-06-27 00:18:41 -04:00
const source = rawResult && typeof rawResult === "object" && ! Array . isArray ( rawResult )
? rawResult
: ( Array . isArray ( rawResult ) ? { items : rawResult } : null ) ;
if ( ! source ) {
throw new Error ( "Model response was not a JSON object." ) ;
}
const items = ( Array . isArray ( source . items ) ? source . items : [ ] )
2026-06-27 01:12:35 -04:00
. map ( ( item , index ) => normalizeAnalysisItem ( item , index , request , relevantSystems , kb ) )
2026-06-27 00:18:41 -04:00
. filter ( Boolean ) ;
if ( items . length === 0 ) {
throw new Error ( "Model response did not contain any valid request items." ) ;
}
const finiteConfidences = items . map ( ( item ) => item . confidence ) . filter ( ( value ) => Number . isFinite ( value ) ) ;
const averageConfidence = finiteConfidences . length > 0
? finiteConfidences . reduce ( ( total , value ) => total + value , 0 ) / finiteConfidences . length
: null ;
const minimumConfidence = finiteConfidences . length > 0
? Math . min ( ... finiteConfidences )
: null ;
return {
submissionId : String ( source . submissionId || request . id || "" ) . trim ( ) || request . id ,
sourceText : String ( source . sourceText || request . sourceText || "" ) . trim ( ) || request . sourceText ,
confidence : clampConfidence ( source . confidence ) ? ? averageConfidence ,
minimumConfidence ,
items ,
} ;
}
async function loadKnowledgeBase ( ) {
const systemsIndexPath = path . join ( kbRoot , "systems.json" ) ;
const requestSchemaPath = path . join ( kbRoot , "request-analysis-schema.json" ) ;
const systemsIndex = JSON . parse ( await fs . readFile ( systemsIndexPath , "utf8" ) ) ;
const requestSchema = JSON . parse ( await fs . readFile ( requestSchemaPath , "utf8" ) ) ;
2026-06-27 01:12:35 -04:00
const requestTagDefinitions = await loadRequestTagCatalog ( ) ;
2026-06-27 01:44:11 -04:00
const terminology = await loadTerminologyCatalog ( ) ;
const moduleIndex = await loadModuleIndex ( ) ;
2026-06-27 00:18:41 -04:00
const docsById = new Map ( ) ;
2026-06-27 01:44:11 -04:00
const moduleDocsById = new Map ( ) ;
2026-06-27 00:18:41 -04:00
for ( const system of Array . isArray ( systemsIndex . systems ) ? systemsIndex . systems : [ ] ) {
const docPath = path . join ( repoRoot , String ( system . docPath || "" ) . replace ( /\//g , path . sep ) ) ;
const docText = await fs . readFile ( docPath , "utf8" ) . catch ( ( ) => "" ) ;
docsById . set ( String ( system . id || "" ) , docText ) ;
}
2026-06-27 01:44:11 -04:00
for ( const moduleEntry of Array . isArray ( moduleIndex . modules ) ? moduleIndex . modules : [ ] ) {
const docPath = path . join ( repoRoot , String ( moduleEntry . docPath || "" ) . replace ( /\//g , path . sep ) ) ;
const docText = await fs . readFile ( docPath , "utf8" ) . catch ( ( ) => "" ) ;
moduleDocsById . set ( String ( moduleEntry . id || "" ) , docText ) ;
}
2026-06-27 00:18:41 -04:00
return {
systemsIndex ,
requestSchema ,
2026-06-27 01:12:35 -04:00
requestTagDefinitions ,
requestTagLookup : buildRequestTagLookup ( requestTagDefinitions ) ,
2026-06-27 01:44:11 -04:00
terminology ,
moduleIndex ,
2026-06-27 00:18:41 -04:00
docsById ,
2026-06-27 01:44:11 -04:00
moduleDocsById ,
editorCapabilitiesText : await fs . readFile ( editorCapabilitiesPath , "utf8" ) . catch ( ( ) => "" ) ,
queueWorkflowText : await fs . readFile ( queueWorkflowPath , "utf8" ) . catch ( ( ) => "" ) ,
2026-06-27 00:18:41 -04:00
} ;
}
2026-06-27 01:44:11 -04:00
function normalizeAmbiguityLevel ( value ) {
const normalized = String ( value || "" ) . trim ( ) . toLowerCase ( ) ;
if ( normalized === "low" || normalized === "medium" || normalized === "high" ) {
return normalized ;
}
return "medium" ;
}
function normalizeSystemIds ( values , kb , fallback = [ ] ) {
const knownIds = new Set ( ( Array . isArray ( kb ? . systemsIndex ? . systems ) ? kb . systemsIndex . systems : [ ] ) . map ( ( system ) => String ( system . id || "" ) . trim ( ) ) ) ;
const requested = uniqueStrings ( Array . isArray ( values ) ? values : [ ] )
. map ( ( entry ) => String ( entry || "" ) . trim ( ) )
. filter ( ( entry ) => knownIds . has ( entry ) ) ;
if ( requested . length > 0 ) {
return requested ;
}
return uniqueStrings ( Array . isArray ( fallback ) ? fallback : [ ] )
. map ( ( entry ) => String ( entry || "" ) . trim ( ) )
. filter ( ( entry ) => knownIds . has ( entry ) ) ;
}
function normalizeModuleIds ( values , kb , fallback = [ ] ) {
const knownIds = new Set ( ( Array . isArray ( kb ? . moduleIndex ? . modules ) ? kb . moduleIndex . modules : [ ] ) . map ( ( moduleEntry ) => String ( moduleEntry . id || "" ) . trim ( ) ) ) ;
const requested = uniqueStrings ( Array . isArray ( values ) ? values : [ ] )
. map ( ( entry ) => String ( entry || "" ) . trim ( ) )
. filter ( ( entry ) => knownIds . has ( entry ) ) ;
if ( requested . length > 0 ) {
return requested ;
}
return uniqueStrings ( Array . isArray ( fallback ) ? fallback : [ ] )
. map ( ( entry ) => String ( entry || "" ) . trim ( ) )
. filter ( ( entry ) => knownIds . has ( entry ) ) ;
}
2026-06-27 00:18:41 -04:00
function buildSystemSearchText ( system , docText ) {
const parts = [
system ? . id ,
system ? . name ,
... ( Array . isArray ( system ? . aliases ) ? system . aliases : [ ] ) ,
... ( Array . isArray ( system ? . tags ) ? system . tags : [ ] ) ,
... ( Array . isArray ( system ? . uiSurfaces ) ? system . uiSurfaces : [ ] ) ,
... ( Array . isArray ( system ? . keyFiles ) ? system . keyFiles : [ ] ) ,
... ( Array . isArray ( system ? . apiEndpoints ) ? system . apiEndpoints : [ ] ) ,
docText ,
] ;
return parts . join ( " " ) . toLowerCase ( ) ;
}
2026-06-27 01:44:11 -04:00
function buildModuleSearchText ( moduleEntry , docText ) {
const parts = [
moduleEntry ? . id ,
moduleEntry ? . name ,
... ( Array . isArray ( moduleEntry ? . aliases ) ? moduleEntry . aliases : [ ] ) ,
... ( Array . isArray ( moduleEntry ? . tags ) ? moduleEntry . tags : [ ] ) ,
... ( Array . isArray ( moduleEntry ? . systemIds ) ? moduleEntry . systemIds : [ ] ) ,
docText ,
] ;
return parts . join ( " " ) . toLowerCase ( ) ;
}
function collectTerminologyMatches ( kb , requestText ) {
const requestLower = String ( requestText || "" ) . toLowerCase ( ) ;
return ( Array . isArray ( kb ? . terminology ? . terms ) ? kb . terminology . terms : [ ] )
. map ( ( term ) => {
const phrases = [ term . canonical , ... ( Array . isArray ( term . aliases ) ? term . aliases : [ ] ) ]
. map ( ( entry ) => String ( entry || "" ) . trim ( ) . toLowerCase ( ) )
. filter ( Boolean ) ;
const matched = phrases . filter ( ( phrase ) => requestLower . includes ( phrase ) ) ;
return matched . length > 0
? {
term ,
matched ,
}
: null ;
} )
. filter ( Boolean ) ;
}
2026-06-27 00:18:41 -04:00
function pickRelevantSystems ( kb , requestText , limit = 4 ) {
const systems = Array . isArray ( kb ? . systemsIndex ? . systems ) ? kb . systemsIndex . systems : [ ] ;
const queryTokens = tokenize ( requestText ) ;
const requestLower = String ( requestText || "" ) . toLowerCase ( ) ;
2026-06-27 01:44:11 -04:00
const terminologyMatches = collectTerminologyMatches ( kb , requestText ) ;
const terminologyBoostBySystemId = new Map ( ) ;
terminologyMatches . forEach ( ( { term , matched } ) => {
( Array . isArray ( term . systemIds ) ? term . systemIds : [ ] ) . forEach ( ( systemId ) => {
terminologyBoostBySystemId . set ( systemId , ( terminologyBoostBySystemId . get ( systemId ) || 0 ) + ( matched . length * 4 ) ) ;
} ) ;
} ) ;
2026-06-27 00:18:41 -04:00
const ranked = systems
. map ( ( system ) => {
const docText = kb . docsById . get ( String ( system . id || "" ) ) || "" ;
const corpus = buildSystemSearchText ( system , docText ) ;
2026-06-27 01:44:11 -04:00
let score = terminologyBoostBySystemId . get ( String ( system . id || "" ) . trim ( ) ) || 0 ;
2026-06-27 00:18:41 -04:00
for ( const token of queryTokens ) {
if ( corpus . includes ( token ) ) {
score += 2 ;
}
}
for ( const alias of Array . isArray ( system . aliases ) ? system . aliases : [ ] ) {
const normalizedAlias = String ( alias || "" ) . trim ( ) . toLowerCase ( ) ;
if ( normalizedAlias && requestLower . includes ( normalizedAlias ) ) {
score += 5 ;
}
}
if ( String ( system . priorityForRequestTriage || "" ) . trim ( ) . toLowerCase ( ) === "high" ) {
score += 1 ;
}
return {
system ,
docText ,
score ,
} ;
} )
. sort ( ( left , right ) => right . score - left . score || String ( left . system ? . name || "" ) . localeCompare ( String ( right . system ? . name || "" ) ) ) ;
const positive = ranked . filter ( ( entry ) => entry . score > 0 ) . slice ( 0 , limit ) ;
if ( positive . length > 0 ) {
return positive ;
}
return ranked . slice ( 0 , limit ) ;
}
2026-06-27 01:44:11 -04:00
function pickRelevantModules ( kb , requestText , routing , limit = 4 ) {
const modules = Array . isArray ( kb ? . moduleIndex ? . modules ) ? kb . moduleIndex . modules : [ ] ;
const queryTokens = tokenize ( requestText ) ;
const requestLower = String ( requestText || "" ) . toLowerCase ( ) ;
const suggestedTags = new Set ( Array . isArray ( routing ? . suggestedTags ) ? routing . suggestedTags : [ ] ) ;
const suggestedSystems = new Set ( Array . isArray ( routing ? . suggestedSystems ) ? routing . suggestedSystems : [ ] ) ;
const ranked = modules
. map ( ( moduleEntry ) => {
const docText = kb . moduleDocsById . get ( String ( moduleEntry . id || "" ) ) || "" ;
const corpus = buildModuleSearchText ( moduleEntry , docText ) ;
let score = 0 ;
for ( const token of queryTokens ) {
if ( corpus . includes ( token ) ) {
score += 2 ;
}
}
for ( const alias of Array . isArray ( moduleEntry . aliases ) ? moduleEntry . aliases : [ ] ) {
const normalizedAlias = String ( alias || "" ) . trim ( ) . toLowerCase ( ) ;
if ( normalizedAlias && requestLower . includes ( normalizedAlias ) ) {
score += 5 ;
}
}
for ( const tag of Array . isArray ( moduleEntry . tags ) ? moduleEntry . tags : [ ] ) {
if ( suggestedTags . has ( tag ) ) {
score += 4 ;
}
}
for ( const systemId of Array . isArray ( moduleEntry . systemIds ) ? moduleEntry . systemIds : [ ] ) {
if ( suggestedSystems . has ( systemId ) ) {
score += 4 ;
}
}
if ( String ( moduleEntry . priority || "medium" ) === "high" ) {
score += 1 ;
}
return {
moduleEntry ,
docText ,
score ,
} ;
} )
. sort ( ( left , right ) => right . score - left . score || String ( left . moduleEntry ? . name || "" ) . localeCompare ( String ( right . moduleEntry ? . name || "" ) ) ) ;
const positive = ranked . filter ( ( entry ) => entry . score > 0 ) . slice ( 0 , limit ) ;
if ( positive . length > 0 ) {
return positive ;
}
return ranked . slice ( 0 , limit ) ;
}
function deriveHeuristicRouting ( kb , requestText ) {
const terminologyMatches = collectTerminologyMatches ( kb , requestText ) ;
const relevantSystems = pickRelevantSystems ( kb , requestText , 4 ) ;
const matchedTerms = uniqueStrings (
terminologyMatches . flatMap ( ( { matched } ) => matched ) ,
) ;
const suggestedTags = uniqueStrings ( [
... terminologyMatches . flatMap ( ( { term } ) => Array . isArray ( term . tags ) ? term . tags : [ ] ) ,
... relevantSystems . flatMap ( ( { system } ) => Array . isArray ( system . tags ) ? system . tags : [ ] ) ,
] ) ;
const suggestedSystems = uniqueStrings ( [
... terminologyMatches . flatMap ( ( { term } ) => Array . isArray ( term . systemIds ) ? term . systemIds : [ ] ) ,
... relevantSystems . map ( ( { system } ) => String ( system . id || "" ) . trim ( ) ) ,
] ) ;
const suggestedModules = pickRelevantModules ( kb , requestText , {
suggestedTags ,
suggestedSystems ,
} , 4 ) . map ( ( { moduleEntry } ) => String ( moduleEntry . id || "" ) . trim ( ) ) ;
const normalizedText = String ( requestText || "" ) . replace ( /\s+/g , " " ) . trim ( ) ;
const tokenCount = tokenize ( normalizedText ) . length ;
const genericPatterns = [
/\b(make|improve|fix|upgrade)\b.+\b(game|editor|worldshaper|site|launcher|it)\b/i ,
/^\s*(better|more|faster|cooler|good)\s*!?\s*$/i ,
/^\s*add\s+[a-z0-9_-]+\s*!?\s*$/i ,
] ;
const isBroad = genericPatterns . some ( ( pattern ) => pattern . test ( normalizedText ) ) || tokenCount <= 4 ;
const ambiguity = isBroad
? "high"
: ( matchedTerms . length === 0 || suggestedSystems . length > 3 ? "medium" : "low" ) ;
const displaySystemNames = suggestedSystems
. map ( ( systemId ) => ( Array . isArray ( kb ? . systemsIndex ? . systems ) ? kb . systemsIndex . systems : [ ] ) . find ( ( system ) => String ( system . id || "" ) . trim ( ) === systemId ) ? . name || systemId )
. slice ( 0 , 4 ) ;
const possibleDirections = uniqueStrings ( [
suggestedTags . includes ( "Graphics Painter" ) ? "Treat it as an asset-editing request inside the Graphics Painter." : "" ,
suggestedTags . includes ( "Tiling" ) ? "Treat it as map-grid painting or placement behavior on the tile canvas." : "" ,
suggestedTags . includes ( "Content" ) ? "Treat it as a new asset or content-record request that needs catalog support." : "" ,
suggestedTags . includes ( "Rendering" ) || suggestedTags . includes ( "Worlds" ) ? "Treat it as a runtime-facing request that may need renderer or world metadata support." : "" ,
suggestedTags . includes ( "Chunks" ) ? "Treat it as a chunk or world-scale placement workflow." : "" ,
] ) . slice ( 0 , 4 ) ;
return {
summary : displaySystemNames . length > 0
? ` Likely touches ${ displaySystemNames . join ( ", " ) } . `
: "Broad request with no single obvious subsystem match yet." ,
ambiguity ,
matchedTerms ,
suggestedTags : suggestedTags . length > 0 ? suggestedTags . slice ( 0 , 6 ) : [ "General" ] ,
suggestedSystems : suggestedSystems . slice ( 0 , 4 ) ,
suggestedModules ,
rationale : matchedTerms . length > 0
? ` Matched terminology: ${ matchedTerms . join ( ", " ) } . `
: "Used broad alias and system matching because the submission did not name an exact editor surface." ,
possibleDirections : possibleDirections . length > 0
? possibleDirections
: [ "Clarify which editor surface or runtime behavior the request should target first." ] ,
} ;
}
function normalizeRoutingResult ( rawResult , kb , requestText , fallbackRouting ) {
const source = rawResult && typeof rawResult === "object" && ! Array . isArray ( rawResult )
? rawResult
: { } ;
const suggestedTags = normalizeRequestTags (
Array . isArray ( source . suggestedTags ) ? source . suggestedTags : source . tags ,
kb ? . requestTagLookup || new Map ( ) ,
fallbackRouting ? . suggestedTags || [ "General" ] ,
) ;
const suggestedSystems = normalizeSystemIds (
Array . isArray ( source . suggestedSystems ) ? source . suggestedSystems : source . suggestedSystemIds ,
kb ,
fallbackRouting ? . suggestedSystems || [ ] ,
) ;
const suggestedModules = normalizeModuleIds (
Array . isArray ( source . suggestedModules ) ? source . suggestedModules : source . suggestedModuleIds ,
kb ,
fallbackRouting ? . suggestedModules || [ ] ,
) ;
const matchedTerms = uniqueStrings ( source . matchedTerms || source . terms || fallbackRouting ? . matchedTerms || [ ] ) ;
const possibleDirections = uniqueStrings ( source . possibleDirections || fallbackRouting ? . possibleDirections || [ ] ) ;
const summary = String ( source . summary || source . routingSummary || fallbackRouting ? . summary || "" ) . trim ( )
|| ` Likely touches ${ suggestedTags . join ( ", " ) } . ` ;
const rationale = String ( source . rationale || source . reviewRationale || fallbackRouting ? . rationale || "" ) . trim ( )
|| "Routing used terminology and KB alias matching." ;
const ambiguity = normalizeAmbiguityLevel ( source . ambiguity || fallbackRouting ? . ambiguity || "" ) ;
const kbSections = uniqueStrings ( [
... suggestedSystems . map ( ( systemId ) => {
const system = ( Array . isArray ( kb ? . systemsIndex ? . systems ) ? kb . systemsIndex . systems : [ ] ) . find ( ( entry ) => String ( entry . id || "" ) . trim ( ) === systemId ) ;
return String ( system ? . docPath || "" ) . trim ( ) ;
} ) ,
... suggestedModules . map ( ( moduleId ) => {
const moduleEntry = ( Array . isArray ( kb ? . moduleIndex ? . modules ) ? kb . moduleIndex . modules : [ ] ) . find ( ( entry ) => String ( entry . id || "" ) . trim ( ) === moduleId ) ;
return String ( moduleEntry ? . docPath || "" ) . trim ( ) ;
} ) ,
] . filter ( Boolean ) ) ;
return {
summary ,
ambiguity ,
matchedTerms ,
suggestedTags : suggestedTags . length > 0 ? suggestedTags : [ "General" ] ,
suggestedSystems ,
suggestedModules ,
rationale ,
possibleDirections : possibleDirections . length > 0
? possibleDirections
: ( fallbackRouting ? . possibleDirections || [ "Clarify which part of the editor or runtime should own this request." ] ) ,
kbSections ,
sourceText : String ( requestText || "" ) . trim ( ) ,
} ;
}
function buildRoutingPrompt ( request , kb , heuristicRouting ) {
const systemSummaries = ( Array . isArray ( kb ? . systemsIndex ? . systems ) ? kb . systemsIndex . systems : [ ] )
. map ( ( system ) => ` - ${ system . id } : ${ system . name } | tags= ${ ( Array . isArray ( system . tags ) ? system . tags : [ ] ) . join ( ", " ) } | aliases= ${ ( Array . isArray ( system . aliases ) ? system . aliases : [ ] ) . join ( ", " ) } ` )
. join ( "\n" ) ;
const moduleSummaries = ( Array . isArray ( kb ? . moduleIndex ? . modules ) ? kb . moduleIndex . modules : [ ] )
. map ( ( moduleEntry ) => ` - ${ moduleEntry . id } : ${ moduleEntry . name } | tags= ${ ( Array . isArray ( moduleEntry . tags ) ? moduleEntry . tags : [ ] ) . join ( ", " ) } | systems= ${ ( Array . isArray ( moduleEntry . systemIds ) ? moduleEntry . systemIds : [ ] ) . join ( ", " ) } ` )
. join ( "\n" ) ;
const terminologySummary = ( Array . isArray ( kb ? . terminology ? . terms ) ? kb . terminology . terms : [ ] )
. map ( ( term ) => ` - ${ term . canonical } : aliases= ${ ( Array . isArray ( term . aliases ) ? term . aliases : [ ] ) . join ( ", " ) } | tags= ${ ( Array . isArray ( term . tags ) ? term . tags : [ ] ) . join ( ", " ) } | systems= ${ ( Array . isArray ( term . systemIds ) ? term . systemIds : [ ] ) . join ( ", " ) } ` )
. join ( "\n" ) ;
return [
{
role : "system" ,
content : [
"You are the routing pass for Worldshaper request analysis." ,
"Map the submission onto likely systems, modules, and standardized tags before the deeper analysis pass runs." ,
"Prefer Worldshaper terminology when the user uses adjacent language such as sprite editor, painting tool, recoloring, engine, runtime, map, grid, or chunk." ,
"Broad requests must still receive a useful interpretation and possible directions." ,
"Do not expose or simulate hidden chain-of-thought." ,
"Return only valid JSON with keys: summary, ambiguity, matchedTerms, suggestedTags, suggestedSystems, suggestedModules, rationale, possibleDirections." ,
] . join ( "\n" ) ,
} ,
{
role : "user" ,
content : [
` Submission id: ${ request . id } ` ,
"Raw submission:" ,
request . sourceText ,
"" ,
"Standardized tags:" ,
( Array . isArray ( kb ? . requestTagDefinitions ) ? kb . requestTagDefinitions : [ ] ) . map ( ( entry ) => ` - ${ entry . label } : ${ entry . description || "" } ` ) . join ( "\n" ) ,
"" ,
"System index:" ,
systemSummaries ,
"" ,
"Focused modules:" ,
moduleSummaries ,
"" ,
"Terminology map:" ,
terminologySummary ,
"" ,
"Heuristic seed:" ,
JSON . stringify ( heuristicRouting , null , 2 ) ,
] . join ( "\n" ) ,
} ,
] ;
}
function buildKbContext ( kb , requestText , routing ) {
const relevantSystems = uniqueStrings ( [
... normalizeSystemIds ( routing ? . suggestedSystems , kb , [ ] ) ,
... pickRelevantSystems ( kb , requestText , 4 ) . map ( ( { system } ) => String ( system . id || "" ) . trim ( ) ) ,
] )
. slice ( 0 , 4 )
. map ( ( systemId ) => {
const system = ( Array . isArray ( kb ? . systemsIndex ? . systems ) ? kb . systemsIndex . systems : [ ] ) . find ( ( entry ) => String ( entry . id || "" ) . trim ( ) === systemId ) ;
return system
? {
system ,
docText : kb . docsById . get ( systemId ) || "" ,
}
: null ;
} )
. filter ( Boolean ) ;
const relevantModules = uniqueStrings ( [
... normalizeModuleIds ( routing ? . suggestedModules , kb , [ ] ) ,
... pickRelevantModules ( kb , requestText , routing , 5 ) . map ( ( { moduleEntry } ) => String ( moduleEntry . id || "" ) . trim ( ) ) ,
] )
. slice ( 0 , 5 )
. map ( ( moduleId ) => {
const moduleEntry = ( Array . isArray ( kb ? . moduleIndex ? . modules ) ? kb . moduleIndex . modules : [ ] ) . find ( ( entry ) => String ( entry . id || "" ) . trim ( ) === moduleId ) ;
return moduleEntry
? {
moduleEntry ,
docText : kb . moduleDocsById . get ( moduleId ) || "" ,
}
: null ;
} )
. filter ( Boolean ) ;
return {
systems : relevantSystems ,
modules : relevantModules ,
editorCapabilitiesText : String ( kb ? . editorCapabilitiesText || "" ) . trim ( ) ,
} ;
}
function buildAnalysisPrompt ( request , routing , kbContext , schema , kb ) {
2026-06-27 00:18:41 -04:00
const schemaSummary = JSON . stringify ( schema , null , 2 ) ;
2026-06-27 01:12:35 -04:00
const tagCatalogSummary = Array . isArray ( kb ? . requestTagDefinitions )
? kb . requestTagDefinitions . map ( ( entry ) => ` - ${ entry . label } : ${ entry . description || "" } ` . trim ( ) ) . join ( "\n" )
: "" ;
2026-06-27 01:44:11 -04:00
const systemDocs = kbContext . systems . map ( ( { system , docText } ) => [
` System: ${ system . name } ` ,
` System ID: ${ system . id } ` ,
` Tags: ${ ( Array . isArray ( system . tags ) ? system . tags : [ ] ) . join ( ", " ) } ` ,
` Key files: ${ ( Array . isArray ( system . keyFiles ) ? system . keyFiles : [ ] ) . join ( ", " ) } ` ,
` API endpoints: ${ ( Array . isArray ( system . apiEndpoints ) ? system . apiEndpoints : [ ] ) . join ( ", " ) || "(none)" } ` ,
docText ,
] . join ( "\n" ) ) . join ( "\n\n---\n\n" ) ;
const moduleDocs = kbContext . modules . map ( ( { moduleEntry , docText } ) => [
` Module: ${ moduleEntry . name } ` ,
` Module ID: ${ moduleEntry . id } ` ,
` Tags: ${ ( Array . isArray ( moduleEntry . tags ) ? moduleEntry . tags : [ ] ) . join ( ", " ) } ` ,
` System IDs: ${ ( Array . isArray ( moduleEntry . systemIds ) ? moduleEntry . systemIds : [ ] ) . join ( ", " ) } ` ,
docText ,
] . join ( "\n" ) ) . join ( "\n\n---\n\n" ) ;
2026-06-27 00:18:41 -04:00
return [
{
role : "system" ,
content : [
"You are processing Worldshaper editor requests." ,
"Split a submission into one or more atomic requests." ,
2026-06-27 01:44:11 -04:00
"Ground your decisions in the provided KB systems and modules only." ,
2026-06-27 01:12:35 -04:00
"Use only the standardized tags listed in the provided tag catalog." ,
"Do not expose or simulate hidden chain-of-thought. Provide short structured review rationale instead." ,
2026-06-27 01:44:11 -04:00
"If a request is broad or ambiguous, still provide the most useful likely interpretation and concrete implementation path you can." ,
2026-06-27 00:18:41 -04:00
"Return only valid JSON." ,
"Do not wrap the JSON in markdown fences." ,
"If you are unsure, lower confidence and use statusRecommendation = \"needs_review\"." ,
] . join ( "\n" ) ,
} ,
{
role : "user" ,
content : [
` Submission id: ${ request . id } ` ,
"Raw submission:" ,
request . sourceText ,
"" ,
2026-06-27 01:44:11 -04:00
"Routing summary:" ,
JSON . stringify ( routing , null , 2 ) ,
"" ,
2026-06-27 00:18:41 -04:00
"Return JSON matching this schema:" ,
schemaSummary ,
"" ,
2026-06-27 01:12:35 -04:00
"Standardized tags you may use:" ,
tagCatalogSummary ,
"" ,
2026-06-27 01:44:11 -04:00
"Capability shorthand:" ,
kbContext . editorCapabilitiesText || "(none)" ,
"" ,
2026-06-27 00:18:41 -04:00
"Relevant KB systems:" ,
2026-06-27 01:44:11 -04:00
systemDocs || "(none)" ,
"" ,
"Relevant KB modules:" ,
moduleDocs || "(none)" ,
2026-06-27 00:18:41 -04:00
] . join ( "\n" ) ,
} ,
] ;
}
function extractJsonString ( text ) {
const rawText = String ( text || "" ) . trim ( ) ;
if ( ! rawText ) {
throw new Error ( "Model returned empty content." ) ;
}
const fencedMatch = rawText . match ( /```(?:json)?\s*([\s\S]*?)```/i ) ;
if ( fencedMatch && fencedMatch [ 1 ] ) {
return fencedMatch [ 1 ] . trim ( ) ;
}
const firstBrace = rawText . indexOf ( "{" ) ;
const lastBrace = rawText . lastIndexOf ( "}" ) ;
if ( firstBrace >= 0 && lastBrace > firstBrace ) {
return rawText . slice ( firstBrace , lastBrace + 1 ) ;
}
throw new Error ( "Could not find JSON object in model response." ) ;
}
function readMessageContent ( messageContent ) {
if ( typeof messageContent === "string" ) {
return messageContent ;
}
if ( Array . isArray ( messageContent ) ) {
return messageContent
. map ( ( part ) => {
if ( typeof part === "string" ) {
return part ;
}
if ( part && typeof part === "object" && typeof part . text === "string" ) {
return part . text ;
}
return "" ;
} )
. filter ( Boolean )
. join ( "\n" ) ;
}
return "" ;
}
2026-06-27 01:44:11 -04:00
async function callModelApi ( config , messages , options = { } ) {
2026-06-27 00:18:41 -04:00
const headers = {
"Content-Type" : "application/json" ,
} ;
if ( config . apiKey ) {
headers . Authorization = ` Bearer ${ config . apiKey } ` ;
}
const requestBody = {
model : config . model ,
2026-06-27 01:44:11 -04:00
temperature : Number . isFinite ( Number ( options . temperature ) ) ? Number ( options . temperature ) : 0.2 ,
max _tokens : Math . max ( 256 , Math . floor ( Number ( options . maxTokens ) || config . maxTokens ) ) ,
2026-06-27 00:18:41 -04:00
messages ,
} ;
if ( config . provider === "deepseek" ) {
requestBody . response _format = { type : "json_object" } ;
requestBody . thinking = { type : config . thinking } ;
}
const payload = await fetchJson ( buildUrl ( config . modelBaseUrl , "chat/completions" ) , {
method : "POST" ,
headers ,
body : JSON . stringify ( requestBody ) ,
} ) ;
const text = readMessageContent ( payload ? . choices ? . [ 0 ] ? . message ? . content ) ;
return JSON . parse ( extractJsonString ( text ) ) ;
}
async function getLauncherRequests ( config ) {
return fetchJson ( buildUrl ( config . apiBase , "/api/launcher-requests" ) ) ;
}
async function patchLauncherRequest ( config , requestId , body ) {
return fetchJson ( buildUrl ( config . apiBase , ` /api/launcher-requests/ ${ encodeURIComponent ( requestId ) } ` ) , {
method : "PATCH" ,
2026-06-27 00:51:20 -04:00
headers : buildAdminHeaders ( config , {
2026-06-27 00:18:41 -04:00
"Content-Type" : "application/json" ,
2026-06-27 00:51:20 -04:00
} ) ,
2026-06-27 00:18:41 -04:00
body : JSON . stringify ( body ) ,
} ) ;
}
async function processLauncherRequestAnalysis ( config , requestId , body ) {
return fetchJson ( buildUrl ( config . apiBase , ` /api/launcher-requests/ ${ encodeURIComponent ( requestId ) } /process-analysis ` ) , {
method : "POST" ,
2026-06-27 00:51:20 -04:00
headers : buildAdminHeaders ( config , {
2026-06-27 00:18:41 -04:00
"Content-Type" : "application/json" ,
2026-06-27 00:51:20 -04:00
} ) ,
2026-06-27 00:18:41 -04:00
body : JSON . stringify ( body ) ,
} ) ;
}
function shouldPromoteAnalysis ( result , config ) {
2026-06-27 01:44:11 -04:00
return result . items . length > 0
&& String ( result ? . routing ? . ambiguity || "" ) . trim ( ) . toLowerCase ( ) !== "high"
&& result . items . every ( ( item ) => (
2026-06-27 00:18:41 -04:00
item . statusRecommendation === "active"
&& Number . isFinite ( item . confidence )
&& item . confidence >= config . promoteThreshold
2026-06-27 01:44:11 -04:00
) ) ;
2026-06-27 00:18:41 -04:00
}
async function markRequestProcessing ( config , request ) {
const now = new Date ( ) . toISOString ( ) ;
return patchLauncherRequest ( config , request . id , {
analysis : {
... ( request . analysis && typeof request . analysis === "object" && ! Array . isArray ( request . analysis ) ? request . analysis : { } ) ,
state : "processing" ,
model : config . model ,
submissionId : request . id ,
sourceTextSnapshot : request . sourceText ,
createdAt : request . analysis ? . createdAt || now ,
updatedAt : now ,
error : "" ,
} ,
} ) ;
}
async function markRequestError ( config , request , errorMessage ) {
return processLauncherRequestAnalysis ( config , request . id , {
action : "error" ,
model : config . model ,
error : String ( errorMessage || "Unknown analysis failure." ) ,
analysis : {
submissionId : request . id ,
sourceTextSnapshot : request . sourceText ,
} ,
} ) ;
}
async function markRequestReview ( config , request , result ) {
return processLauncherRequestAnalysis ( config , request . id , {
action : "review" ,
model : config . model ,
analysis : {
submissionId : request . id ,
sourceTextSnapshot : request . sourceText ,
confidence : result . confidence ,
2026-06-27 01:44:11 -04:00
routing : result . routing ,
2026-06-27 00:18:41 -04:00
items : result . items ,
} ,
} ) ;
}
async function promoteRequest ( config , request , result ) {
return processLauncherRequestAnalysis ( config , request . id , {
action : "promote" ,
model : config . model ,
analysis : {
submissionId : request . id ,
sourceTextSnapshot : request . sourceText ,
confidence : result . confidence ,
2026-06-27 01:44:11 -04:00
routing : result . routing ,
2026-06-27 00:18:41 -04:00
items : result . items ,
} ,
} ) ;
}
2026-06-27 01:44:11 -04:00
function buildFallbackAnalysisResult ( request , routing , kb ) {
const primaryTag = Array . isArray ( routing ? . suggestedTags ) && routing . suggestedTags . length > 0
? routing . suggestedTags [ 0 ]
: "General" ;
const primarySystemId = Array . isArray ( routing ? . suggestedSystems ) && routing . suggestedSystems . length > 0
? routing . suggestedSystems [ 0 ]
: "" ;
const primarySystem = ( Array . isArray ( kb ? . systemsIndex ? . systems ) ? kb . systemsIndex . systems : [ ] )
. find ( ( system ) => String ( system . id || "" ) . trim ( ) === primarySystemId ) ;
const title = buildFallbackTitle (
request . sourceText ,
routing ? . ambiguity === "high" ? "Broader editor improvement request" : "Pending request" ,
) ;
const parsedInterpretation = routing ? . ambiguity === "high"
? ` This submission is broad, but it most likely points toward ${ primaryTag } work ${ primarySystem ? . name ? ` touching ${ primarySystem . name } ` : "" } . It needs a narrower decision before it can be promoted cleanly. `
: ` This request most likely targets ${ primarySystem ? . name || primaryTag } , based on the submission language and the matched KB terminology. ` ;
const implementationApproach = routing ? . ambiguity === "high"
? ` Turn the request into a smaller scoped follow-up by choosing one direction first: ${ ( Array . isArray ( routing ? . possibleDirections ) && routing . possibleDirections . length > 0 ? routing . possibleDirections : [ "clarify whether this should be content, map editing, or runtime work" ] ) . join ( " " ) } ` . trim ( )
: ` Start from ${ primarySystem ? . name || primaryTag } , audit the existing workflow there, and then scope the change so the request can be broken into editor-facing behavior, saved data changes, and any runtime handoff that may be required. ` ;
const reviewOptions = Array . isArray ( routing ? . possibleDirections ) && routing . possibleDirections . length > 0
? routing . possibleDirections
: [ "Clarify which part of the editor or runtime should own this request first." ] ;
return {
submissionId : request . id ,
sourceText : request . sourceText ,
confidence : 0.38 ,
minimumConfidence : 0.38 ,
routing ,
items : [
{
title ,
primaryCategory : primaryTag ,
tags : Array . isArray ( routing ? . suggestedTags ) && routing . suggestedTags . length > 0 ? routing . suggestedTags : [ "General" ] ,
statusRecommendation : "needs_review" ,
parsedInterpretation ,
implementationApproach ,
affectedSystems : primarySystem ? . name ? [ primarySystem . name ] : [ ] ,
affectedFiles : [ ] ,
problemType : "feature" ,
rawExcerpt : String ( request . sourceText || "" ) . trim ( ) ,
confidence : 0.38 ,
reviewRationale : String ( routing ? . rationale || "" ) . trim ( ) || "The request is still too broad to auto-promote safely." ,
reviewOptions ,
notes : String ( routing ? . summary || "" ) . trim ( ) ,
} ,
] ,
} ;
}
2026-06-27 00:18:41 -04:00
async function analyzeRequest ( config , kb , request ) {
2026-06-27 01:44:11 -04:00
const heuristicRouting = deriveHeuristicRouting ( kb , request . sourceText ) ;
const routingPrompt = buildRoutingPrompt ( request , kb , heuristicRouting ) ;
let routing = heuristicRouting ;
try {
const routingResult = await callModelApi ( config , routingPrompt , {
maxTokens : Math . min ( config . maxTokens , 900 ) ,
temperature : 0.1 ,
} ) ;
routing = normalizeRoutingResult ( routingResult , kb , request . sourceText , heuristicRouting ) ;
} catch ( error ) {
console . warn ( ` Routing pass fallback for ${ request . id } : ${ String ( error ) } ` ) ;
routing = normalizeRoutingResult ( { } , kb , request . sourceText , heuristicRouting ) ;
}
const kbContext = buildKbContext ( kb , request . sourceText , routing ) ;
const systemNames = kbContext . systems . map ( ( { system } ) => system . name ) ;
2026-06-27 00:18:41 -04:00
console . log ( ` Analyzing ${ request . id } : ${ request . title } ` ) ;
console . log ( ` Systems: ${ systemNames . join ( ", " ) } ` ) ;
if ( ! config . dryRun ) {
await markRequestProcessing ( config , request ) ;
}
2026-06-27 01:44:11 -04:00
const prompt = buildAnalysisPrompt ( request , routing , kbContext , kb . requestSchema , kb ) ;
let normalizedResult ;
try {
const modelResult = await callModelApi ( config , prompt , {
maxTokens : config . maxTokens ,
temperature : 0.2 ,
} ) ;
normalizedResult = normalizeAnalysisResult (
modelResult ,
request ,
kbContext . systems . map ( ( entry ) => entry . system ) ,
kb ,
) ;
} catch ( error ) {
console . warn ( ` Analysis pass fallback for ${ request . id } : ${ String ( error ) } ` ) ;
normalizedResult = buildFallbackAnalysisResult ( request , routing , kb ) ;
}
normalizedResult . routing = routing ;
2026-06-27 00:18:41 -04:00
const action = shouldPromoteAnalysis ( normalizedResult , config ) ? "promote" : "review" ;
console . log ( ` Result: ${ normalizedResult . items . length } item(s), action= ${ action } , confidence= ${ normalizedResult . confidence ? ? "n/a" } ` ) ;
if ( config . dryRun ) {
console . log ( JSON . stringify ( {
requestId : request . id ,
action ,
result : normalizedResult ,
} , null , 2 ) ) ;
return { action , result : normalizedResult } ;
}
if ( action === "promote" ) {
await promoteRequest ( config , request , normalizedResult ) ;
} else {
await markRequestReview ( config , request , normalizedResult ) ;
}
return { action , result : normalizedResult } ;
}
function selectPendingRequests ( payload , config ) {
const requests = Array . isArray ( payload ? . requests ) ? payload . requests : [ ] ;
const pending = requests . filter ( ( request ) => String ( request ? . status || "" ) . trim ( ) . toLowerCase ( ) === "pending" ) ;
if ( config . requestId ) {
return pending . filter ( ( request ) => String ( request ? . id || "" ) . trim ( ) === config . requestId ) ;
}
return pending
. filter ( ( request ) => {
const analysisState = String ( request ? . analysis ? . state || "" ) . trim ( ) . toLowerCase ( ) ;
return ! analysisState || analysisState === "unprocessed" ;
} )
. slice ( 0 , Math . max ( 1 , config . limit ) ) ;
}
async function runSinglePass ( config , kb ) {
const payload = await getLauncherRequests ( config ) ;
const pendingRequests = selectPendingRequests ( payload , config ) ;
if ( pendingRequests . length === 0 ) {
console . log ( "No eligible pending requests found." ) ;
return 0 ;
}
let processedCount = 0 ;
for ( const request of pendingRequests ) {
try {
await analyzeRequest ( config , kb , request ) ;
processedCount += 1 ;
} catch ( error ) {
console . error ( ` Failed to analyze ${ request . id } : ${ String ( error ) } ` ) ;
if ( ! config . dryRun ) {
try {
await markRequestError ( config , request , String ( error ) ) ;
} catch ( secondaryError ) {
console . error ( ` Failed to record analysis error for ${ request . id } : ${ String ( secondaryError ) } ` ) ;
}
}
}
}
return processedCount ;
}
async function main ( ) {
const config = finalizeConfig ( parseArgs ( process . argv . slice ( 2 ) ) ) ;
if ( config . help ) {
printHelp ( ) ;
return ;
}
if ( ! config . dryRun && ! config . model ) {
throw new Error ( "Missing local model id. Set REQUEST_ANALYZER_MODEL or pass --model." ) ;
}
if ( ! config . dryRun && ! config . apiKey ) {
throw new Error (
config . provider === "deepseek"
? "Missing DeepSeek API key. Set DEEPSEEK_API_KEY or REQUEST_ANALYZER_API_KEY."
: "Missing model API key. Set REQUEST_ANALYZER_API_KEY or pass --api-key."
) ;
}
const kb = await loadKnowledgeBase ( ) ;
if ( config . poll ) {
console . log ( ` Watching pending requests every ${ config . intervalMs } ms ` ) ;
// eslint-disable-next-line no-constant-condition
while ( true ) {
await runSinglePass ( config , kb ) ;
await sleep ( config . intervalMs ) ;
}
}
await runSinglePass ( config , kb ) ;
}
main ( ) . catch ( ( error ) => {
console . error ( String ( error ) ) ;
process . exitCode = 1 ;
} ) ;