{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../src/core/routing/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC3B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gEAAgE;IAChE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,uDAAuD;IACvD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,oGAAoG;AACpG,MAAM,WAAW,wBAAwB;IACxC,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,uGAAuG;IACvG,aAAa,CAAC,EAAE;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;CACF;AAED,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,YAAY,KAAK,CAAC;AAmJ/B;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,GAAE,wBAA6B,GACpC,0BAA0B,CAiD5B;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,0BAA0B,EAAE,aAAa,CAAC,GAAG,MAAM,CAG3F;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD","sourcesContent":["/**\n * Deterministic task feature extraction.\n *\n * Produces a bounded OrchestrationFeatureVector from the task text and optional\n * repository context. The baseline extractor never consults a model: it is a\n * pure deterministic function of its inputs so that decisions are replayable.\n * Missing values are explicit (undefined), never silently defaulted to zero\n * when that would imply confidence.\n *\n * Model-assisted features are optional and separately labeled; they can never\n * override the deterministic risk features (mutationRisk, requiresMutation,\n * requiresRelease, security sensitivity).\n */\n\nimport { createHash } from \"node:crypto\";\nimport type { OrchestrationFeatureVector } from \"./types.js\";\n\nexport interface TaskContext {\n\t/** The user task text. */\n\ttask: string;\n\t/** Repository language IDs detected by the caller (e.g. [\"typescript\"]). */\n\tlanguageIds?: string[];\n\t/** Number of affected projects/files if known (0 = unknown). */\n\testimatedAffectedFiles?: number;\n\t/** Operator-declared budget class override, if any. */\n\toperatorBudgetClass?: string;\n\t/** Operator-declared urgency. */\n\toperatorUrgency?: string;\n}\n\n/** Feature extraction mode: deterministic baseline only, or with optional model-assisted labels. */\nexport interface FeatureExtractionOptions {\n\t/** Bounded set of allowed language IDs (prevents unbounded features). */\n\tallowedLanguageIds?: string[];\n\t/** Model-assisted category labels. Separately labeled; never overrides deterministic risk features. */\n\tmodelAssisted?: {\n\t\ttaskCategory?: string;\n\t\tambiguity?: number;\n\t\tevidenceRequirement?: number;\n\t};\n}\n\nexport const FEATURE_SCHEMA_VERSION = 1;\nexport const MAX_FEATURES = 48;\nconst MAX_LANGUAGES = 12;\n\n/** Deterministic token estimate: word + identifier boundaries. */\nfunction estimateTokens(text: string): number {\n\tif (!text) return 0;\n\treturn Math.ceil(text.length / 4);\n}\n\n/** Detect category from task text deterministically. */\nfunction detectCategory(task: string): string {\n\tconst t = task.toLowerCase();\n\tif (/(release|publish|publish-?npm|tag|version|changelog|lockstep)/.test(t) && /(release|publish)/.test(t)) {\n\t\treturn \"release\";\n\t}\n\tif (/(migrate|migration|refactor|rename|move|restructure)/.test(t)) {\n\t\treturn \"implementation\";\n\t}\n\tif (/(bug\\s?fix|fix|repair|patch|regression)/.test(t)) {\n\t\treturn \"implementation\";\n\t}\n\tif (/(security|vulnerability|cve|exploit|sanitize|injection)/.test(t)) {\n\t\treturn \"security\";\n\t}\n\tif (/(implement|add|build|create|feature|write)/.test(t)) {\n\t\treturn \"implementation\";\n\t}\n\tif (/(investigate|find|locate|search|why|how does|understand|explain|trace|analyze)/.test(t)) {\n\t\treturn \"analysis\";\n\t}\n\tif (/(test|validate|verify|check|quality gate|benchmark)/.test(t)) {\n\t\treturn \"operational_testing\";\n\t}\n\treturn \"operational\";\n}\n\n/** Detect mutation requirement deterministically. */\nfunction detectRequiresMutation(task: string): boolean {\n\tconst t = task.toLowerCase();\n\treturn /(edit|write|update|add file|delete file|remove|create file|modify|change|implement|fix|refactor|migrate|rename)/.test(\n\t\tt,\n\t);\n}\n\nfunction detectRequiresRelease(task: string): boolean {\n\tconst t = task.toLowerCase();\n\treturn /(release|publish|deploy|tag|publish-?npm|version and release)/.test(t);\n}\n\nfunction detectRequiresCrossPlatform(task: string): boolean {\n\tconst t = task.toLowerCase();\n\treturn /(cross-platform|windows|linux|both platforms|platform-ci|win32)/.test(t);\n}\n\nfunction detectRequiresExternalResearch(task: string): boolean {\n\tconst t = task.toLowerCase();\n\treturn /(research|up-to-date|latest version|web search|primary source|paper|reference implementation|cta)/.test(t);\n}\n\n/** Deterministic complexity in 0..1. */\nfunction detectComplexity(task: string, ctx: TaskContext): number {\n\tlet score = 0;\n\tif (ctx.estimatedAffectedFiles !== undefined && ctx.estimatedAffectedFiles > 1) score += 0.3;\n\tconst files = ctx.estimatedAffectedFiles ?? 0;\n\tif (files >= 5) score += 0.2;\n\tif (detectRequiresMutation(task)) score += 0.2;\n\tif (detectRequiresCrossPlatform(task)) score += 0.1;\n\tif (ctx.languageIds && ctx.languageIds.length > 1) score += 0.1;\n\tif (\n\t\t/(multiple file|many file|across|cross-cutting|architecture|state machine|transactional)/.test(task.toLowerCase())\n\t) {\n\t\tscore += 0.2;\n\t}\n\t// Length heuristic — very short tasks are simple, very long tasks compound complexity.\n\tconst len = task.length;\n\tif (len > 400) score += 0.1;\n\treturn Math.min(1, score);\n}\n\n/** Deterministic ambiguity in 0..1. */\nfunction detectAmbiguity(task: string, modelAssistedAmbiguity?: number): number {\n\tif (modelAssistedAmbiguity !== undefined) return Math.max(0, Math.min(1, modelAssistedAmbiguity));\n\tconst t = task.toLowerCase();\n\tlet score = 0;\n\tif (/(maybe|perhaps|possibly|unsure|not sure|ambiguous|either|or whether)/.test(t)) score += 0.4;\n\tif (/(i don't know|unknown|unclear|investigate first)/.test(t)) score += 0.3;\n\tif (t.split(/\\?/).length - 1 > 1) score += 0.2;\n\tif (task.includes(\"?\")) score += 0.1;\n\t// Vagueness by length\n\tif (task.trim().length < 40) score += 0.2;\n\treturn Math.min(1, score);\n}\n\n/** Deterministic evidence requirement in 0..1. */\nfunction detectEvidenceRequirement(task: string, modelAssistedEvidence?: number): number {\n\tif (modelAssistedEvidence !== undefined) return Math.max(0, Math.min(1, modelAssistedEvidence));\n\tconst t = task.toLowerCase();\n\tlet score = 0;\n\tif (/(verify|validate|evidence|baseline|compare|measure|prove|test|reproduce|confirm)/.test(t)) score += 0.5;\n\tif (/(regression|quality gate|acceptance)/.test(t)) score += 0.3;\n\tif (/benchmark|performance|latency|cost/.test(t)) score += 0.3;\n\treturn Math.min(1, score);\n}\n\n/** Deterministic mutation risk (0..1). Higher = more destructive/irreversible. */\nfunction detectMutationRisk(task: string): number {\n\tconst t = task.toLowerCase();\n\tlet score = 0;\n\tif (/(delete|remove file|drop|wipe|clear|reset\\s--hard|force|overwrite source)/.test(t)) score = 0.9;\n\telse if (/(migrate|refactor|rename|restructure|rewrite)/.test(t)) score = 0.6;\n\telse if (/(edit|update|modify|change|write to|add file|patch)/.test(t)) score = 0.35;\n\tif (/(security|production|prod|release|critical path)/.test(t)) score = Math.min(1, score + 0.2);\n\treturn Math.min(1, score);\n}\n\nfunction failureClusters(task: string, _ctx: TaskContext, allowed: string[]): string[] {\n\tconst clusters: string[] = [];\n\tif (/(stall|hang|timeout|not responding)/.test(task.toLowerCase())) clusters.push(\"stall\");\n\tif (/(flaky|intermittent|sporadic)/.test(task.toLowerCase())) clusters.push(\"flakiness\");\n\tif (/(tool failure|tool call|capability)/.test(task.toLowerCase())) clusters.push(\"tool_failure\");\n\tif (/(rollback|transaction|partial|atomic)/.test(task.toLowerCase())) clusters.push(\"rollback\");\n\t// Bound to allowed set.\n\tconst out = clusters.filter((c) => allowed.includes(c));\n\treturn out.slice(0, 4);\n}\n\nconst DEFAULT_ALLOWED_LANGUAGES = [\n\t\"typescript\",\n\t\"javascript\",\n\t\"python\",\n\t\"go\",\n\t\"rust\",\n\t\"java\",\n\t\"csharp\",\n\t\"ruby\",\n\t\"kotlin\",\n\t\"swift\",\n\t\"php\",\n\t\"c\",\n\t\"cpp\",\n\t\"shell\",\n\t\"sql\",\n\t\"markdown\",\n\t\"yaml\",\n\t\"json\",\n];\n\n/**\n * Deterministic feature extraction baseline.\n * Returns a bounded, versioned feature vector with an explicit feature hash.\n */\nexport function extractFeatures(\n\ttask: string | TaskContext,\n\toptions: FeatureExtractionOptions = {},\n): OrchestrationFeatureVector {\n\tconst ctx: TaskContext = typeof task === \"string\" ? { task } : task;\n\tconst text = ctx.task ?? \"\";\n\tconst allowed = options.allowedLanguageIds ?? DEFAULT_ALLOWED_LANGUAGES;\n\tconst languageIds = (ctx.languageIds ?? []).filter((l) => allowed.includes(l)).slice(0, MAX_LANGUAGES);\n\n\tconst taskCategory = options.modelAssisted?.taskCategory ?? detectCategory(text);\n\tconst ambiguity = detectAmbiguity(text, options.modelAssisted?.ambiguity);\n\tconst evidenceRequirement = detectEvidenceRequirement(text, options.modelAssisted?.evidenceRequirement);\n\t// Deterministic risk features — never overridden by model-assisted labels.\n\tconst requiresMutation = detectRequiresMutation(text);\n\tconst requiresRelease = detectRequiresRelease(text);\n\tconst requiresCrossPlatformValidation = detectRequiresCrossPlatform(text);\n\tconst requiresExternalResearch = detectRequiresExternalResearch(text);\n\tconst mutationRisk = detectMutationRisk(text);\n\tconst taskComplexity = detectComplexity(text, ctx);\n\tconst relevantFailureClusters = failureClusters(text, ctx, [\n\t\t\"stall\",\n\t\t\"flakiness\",\n\t\t\"tool_failure\",\n\t\t\"rollback\",\n\t\t\"retrieval\",\n\t\t\"structured_output\",\n\t]);\n\n\tconst vector: OrchestrationFeatureVector = {\n\t\tschemaVersion: FEATURE_SCHEMA_VERSION,\n\t\ttaskCategory,\n\t\ttaskComplexity,\n\t\tambiguity,\n\t\tmutationRisk,\n\t\tevidenceRequirement,\n\t\testimatedAffectedFiles: ctx.estimatedAffectedFiles,\n\t\testimatedContextTokens:\n\t\t\tctx.estimatedAffectedFiles !== undefined && ctx.estimatedAffectedFiles > 0\n\t\t\t\t? Math.min(400_000, estimateTokens(text) + ctx.estimatedAffectedFiles * 8000)\n\t\t\t\t: estimateTokens(text),\n\t\trequiresMutation,\n\t\trequiresExternalResearch,\n\t\trequiresCrossPlatformValidation,\n\t\trequiresRelease,\n\t\tlanguageIds,\n\t\trelevantFailureClusters,\n\t\tfeatureHash: \"\",\n\t};\n\n\t// Compute feature hash after all fields are set (excluding the hash itself).\n\tvector.featureHash = featureHash(vector);\n\treturn vector;\n}\n\n/** Stable hash of a feature vector (excluding the hash field). */\nexport function featureHash(vector: Omit<OrchestrationFeatureVector, \"featureHash\">): string {\n\tconst copy = { ...vector, featureHash: undefined } as Record<string, unknown>;\n\treturn createHash(\"sha256\").update(JSON.stringify(copy)).digest(\"hex\");\n}\n\n/** Deterministic task fingerprint used to correlate decisions across runs. */\nexport function taskFingerprint(task: string): string {\n\treturn createHash(\"sha256\").update(task.trim()).digest(\"hex\");\n}\n"]}