// Universal solver implementation for the demo worker.
//
// Every reasoning path here mirrors the Rust `FormalAiEngine` in
// `src/solver.rs` so the website, CLI, Telegram bot, library, and HTTP server
// all produce the same answers for the same prompts. The answer the user
// sees is always a projection of an append-only event log — there is no
// hardcoded prompt→answer table.
//
// All multilingual phrases, concept summaries, and the tool registry are
// loaded from `seed/*.lino` files at startup via `seed_loader.js`. Editing a
// `.lino` file is enough to retune the agent — no JavaScript change required.
function currentAssetVersion() {
try {
const search = self.location && self.location.search;
const match = search && /[?&]v=([^&]+)/.exec(search);
return match ? decodeURIComponent(match[1].replace(/\+/g, " ")) : "";
} catch (_error) {
return "";
}
}
function withAssetVersion(url) {
const version = currentAssetVersion();
if (!version) return url;
return `${url}${url.includes("?") ? "&" : "?"}v=${encodeURIComponent(
version,
)}`;
}
try {
importScripts(withAssetVersion("seed_loader.js"));
} catch (_error) {
// Seed loader is optional: tests that mock the worker may exclude it.
}
let wasm;
let mode = "wasm worker";
// Hard-coded fallbacks. These are only used if `seed/*.lino` fails to load,
// e.g. when the worker runs from a `file://` URL. The shipped GitHub Pages
// build always fetches the seed successfully.
const FALLBACK_IDENTITY_ANSWER =
"I am formal-ai, a deterministic symbolic AI implementation that answers from local Links Notation rules and OpenAI-compatible API shapes. I do not perform neural inference in this demo.";
const FALLBACK_ASSISTANT_NAME_ANSWER =
"I'm formal AI, and currently I don't have a name. But you can name me as you like.";
const FALLBACK_GREETING_ANSWER = "Hi, how may I help you?";
const FALLBACK_TEST_STATUS_ANSWER = "Test passed. I'm here.";
const FALLBACK_COURTESY_RESPONSE_ANSWER =
"Glad to hear it. What would you like to do next?";
const FALLBACK_COURTESY_ACKNOWLEDGEMENTS = [
"Glad to hear it.",
"You're welcome.",
];
const FALLBACK_COURTESY_FOLLOW_UPS = [
"What would you like to do next?",
"Do you want to discuss something else?",
];
const FALLBACK_UNKNOWN_ANSWER =
"I don't know how to answer that yet. I cannot answer that from local links rules yet. To inspect what I can do, send `List behavior rules`, then `Show behavior rule unknown`. To teach this dialog a response, send: When I say `your prompt`, answer `your answer`. If this still needs a shared Links Notation seed fact or links rule after those checks, use Report issue with the reasoning trace, or export memory to keep a dialog-local rule durable.";
const FALLBACK_CLARIFICATION_ANSWER =
"I'm sorry for the confusion. I am formal-ai, a deterministic symbolic AI. I can answer greetings, identity questions, concept lookups (what is X?), arithmetic, and parameterized program templates. If you'd like to ask about something specific, try one of those or add a fact in Links Notation.";
// Mutable runtime tables — populated from seed at init(). Each entry is
// `{ text, variants }` so the worker can return either the canonical phrase
// (for deterministic tests and tool calls) or a random variant (for greeting
// randomisation introduced in issue #27). Courtesy responses can also carry
// separated acknowledgement and follow-up fragments for issue #160.
let MULTILINGUAL_ANSWERS = {
greeting: {
en: { text: FALLBACK_GREETING_ANSWER, variants: [FALLBACK_GREETING_ANSWER] },
},
farewell: {
en: { text: "Goodbye! Feel free to return any time.", variants: ["Goodbye! Feel free to return any time."] },
},
test_status: {
en: { text: FALLBACK_TEST_STATUS_ANSWER, variants: [FALLBACK_TEST_STATUS_ANSWER] },
},
courtesy_response: {
en: {
text: FALLBACK_COURTESY_RESPONSE_ANSWER,
variants: [FALLBACK_COURTESY_RESPONSE_ANSWER],
acknowledgements: FALLBACK_COURTESY_ACKNOWLEDGEMENTS,
followUps: FALLBACK_COURTESY_FOLLOW_UPS,
},
},
identity: {
en: { text: FALLBACK_IDENTITY_ANSWER, variants: [FALLBACK_IDENTITY_ANSWER] },
},
assistant_name: {
en: {
text: FALLBACK_ASSISTANT_NAME_ANSWER,
variants: [FALLBACK_ASSISTANT_NAME_ANSWER],
},
},
clarification: {
en: {
text: FALLBACK_CLARIFICATION_ANSWER,
variants: [FALLBACK_CLARIFICATION_ANSWER],
},
},
unknown: {
en: { text: FALLBACK_UNKNOWN_ANSWER, variants: [FALLBACK_UNKNOWN_ANSWER] },
},
};
let CONCEPTS = [];
let CONCEPT_CONTEXTS = [];
let FACTS = [];
let PROJECTS = [];
let BRAINSTORM_SEEDS = {
triggers: [
"brainstorm",
"give me five ideas",
"give me 5 ideas",
"give me ten ideas",
"give me 10 ideas",
"suggest five",
"suggest 5",
"suggest ten",
"suggest 10",
],
categories: [
{
slug: "project_ideas",
intent: "brainstorm_project_ideas",
detectionKeywords: [],
items: [
"A local Links Notation notebook with searchable traces.",
"A deterministic code-review checklist generator.",
"A multilingual prompt-variation test corpus.",
"A CLI that converts issue requirements into traceable tests.",
"A source-cache inspector for reproducible agent runs.",
"A changelog-fragment consistency checker.",
"A prompt-matrix generator for four-language smoke tests.",
"A Wikidata anchor verifier for local seed records.",
"A trace viewer that groups events by solver phase.",
"A small offline issue-to-test planning tool.",
],
},
],
};
let PERSONA_SEEDS = {
triggers: ["pretend you are", "act as", "roleplay", "explain like you are"],
defaultPersona: "requested persona",
bodyTemplate:
"Roleplay frame recorded for <persona>. I will keep the persona explicit and factual: <body>",
fallbackBody:
"relativity says measurements of space and time depend on the observer's motion, while the laws of physics stay consistent.",
personas: [
{ displayName: "Albert Einstein", aliases: ["einstein"], wikidata: "Q937" },
{ displayName: "Ada Lovelace", aliases: ["ada lovelace"], wikidata: "Q7259" },
{ displayName: "teacher", aliases: ["teacher"], wikidata: "" },
],
topics: [
{
slug: "algorithm",
detectionKeywords: ["algorithm", "algorithms"],
body:
"an algorithm is a precise sequence of steps, so a reliable explanation names the inputs, the ordered operations, and the expected result.",
},
{
slug: "time_dilation",
detectionKeywords: ["time dilation"],
body:
"time dilation means clocks can measure different elapsed times when observers move differently or sit in different gravitational fields.",
},
],
};
let TOOLS = [];
let SEED_RAW = {};
let AGENT_INFO = {};
let LANGUAGE_RULES = [
{ language: "ru", start: 0x0400, end: 0x04ff },
{ language: "hi", start: 0x0900, end: 0x097f },
{ language: "zh", start: 0x4e00, end: 0x9fff },
];
let PROMPT_PATTERNS = [];
// Intent routing rules loaded from `seed/intent-routing.lino` at init time.
// `intents` mirror `seed::IntentRoute` from the Rust crate, so the browser
// and the Rust solver behave identically when classifying prompts. The
// fallback below mirrors the contents of `data/seed/intent-routing.lino`
// so the worker remains functional even when the `.lino` fetch fails (for
// example when the demo is opened from `file://`).
let INTENT_ROUTING = {
intents: [
{
id: "intent_greeting",
slug: "greeting",
responseLink: "response:greeting",
keywords: [
"hi",
"hello",
"hey",
"привет",
"здравствуйте",
"шалом",
"नमस्ते",
"नमस्कार",
"सलाम",
"हाय",
"你好",
"您好",
"嗨",
"哈喽",
],
phrases: [
"how are you",
"how are you doing",
"how do you do",
"how is it going",
"how s it going",
"how are things",
"шабат шалом",
"как дела",
"как твои дела",
"как ваши дела",
"как у тебя дела",
"как у вас дела",
"привет как дела",
"здравствуйте как ваши дела",
"как поживаешь",
"как вы поживаете",
"राम राम",
"कैसे हो",
"आप कैसे हैं",
"तुम कैसे हो",
"क्या हाल है",
"आपका क्या हाल है",
"सब कैसा चल रहा है",
"早上好",
"早安",
"你好吗",
"你还好吗",
"你怎么样",
"您怎么样",
"最近怎么样",
"过得怎么样",
],
tokens: ["greet"],
combos: [],
},
{
id: "intent_farewell",
slug: "farewell",
responseLink: "response:farewell",
keywords: [
"bye",
"goodbye",
"пока",
"ciao",
"tschüss",
"再见",
"拜拜",
"回见",
"अलविदा",
"विदा",
"बाय",
"टाटा",
],
phrases: ["до свидания", "досвидания", "改天见", "后会有期", "फिर मिलेंगे"],
tokens: [],
combos: [],
},
{
id: "intent_courtesy_response",
slug: "courtesy_response",
responseLink: "response:courtesy_response",
keywords: ["thanks", "спасибо", "благодарю", "धन्यवाद", "शुक्रिया", "谢谢"],
phrases: [
"thank you",
"i am fine thank you",
"i am fine thanks",
"i m fine thank you",
"i m fine thanks",
"i am good thank you",
"i am good thanks",
"i m good thank you",
"i m good thanks",
"fine thank you",
"fine thanks",
"good thank you",
"good thanks",
"doing well thank you",
"doing well thanks",
"у меня все хорошо спасибо",
"у меня всё хорошо спасибо",
"все хорошо спасибо",
"всё хорошо спасибо",
"хорошо спасибо",
"нормально спасибо",
"ого чето начал соображать",
"ого чёто начал соображать",
"ого чё то начал соображать",
"ого что то начал соображать",
"मैं ठीक हूँ धन्यवाद",
"ठीक हूँ धन्यवाद",
"मैं अच्छा हूँ धन्यवाद",
"我很好谢谢",
"我很好 谢谢",
"好的谢谢",
"好的 谢谢",
],
tokens: [],
combos: [],
},
{
id: "intent_test_status",
slug: "test_status",
responseLink: "response:test_status",
keywords: [
"test",
"ping",
"pong",
"testing",
"тест",
"пинг",
"टेस्ट",
"परीक्षण",
"测试",
"測試",
],
phrases: [
"test passed",
"testing 123",
"are you there",
"you there",
"i m here",
"i am here",
"я здесь",
"тест пройден",
"ты здесь",
"вы здесь",
"परीक्षण सफल रहा",
"मैं यहाँ हूँ",
"मैं यहां हूं",
"क्या आप वहाँ हैं",
"क्या आप वहां हैं",
"测试通过",
"測試通過",
"我在这里",
"我在這裡",
"你在吗",
"您在吗",
"你在嗎",
"您在嗎",
],
tokens: [],
combos: [
["test", "passed"],
["test", "here"],
["testing", "123"],
["ping", "test"],
["тест", "пройден"],
["тест", "здесь"],
["परीक्षण", "सफल"],
],
},
{
id: "intent_assistant_name",
slug: "assistant_name",
responseLink: "response:assistant_name",
keywords: [],
phrases: [
"what is your name",
"what s your name",
"what's your name",
"do you have a name",
"what should i call you",
"как твое имя",
"как твоё имя",
"как тебя зовут",
"у тебя есть имя",
"आपका नाम क्या है",
"तुम्हारा नाम क्या है",
"你叫什么名字",
"您叫什么名字",
"你的名字是什么",
"你有名字吗",
],
tokens: [],
combos: [
["what", "your", "name"],
["you", "have", "name"],
["call", "you"],
["как", "тебя", "зовут"],
],
},
{
id: "intent_identity",
slug: "identity",
responseLink: "response:identity",
keywords: [],
phrases: [
"who are you",
"what are you",
"who is formal ai",
"what is formal ai",
"who is formalai",
"what is formalai",
"tell me about yourself",
"introduce yourself",
"let s get acquainted",
"lets get acquainted",
"let us get acquainted",
"let s get to know each other",
"кто ты",
"что ты",
"расскажи о себе",
"расскажи мне о себе",
"расскажи про себя",
"опиши себя",
"представься",
"давай знакомиться",
"давай познакомимся",
"давайте познакомимся",
"तुम कौन हो",
"तू कौन है",
"आप कौन हैं",
"अपना परिचय दो",
"अपने बारे में बताओ",
"चलो परिचय करते हैं",
"आइए परिचय करें",
"चलो एक दूसरे को जानें",
"你是谁",
"您是谁",
"你是什么",
"介绍一下你自己",
"告诉我你自己",
"你是誰",
"我们认识一下吧",
"认识一下吧",
"让我们认识一下",
],
tokens: [],
combos: [
["who", "you"],
["what", "you"],
["tell", "yourself"],
["introduce", "yourself"],
["let", "s", "acquainted"],
["lets", "acquainted"],
["let", "us", "acquainted"],
["know", "each", "other"],
["кто", "ты"],
["что", "ты"],
["расскажи", "себе"],
["опиши", "себя"],
["давай", "знакомиться"],
["давай", "познакомимся"],
["давайте", "познакомимся"],
["चलो", "परिचय"],
["आइए", "परिचय"],
["who", "formal", "ai"],
["what", "formal", "ai"],
],
},
],
articlePrefixes: ["the ", "a ", "an "],
tracePrefixes: ["answer_", "trace_"],
};
function fallbackEntry(intent) {
if (intent === "greeting") {
return { text: FALLBACK_GREETING_ANSWER, variants: [FALLBACK_GREETING_ANSWER] };
}
if (intent === "courtesy_response") {
return {
text: FALLBACK_COURTESY_RESPONSE_ANSWER,
variants: [FALLBACK_COURTESY_RESPONSE_ANSWER],
acknowledgements: FALLBACK_COURTESY_ACKNOWLEDGEMENTS,
followUps: FALLBACK_COURTESY_FOLLOW_UPS,
};
}
if (intent === "identity") {
return { text: FALLBACK_IDENTITY_ANSWER, variants: [FALLBACK_IDENTITY_ANSWER] };
}
if (intent === "assistant_name") {
return {
text: FALLBACK_ASSISTANT_NAME_ANSWER,
variants: [FALLBACK_ASSISTANT_NAME_ANSWER],
};
}
if (intent === "test_status") {
return { text: FALLBACK_TEST_STATUS_ANSWER, variants: [FALLBACK_TEST_STATUS_ANSWER] };
}
if (intent === "clarification") {
return {
text: FALLBACK_CLARIFICATION_ANSWER,
variants: [FALLBACK_CLARIFICATION_ANSWER],
};
}
return { text: FALLBACK_UNKNOWN_ANSWER, variants: [FALLBACK_UNKNOWN_ANSWER] };
}
function normalizeEntry(value, intent) {
if (value && typeof value === "object" && typeof value.text === "string") {
const variants =
Array.isArray(value.variants) && value.variants.length > 0
? value.variants
: [value.text];
const acknowledgements = Array.isArray(value.acknowledgements)
? value.acknowledgements.filter(Boolean)
: [];
const followUps = Array.isArray(value.followUps)
? value.followUps.filter(Boolean)
: [];
return {
text: value.text,
variants: variants,
acknowledgements: acknowledgements,
followUps: followUps,
};
}
if (typeof value === "string") {
return {
text: value,
variants: [value],
acknowledgements: [],
followUps: [],
};
}
return fallbackEntry(intent);
}
function responseEntryFor(intent, language) {
const table = MULTILINGUAL_ANSWERS[intent] || {};
const raw = table[language] || table.en || fallbackEntry(intent);
return normalizeEntry(raw, intent);
}
function answerFor(intent, language, options) {
const opts = options || {};
const entry = responseEntryFor(intent, language);
if (opts.randomize && Array.isArray(entry.variants) && entry.variants.length > 1) {
const idx = Math.floor(Math.random() * entry.variants.length);
return entry.variants[idx] || entry.text;
}
return entry.text;
}
function normalizeAssistantNamePreference(value) {
return String(value || "")
.replace(/[\r\n\t]+/g, " ")
.replace(/\s+/g, " ")
.trim()
.replace(/^[`"']+|[`"']+$/g, "")
.trim()
.slice(0, 64);
}
function assistantNameAnswer(language, preferences) {
const name = normalizeAssistantNamePreference(
preferences && preferences.assistantName,
);
if (!name) return answerFor("assistant_name", language);
if (language === "ru") {
return `Меня зовут ${name}. Я formal AI.`;
}
if (language === "hi") {
return `मेरा नाम ${name} है। मैं formal AI हूँ।`;
}
if (language === "zh") {
return `我的名字是 ${name}。我是 formal AI。`;
}
return `My name is ${name}. I'm formal AI.`;
}
// Mirrors `src/engine.rs::UNKNOWN_OPENERS_*`. The first entry of each pool
// equals the opener already embedded in the seed text so the "with-variations"
// answer is a strict superset of the seed. Different prompts get different
// openers; the same prompt always picks the same one (FNV-1a hash, mirrored
// from `stableBehaviorRuleId`).
const UNKNOWN_OPENERS_BY_LANGUAGE = {
en: [
"I don't know how to answer that yet.",
"I didn't understand you.",
"I'm not sure how to respond to that yet.",
"I haven't learned to answer that yet.",
"That one is new to me.",
],
ru: [
"Я пока не знаю, как ответить на это.",
"Я тебя не понял.",
"Я не уверен, как на это ответить.",
"Я ещё не научился отвечать на это.",
"Это для меня новое.",
],
hi: [
"मुझे अभी इसका उत्तर देना नहीं आता।",
"मैं समझ नहीं पाया।",
"मुझे यकीन नहीं है कि कैसे उत्तर दूँ।",
"मैंने अभी तक यह उत्तर देना नहीं सीखा।",
"यह मेरे लिए नया है।",
],
zh: [
"我还不知道如何回答这个问题。",
"我不太明白你说的意思。",
"我不确定该如何回答。",
"我还没有学会回答这个问题。",
"这对我来说是新的。",
],
};
function unknownOpenersFor(language) {
return UNKNOWN_OPENERS_BY_LANGUAGE[language] || UNKNOWN_OPENERS_BY_LANGUAGE.en;
}
function selectUnknownOpener(prompt, language) {
const fromWasm = wasmSelectUnknownOpener(prompt, language);
if (fromWasm) return fromWasm;
const pool = unknownOpenersFor(language);
const trimmed = String(prompt || "").trim();
if (trimmed === "") return pool[0];
const id = stableBehaviorRuleId("unknown_opener", trimmed);
const hex = id.split("_").pop() || "0";
let value;
try {
value = BigInt(`0x${hex}`);
} catch (_err) {
value = 0n;
}
const index = Number(value % BigInt(pool.length));
return pool[index] || pool[0];
}
function stripLeadingUnknownOpener(text, language) {
const trimmed = String(text || "").trimStart();
const openers = unknownOpenersFor(language);
for (const known of openers) {
if (trimmed.startsWith(known)) {
return trimmed.slice(known.length).trimStart();
}
}
for (const separator of [". ", "。", "। "]) {
const idx = trimmed.indexOf(separator);
if (idx >= 0) {
return trimmed.slice(idx + separator.length).trimStart();
}
}
return trimmed;
}
function unknownAnswerWithVariation(prompt, language) {
const seedText = answerFor("unknown", language);
const opener = selectUnknownOpener(prompt, language);
const body = stripLeadingUnknownOpener(seedText, language);
if (!body) return opener;
return `${opener} ${body}`;
}
function numericPreference(value, fallback, min, max) {
const parsed = Number(value);
if (!Number.isFinite(parsed)) return fallback;
return Math.min(max, Math.max(min, parsed));
}
function pickVariant(values, randomize) {
if (!Array.isArray(values) || values.length === 0) return "";
if (!randomize || values.length === 1) return values[0];
return values[Math.floor(Math.random() * values.length)] || values[0];
}
function includeFollowUpQuestion(probability, randomize) {
if (probability <= 0) return false;
if (probability >= 1) return true;
if (!randomize) return probability >= 0.5;
return Math.random() < probability;
}
function courtesyResponseFor(language, preferences) {
const prefs = preferences || {};
const entry = responseEntryFor("courtesy_response", language);
const temperature = numericPreference(prefs.temperature, 0.7, 0, 1);
const followUpProbability = numericPreference(
prefs.followUpProbability,
0.75,
0,
1,
);
const randomize = temperature > 0;
const acknowledgements =
entry.acknowledgements.length > 0 ? entry.acknowledgements : [entry.text];
const followUps = entry.followUps;
const acknowledgement = pickVariant(acknowledgements, randomize);
const includeFollowUp =
followUps.length > 0 &&
includeFollowUpQuestion(followUpProbability, randomize);
return {
content: includeFollowUp
? `${acknowledgement} ${pickVariant(followUps, randomize)}`
: acknowledgement,
temperature: temperature,
randomize: randomize,
followUpProbability: followUpProbability,
followUpIncluded: includeFollowUp,
};
}
function definitionFusionByDefault(preferences) {
const value = preferences && preferences.definitionFusion;
if (value === true) return true;
if (value === false) return false;
const normalized = String(value || "").trim().toLowerCase();
return ["auto", "on", "true", "1", "merge", "fusion"].includes(normalized);
}
// Language detection and prompt normalization are owned by the Rust core
// (`src/web_engine_core.rs`) and exposed to the worker through the WASM
// exports `engine_detect_language` and `engine_normalize_prompt`. The JS
// branches below are pre-WASM fallbacks used during init() and on browsers
// that could not instantiate the worker — they must stay byte-for-byte
// compatible with the Rust path so the offline trace and the live answer
// agree (PR #134 feedback 4489651616).
function detectLanguage(prompt) {
const text = String(prompt || "");
const fromWasm = wasmDetectLanguage(text);
if (fromWasm !== null) {
if (fromWasm === "unknown") {
return AGENT_INFO.default_language || "en";
}
return fromWasm;
}
for (const ch of text) {
const code = ch.codePointAt(0);
for (const rule of LANGUAGE_RULES) {
if (
rule.language !== "en" &&
code >= rule.start &&
code <= rule.end
) {
return rule.language;
}
}
}
if (/[a-zA-Z]/.test(text)) return "en";
return AGENT_INFO.default_language || "en";
}
// Issue #324: the user can choose which language drives responses. The default
// ("last_message") answers in the detected language of the current message
// (fixing the Russian-prompt/English-answer bug). "preferred" pins responses to
// an explicitly selected language and "ui" follows the UI-language preference.
// Both fall back to the detected language when their source is "auto"/unset so
// the deterministic default behavior is never lost.
const RESPONSE_LANGUAGE_MODES = ["last_message", "preferred", "ui"];
function isKnownResponseLanguage(slug) {
return slug === "en" || slug === "ru" || slug === "hi" || slug === "zh";
}
function responseLanguageFor(detected, preferences, userContext) {
const prefs = preferences || {};
const mode = RESPONSE_LANGUAGE_MODES.includes(prefs.responseLanguage)
? prefs.responseLanguage
: "last_message";
if (mode === "preferred" && isKnownResponseLanguage(prefs.preferredLanguage)) {
return prefs.preferredLanguage;
}
if (mode === "ui") {
if (isKnownResponseLanguage(prefs.uiLanguage)) return prefs.uiLanguage;
// "auto" UI language follows the browser; fall back to the detected
// message language when no concrete browser language is supplied.
// `browserLanguages` may arrive as an array or a comma-joined string
// (see `collectUserContext` in app.js).
const raw = userContext ? userContext.browserLanguages : null;
const browser = Array.isArray(raw)
? raw
: typeof raw === "string"
? raw.split(",")
: [];
for (const tag of browser) {
const slug = String(tag || "").slice(0, 2).toLowerCase();
if (isKnownResponseLanguage(slug)) return slug;
}
}
return detected;
}
// CONCEPTS is populated from `seed/concepts.lino` at init() time.
function normalizePrompt(prompt) {
const text = String(prompt || "");
const fromWasm = wasmNormalizePrompt(text);
if (fromWasm !== null) return fromWasm;
// Keep letters, numbers and every Unicode mark (category M): Devanagari
// matras, the nukta and the virama are marks, so a bare \p{L}\p{N} filter
// would strip them and corrupt Hindi words (issue #312). Mark-awareness via
// \p{M} mirrors the Rust `normalize_prompt`, which keeps `is_alphanumeric()`
// characters plus its script-combining-mark ranges. Crucially it does NOT
// keep the whole U+0900–U+097F block: Indic punctuation such as the danda
// "।" (U+0964) is category Po, so both sides collapse it to a space. The
// boundary-aware role matcher (issue #386) depends on that parity — a
// retained danda would defeat the whole-token match for phrases like
// "अपना परिचय दो।".
return text
.toLowerCase()
.replace(/[^\p{L}\p{N}\p{M}]+/gu, " ")
.trim();
}
function normalizeConceptTerm(value) {
let lower = String(value || "").toLowerCase();
for (const prefix of ["the ", "a ", "an "]) {
if (lower.startsWith(prefix)) {
lower = lower.slice(prefix.length);
break;
}
}
return lower.trim().replace(/[?.!,;:]+$/g, "").trim();
}
function recordMatchesTerm(record, normalized) {
return (
normalizeConceptTerm(record.term) === normalized ||
normalizeConceptTerm(record.slug) === normalized ||
(Array.isArray(record.aliases) &&
record.aliases.some(
(alias) => normalizeConceptTerm(alias) === normalized,
))
);
}
function recordMatchesQueryTerm(record, normalized, contextNormalized) {
if (recordMatchesTerm(record, normalized)) return true;
if (!contextNormalized) return false;
return recordMatchesTerm(record, `${normalized} ${contextNormalized}`);
}
function contextRecordMatches(contextRecord, contextNormalized) {
if (!contextRecord) return false;
if (
Array.isArray(contextRecord.aliases) &&
contextRecord.aliases.some(
(alias) => normalizeConceptTerm(alias) === contextNormalized,
)
) {
return true;
}
return (
Array.isArray(contextRecord.labels) &&
contextRecord.labels.some(
(label) => normalizeConceptTerm(label.text) === contextNormalized,
)
);
}
function resolveContextRecord(contextNormalized) {
if (!contextNormalized) return null;
for (const record of CONCEPT_CONTEXTS) {
if (contextRecordMatches(record, contextNormalized)) return record;
}
return null;
}
function recordHasContext(record, contextNormalized) {
if (
Array.isArray(record.contexts) &&
record.contexts.some(
(candidate) => normalizeConceptTerm(candidate) === contextNormalized,
)
) {
return true;
}
// Registry fallback: resolve the user-supplied context through the
// concept-contexts registry and see whether the resolved record's slug is
// referenced by the concept's `contextLinks` list. Matches the Rust
// ranker (src/concepts.rs::record_has_context).
const contextRecord = resolveContextRecord(contextNormalized);
if (contextRecord && Array.isArray(record.contextLinks)) {
return record.contextLinks.some(
(slug) => String(slug).trim() === contextRecord.slug,
);
}
return false;
}
function localizedConceptFor(record, language) {
if (!record || !Array.isArray(record.localized)) return null;
return (
record.localized.find((loc) => loc && loc.language === language) ||
record.localized.find((loc) => loc && loc.language === "en") ||
null
);
}
function contextLabelFor(contextRecord, language) {
if (!contextRecord || !Array.isArray(contextRecord.labels)) {
return null;
}
const exact = contextRecord.labels.find(
(label) => label && label.language === language,
);
if (exact && exact.text) return exact.text;
const english = contextRecord.labels.find(
(label) => label && label.language === "en",
);
if (english && english.text) return english.text;
return contextRecord.slug || null;
}
function rankConceptForPair(termRaw, contextRaw) {
const normalized = normalizeConceptTerm(termRaw);
if (!normalized) return null;
const contextNormalized = contextRaw ? normalizeConceptTerm(contextRaw) : "";
const termMatches = CONCEPTS.filter((record) =>
recordMatchesQueryTerm(record, normalized, contextNormalized),
);
if (termMatches.length === 0) return null;
if (contextNormalized) {
const ctxHit = termMatches.find((record) =>
recordHasContext(record, contextNormalized),
);
if (ctxHit) {
return {
record: ctxHit,
contextMatch: true,
context: contextNormalized,
};
}
}
// No context match: prefer records with no contexts declared.
termMatches.sort((a, b) => {
const ac = (Array.isArray(a.contexts) && a.contexts.length > 0) ? 1 : 0;
const bc = (Array.isArray(b.contexts) && b.contexts.length > 0) ? 1 : 0;
return ac - bc;
});
return {
record: termMatches[0],
contextMatch: false,
context: contextNormalized || null,
};
}
function lookupConceptQuery(query) {
if (!query) return null;
const direct = rankConceptForPair(query.term, query.context);
if (query.context) {
const reversed = rankConceptForPair(query.context, query.term);
if (reversed && (!direct || (!direct.contextMatch && reversed.contextMatch))) {
return reversed;
}
}
return direct || null;
}
function lookupConcept(term) {
const hit = lookupConceptQuery({ term: term, context: null });
return hit ? hit.record : null;
}
// Default concept-lookup patterns when seed/prompt-patterns.lino is missing.
// Sorted longest-first so "what is a " beats "what is " when both match.
const DEFAULT_CONCEPT_SUFFIXES = [
" का अर्थ बताओ",
" क्या होता है",
" क्या है",
" कौन हैं",
" कौन है",
"的意思是什么",
"是什么意思",
"是甚麼",
"是什么",
"是誰",
"是谁",
];
const DEFAULT_CONCEPT_PREFIXES = [
"what is a ",
"what is an ",
"what is the ",
"what is ",
"what's a ",
"what's an ",
"what's the ",
"what's ",
"what do ",
"what does ",
"tell me about ",
"tell me what ",
"define ",
"explain ",
"describe ",
"who is ",
"who was ",
"что такое ",
"что это ",
"что означает слово ",
"кто такой ",
"кто такая ",
"кто это ",
"расскажи о ",
"расскажи про ",
"назови ",
"опиши ",
"объясни ",
"什么是",
"甚麼是",
"请解释",
"请说说",
"介绍一下",
];
function conceptPatternsByKind(kind) {
const matches = PROMPT_PATTERNS.filter(
(p) => p && p.intent === "concept_lookup" && p.kind === kind && p.text,
).map((p) => p.text);
// Sort longest-first so more specific patterns win.
matches.sort((a, b) => b.length - a.length);
if (matches.length > 0) return matches;
if (kind === "suffix") return DEFAULT_CONCEPT_SUFFIXES;
if (kind === "prefix") return DEFAULT_CONCEPT_PREFIXES;
return [];
}
function splitTermAndContext(bodyOriginal, bodyLower) {
const delimiters = conceptPatternsByKind("context_delimiter");
for (const delimiter of delimiters) {
const idx = bodyLower.indexOf(delimiter);
if (idx >= 0) {
const term = bodyLower.slice(0, idx).trim();
const context = bodyLower.slice(idx + delimiter.length).trim();
const termOriginal = bodyOriginal.slice(0, idx).trim();
const contextOriginal = bodyOriginal
.slice(idx + delimiter.length)
.trim();
if (term && context) {
return {
term: term,
context: context,
termOriginal: termOriginal || term,
contextOriginal: contextOriginal || context,
};
}
}
}
return {
term: bodyLower,
context: null,
termOriginal: bodyOriginal || bodyLower,
contextOriginal: null,
};
}
function stripLeadingRequest(input) {
const lower = input.toLowerCase();
const prefixes = [
"please tell me,",
"please tell me",
"tell me,",
"tell me",
];
const questionStarts = ["who ", "what ", "what's ", "who's "];
for (const prefix of prefixes) {
if (!lower.startsWith(prefix)) continue;
const rest = input.slice(prefix.length).trimStart();
const restLower = rest.toLowerCase();
if (
questionStarts.some((questionStart) =>
restLower.startsWith(questionStart),
)
) {
return rest;
}
}
return input;
}
function extractInvertedWhoIs(input, lower) {
if (!lower.startsWith("who ") || !lower.endsWith(" is")) return null;
const body = input.slice("who ".length, input.length - " is".length).trim();
if (!body) return null;
const normalized = body.toLowerCase();
if (["is", "was", "are"].includes(normalized)) return null;
return body;
}
function cleanMechanismFragment(value) {
return String(value || "")
.trim()
.replace(/^[`"'«»<>()\[\]{}]+/u, "")
.replace(/[`"'«»<>()\[\]{}]+$/u, "")
.replace(/[??。.!,,;:]+$/u, "")
.trim();
}
// Trim optional detail/politeness modifiers from a candidate subject and reject
// it outright when it is a non-referential subject. The modifier tails carry
// ROLE_DETAIL_MODIFIER (suffix surfaces, stripped in declaration order); the
// rejection set carries ROLE_NON_REFERENTIAL_SUBJECT (bare surfaces match the
// whole candidate, prefix surfaces match a candidate that begins with the
// literal before the … slot). Mirrors clean_mechanism_subject in
// src/solver_handler_how.rs — no per-language modifier or pronoun array here.
function cleanMechanismSubject(value) {
let clean = cleanMechanismFragment(value);
for (const form of roleWordForms(ROLE_DETAIL_MODIFIER)) {
const suffix = form.after;
const lower = clean.toLowerCase();
if (lower.endsWith(suffix)) {
clean = cleanMechanismFragment(clean.slice(0, clean.length - suffix.length));
}
}
const lower = clean.toLowerCase();
const nonReferential = roleWordForms(ROLE_NON_REFERENTIAL_SUBJECT).some((form) => {
if (form.slot === "bare") return lower === form.text;
if (form.slot === "prefix") return lower.startsWith(form.before);
return false;
});
if (!clean || nonReferential) {
return null;
}
return clean;
}
// Strip a trailing mechanism predicate so a prefix match such as "how does X
// work" yields the bare subject "X". The predicate tails carry
// ROLE_MECHANISM_PREDICATE (suffix surfaces); they are tried in declaration
// order and the first match wins. Mirrors strip_mechanism_tail in
// src/solver_handler_how.rs — no per-language tail array here.
function stripMechanismTail(subject) {
let clean = cleanMechanismSubject(subject);
if (!clean) return null;
const lower = clean.toLowerCase();
for (const form of roleWordForms(ROLE_MECHANISM_PREDICATE)) {
const suffix = form.after;
if (lower.endsWith(suffix)) {
clean = cleanMechanismSubject(clean.slice(0, clean.length - suffix.length));
break;
}
}
return clean;
}
function mechanismSubjectAfterPrefix(original, lower, prefix) {
if (!lower.startsWith(prefix)) return null;
return cleanMechanismSubject(original.slice(prefix.length));
}
function mechanismSubjectBeforeSuffix(original, lower, suffix) {
if (!lower.endsWith(suffix)) return null;
return cleanMechanismSubject(original.slice(0, -suffix.length));
}
function mechanismSubjectBetween(original, lower, prefix, suffixes) {
if (!lower.startsWith(prefix)) return null;
for (const suffix of suffixes) {
if (!lower.endsWith(suffix)) continue;
const end = original.length - suffix.length;
if (end <= prefix.length) return null;
return cleanMechanismSubject(original.slice(prefix.length, end));
}
return null;
}
function extractHowItWorksSubject(input, lowerInput) {
const original = cleanMechanismFragment(input);
if (!original) return null;
const lower = cleanMechanismFragment(lowerInput || original.toLowerCase())
.toLowerCase();
// The affixes are the slot-marked surface forms of the mechanism_inquiry
// meaning (data/seed/meanings-how.lino, embedded in MEANINGS_LINO): the
// position of the … marker classifies each form, so the matching strategy is
// derived from the data, not from a hardcoded per-language list. Bucket order
// — prefix, then circumfix, then suffix — and within-bucket declaration order
// mirror extract_how_it_works_subject in src/solver_handler_how.rs (#386).
// Suffix surfaces are end-anchored and script-disjoint across languages, so
// the cross-language declaration order does not change which one matches.
const forms = roleWordForms(ROLE_MECHANISM_INQUIRY);
for (const form of forms) {
if (form.slot !== "prefix") continue;
const subject = mechanismSubjectAfterPrefix(original, lower, form.before);
if (subject) return stripMechanismTail(subject);
}
for (const form of forms) {
if (form.slot !== "circumfix") continue;
const subject = mechanismSubjectBetween(original, lower, form.before, [
form.after,
]);
if (subject) return subject;
}
for (const form of forms) {
if (form.slot !== "suffix") continue;
const subject = mechanismSubjectBeforeSuffix(original, lower, form.after);
if (subject) return subject;
}
return null;
}
function cleanMeaningCandidate(value) {
const cleaned = String(value || "")
.trim()
.replace(/^[«»"“”‘’'`]+|[«»"“”‘’'`]+$/gu, "")
.trim();
if (!cleaned) return null;
if (/^(?:it|that|this|word|the word|mean|means|meaning|i)$/iu.test(cleaned)) {
return null;
}
return cleaned;
}
function extractMeaningQuestionBody(original, lower) {
for (const prefix of [
"what is the meaning of ",
"what's the meaning of ",
"what is meaning of ",
"meaning of ",
]) {
if (lower.startsWith(prefix)) {
return cleanMeaningCandidate(original.slice(prefix.length));
}
}
for (const suffix of [" mean", " means", " meaning"]) {
if (!lower.endsWith(suffix)) continue;
const stem = original.slice(0, -suffix.length).trim();
const stemLower = stem.toLowerCase();
for (const prefix of [
"what does the word ",
"what does ",
"what do ",
"what did ",
"what is the word ",
"what is ",
"what's ",
"what i ",
]) {
if (stemLower.startsWith(prefix)) {
return cleanMeaningCandidate(stem.slice(prefix.length));
}
}
}
return null;
}
function extractConceptQuery(prompt) {
let trimmedRaw = String(prompt || "")
.trim()
.replace(/[?。.!!,,;:]+$/g, "")
.trim();
if (!trimmedRaw) return null;
trimmedRaw = stripLeadingRequest(trimmedRaw);
const suffixes = conceptPatternsByKind("suffix");
for (const suffix of suffixes) {
if (trimmedRaw.endsWith(suffix)) {
return finalizeConceptBody(
trimmedRaw.slice(0, -suffix.length).trim(),
);
}
}
const lower = trimmedRaw.toLowerCase();
const meaningBody = extractMeaningQuestionBody(trimmedRaw, lower);
if (meaningBody) return finalizeConceptBody(meaningBody);
const invertedWhoBody = extractInvertedWhoIs(trimmedRaw, lower);
if (invertedWhoBody) return finalizeConceptBody(invertedWhoBody);
const howItWorksSubject = extractHowItWorksSubject(trimmedRaw, lower);
if (howItWorksSubject) return finalizeConceptBody(howItWorksSubject);
const prefixes = conceptPatternsByKind("prefix");
let body = null;
for (const prefix of prefixes) {
if (lower.startsWith(prefix)) {
body = trimmedRaw.slice(prefix.length);
break;
}
}
if (!body) return null;
return finalizeConceptBody(body);
}
function extractConceptTerm(prompt) {
const query = extractConceptQuery(prompt);
return query ? query.term : null;
}
function cleanWikipediaArticleQuestionTerm(value) {
return String(value || "")
.trim()
.replace(/^[«»"“”‘’'`「」『』]+|[«»"“”‘’'`「」『』]+$/gu, "")
.replace(/[?!.。!?।]+$/gu, "")
.replace(/\s+/g, " ")
.trim();
}
function hasWikipediaArticleQuestionShape(value) {
const lower = String(value || "").toLowerCase();
if (!/(?:wikipedia|wiki|википед|维基百科|維基百科|विकिपीडिया)/u.test(lower)) return false;
const hasArticleWord = /(?:article|page|стать[ьяеию]|страниц|条目|條目|页面|頁面|文章|लेख|पृष्ठ)/u.test(lower);
if (!hasArticleWord) return false;
return /(?:is there|does .*have|exist|available|есть|существ|имеет|найд|назв|有|存在|有没有|是否有|吗|嗎|क्या|है|मौजूद)/u.test(lower);
}
function extractWikipediaArticleQuestionTerm(prompt) {
const raw = cleanWikipediaArticleQuestionTerm(prompt);
if (!raw || !hasWikipediaArticleQuestionShape(raw)) return null;
const dashMatch = raw.match(/^(.+?)\s+[-—–:]\s+(.+)$/u);
if (dashMatch && hasWikipediaArticleQuestionShape(dashMatch[2])) {
return cleanWikipediaArticleQuestionTerm(dashMatch[1]);
}
for (const pattern of [
/^(?:is|are)\s+there\s+(?:an?\s+)?(?:wikipedia|wiki)\s+(?:article|page)\s+(?:about|on|for)\s+(.+)$/iu,
/^does\s+(?:wikipedia|wiki)\s+have\s+(?:an?\s+)?(?:article|page)\s+(?:about|on|for)\s+(.+)$/iu,
/^(?:есть|существует|имеется)\s+(?:ли\s+)?(?:в\s+)?(?:русскоязычной\s+)?википедии\s+(?:отдельная\s+)?(?:статья|страница)\s+(?:о|об|про|с\s+названием)\s+(.+)$/iu,
/^(?:есть|существует|имеется)\s+(?:ли\s+)?(?:отдельная\s+)?(?:статья|страница)\s+(?:в\s+)?(?:русскоязычной\s+)?википедии\s+(?:о|об|про|с\s+названием)\s+(.+)$/iu,
/^(?:维基百科|維基百科)(?:上)?(?:有|存在)(?:关于|關於|名为|名為)?\s*(.+?)\s*(?:的)?(?:条目|條目|文章|页面|頁面)(?:吗|嗎)?$/iu,
/^(.+?)\s*(?:在)?(?:维基百科|維基百科)(?:上)?(?:有|存在)(?:这样(?:的)?|這樣(?:的)?|一篇)?(?:条目|條目|文章|页面|頁面)(?:吗|嗎)?$/iu,
/^(?:क्या\s+)?(?:विकिपीडिया|wiki)\s+(?:पर|में)\s+(.+?)\s+(?:के\s+बारे\s+में\s+)?(?:लेख|पृष्ठ)\s+(?:है|मौजूद\s+है)$/iu,
/^(?:क्या\s+)?(.+?)\s+(?:के\s+बारे\s+में\s+)?(?:विकिपीडिया|wiki)\s+(?:पर|में)\s+(?:ऐसा\s+)?(?:लेख|पृष्ठ)\s+(?:है|मौजूद\s+है)$/iu,
]) {
const match = raw.match(pattern);
if (match) return cleanWikipediaArticleQuestionTerm(match[1]);
}
const trailingRussian = raw.match(/^(.+?)\s+(?:есть|существует|имеется)\s+(?:ли\s+)?(?:такая\s+)?(?:статья|страница)\s+(?:в\s+)?(?:русскоязычной\s+)?википедии$/iu);
if (trailingRussian) return cleanWikipediaArticleQuestionTerm(trailingRussian[1]);
const trailingHindi = raw.match(/^(.+?)\s+(?:के\s+बारे\s+में\s+)?(?:विकिपीडिया|wiki)\s+(?:पर|में)\s+(?:ऐसा\s+)?(?:लेख|पृष्ठ)\s+(?:है|मौजूद\s+है)$/iu);
if (trailingHindi) return cleanWikipediaArticleQuestionTerm(trailingHindi[1]);
const trailingChinese = raw.match(/^(.+?)\s*(?:在)?(?:维基百科|維基百科)(?:上)?(?:有|存在)(?:这样(?:的)?|這樣(?:的)?|一篇)?(?:条目|條目|文章|页面|頁面)(?:吗|嗎)?$/iu);
if (trailingChinese) return cleanWikipediaArticleQuestionTerm(trailingChinese[1]);
return null;
}
function refineWikipediaArticleQuestionLookup(term, language) {
const exactTerm = cleanWikipediaArticleQuestionTerm(term);
const query = {
exactTerm,
lookupTerm: exactTerm,
contextOriginal: "",
};
const lower = exactTerm.toLowerCase();
if (
(language === "ru" || /[а-яё]/iu.test(exactTerm)) &&
/\s(?:в|на)\s+(?:предложени[еяию]|предложениях|словосочетани[еяию]|словосочетаниях)$/iu.test(lower)
) {
query.lookupTerm = cleanWikipediaArticleQuestionTerm(
exactTerm.replace(/\s(?:в|на)\s+(?:предложени[еяию]|предложениях|словосочетани[еяию]|словосочетаниях)$/iu, ""),
);
query.contextOriginal = "грамматика";
}
if (
(language === "en" || /^[\p{ASCII}\s]+$/u.test(exactTerm)) &&
/\s+in\s+(?:a\s+)?sentences?$/iu.test(lower)
) {
query.lookupTerm = cleanWikipediaArticleQuestionTerm(
exactTerm.replace(/\s+in\s+(?:a\s+)?sentences?$/iu, ""),
);
query.contextOriginal = "grammar";
}
if (language === "hi" || /[\u0900-\u097f]/u.test(exactTerm)) {
const prefix = exactTerm.match(/^(?:वाक्य|वाक्यों)\s+में\s+(.+)$/u);
const suffix = exactTerm.match(/^(.+?)\s+(?:वाक्य|वाक्यों)\s+में$/u);
const match = prefix || suffix;
if (match) {
query.lookupTerm = cleanWikipediaArticleQuestionTerm(match[1]);
query.contextOriginal = "व्याकरण";
}
}
if (language === "zh" || /[\u3400-\u9fff]/u.test(exactTerm)) {
const prefix = exactTerm.match(/^(?:句子中(?:的)?|句子里(?:的)?|句中的)(.+)$/u);
const suffix = exactTerm.match(/^(.+?)(?:在)?句子(?:中|里)$/u);
const match = prefix || suffix;
if (match) {
query.lookupTerm = cleanWikipediaArticleQuestionTerm(match[1]);
query.contextOriginal = "语法";
}
}
return query;
}
// Issue #21: render a percent-encoded URL in its readable IRI form for
// display, while leaving the original encoded form available as the href.
// `decodeURI` keeps reserved URI delimiters (`; / ? : @ & = + $ , #`) intact,
// so query strings are preserved; malformed escapes fall back to the original
// string.
function humanizeUrl(url) {
if (typeof url !== "string" || url.length === 0) return url;
if (!url.includes("%")) return url;
try {
return decodeURI(url);
} catch (_error) {
return url;
}
}
// Render a source URL as a Markdown link [human](encoded) when humanization
// changes anything, or the bare URL otherwise.
function renderSourceLink(source) {
const human = humanizeUrl(source);
return human === source ? source : `[${human}](${source})`;
}
function finalizeConceptBody(body) {
let originalBase = String(body || "")
.trim()
.replace(/[?。.!!,,;:]+$/g, "")
.trim();
if (!originalBase) return null;
let original = originalBase;
let lower = original.toLowerCase();
// Strip trailing "mean"/"stand for" markers shared across English idioms.
// The lowercased view drives matching while the original-case view is kept
// so downstream Wikipedia URL lookups preserve Cyrillic capitalization
// (see docs/case-studies/issue-27/README.md).
for (const suffix of [" mean", " stand for"]) {
if (lower.endsWith(suffix)) {
original = original.slice(0, -suffix.length).trim();
lower = lower.slice(0, -suffix.length).trim();
break;
}
}
if (!lower) return null;
return splitTermAndContext(original, lower);
}
function tokenizeArithmetic(input) {
const tokens = [];
let i = 0;
while (i < input.length) {
const ch = input[i];
if (ch === " " || ch === "\t" || ch === "_" || ch === ",") {
i += 1;
continue;
}
if (ch === "+") {
tokens.push({ kind: "+" });
i += 1;
} else if (ch === "-" || ch === "−") {
tokens.push({ kind: "-" });
i += 1;
} else if (ch === "*" || ch === "×" || ch === "·") {
tokens.push({ kind: "*" });
i += 1;
} else if (ch === "/" || ch === "÷") {
tokens.push({ kind: "/" });
i += 1;
} else if (ch === "%") {
tokens.push({ kind: "%" });
i += 1;
} else if (ch === "^") {
tokens.push({ kind: "^" });
i += 1;
} else if (ch === "(") {
tokens.push({ kind: "(" });
i += 1;
} else if (ch === ")") {
tokens.push({ kind: ")" });
i += 1;
} else if ((ch >= "0" && ch <= "9") || ch === ".") {
let j = i;
while (
j < input.length &&
((input[j] >= "0" && input[j] <= "9") || input[j] === ".")
) {
j += 1;
}
const slice = input.slice(i, j);
const value = Number(slice);
if (Number.isNaN(value)) {
throw new Error("unparseable");
}
tokens.push({ kind: "num", value });
i = j;
} else {
throw new Error("unparseable");
}
}
return tokens;
}
// Issue #386: the spelled-operator and cardinal-number vocabularies are no
// longer literal arrays here. They live in the seed meanings — the
// arithmetic_operation operators (addition, subtraction, multiplication,
// division, modulo) and the cardinal_number digits (zero, один, 三, …) — and
// are read by role through the lexicon, exactly as the Rust solver does
// (contains_word_operator / contains_spelled_arithmetic in src/calculation.rs).
// These role names mirror the constants in src/seed/roles.rs.
const ROLE_ARITHMETIC_OPERATOR_WORD = "arithmetic_operator_word";
const ROLE_CARDINAL_NUMBER_WORD = "cardinal_number_word";
const ROLE_CALCULATION_REQUEST_CUE = "calculation_request_cue";
const ROLE_CALCULATION_RESULT_QUERY_CUE = "calculation_result_query_cue";
const ROLE_POLITENESS_CUE = "politeness_cue";
const ROLE_QUANTITY_CONVERSION_CUE = "quantity_conversion_cue";
const ROLE_CALCULATION_DOMAIN_TERM = "calculation_domain_term";
const ROLE_MATH_FUNCTION_NAME = "math_function_name";
// Issue #386: the spelled digit/operator → value normalization tables, derived
// from the seed at runtime exactly as the Rust solver derives them
// (Lexicon::arithmetic_normalization_tables in src/seed/meanings.rs, materialized
// into src/arithmetic_word_tables.rs for the no_std wasm worker). A cardinal or
// operator meaning carries its script-independent value surface as the one word
// form with no alphabetic character — the numeral "2", the symbol "+" — and every
// spelled surface (any language) maps onto it. Multi-word surfaces ("divided by",
// "разделить на") are returned as `phrases`, rewritten before tokenization and
// ordered longest-first so a phrase applies before any shorter phrase it
// contains; single words ("two", "плюс") are returned as `tokens`, mapped after
// the whitespace split. Cached because the lexicon never changes at runtime.
let cachedArithmeticTables = null;
function arithmeticNormalizationTables() {
if (cachedArithmeticTables) return cachedArithmeticTables;
const isValueSurface = (word) => !/\p{Alphabetic}/u.test(word);
const tokens = [];
const phrases = [];
for (const role of [ROLE_CARDINAL_NUMBER_WORD, ROLE_ARITHMETIC_OPERATOR_WORD]) {
for (const meaning of meaningsWithRole(role)) {
// The value surface is the one word form with no alphabetic character: the
// numeral for a cardinal, the symbol for an operator. Spelled surfaces in
// every language map onto it.
const value = meaning.words.find((word) => isValueSurface(word));
if (value === undefined) continue;
for (const word of meaning.words) {
if (word === value || isValueSurface(word)) continue;
const entry = [word, value];
if (/\s/u.test(word)) phrases.push(entry);
else tokens.push(entry);
}
}
}
const cmpStr = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
const dedupe = (pairs) =>
pairs.filter(
(pair, index) =>
index === 0 ||
pair[0] !== pairs[index - 1][0] ||
pair[1] !== pairs[index - 1][1],
);
// tokens.sort() in Rust orders tuples by surface then value; phrases sort by
// descending code-point count (longest first), then surface ascending.
tokens.sort((a, b) => cmpStr(a[0], b[0]) || cmpStr(a[1], b[1]));
phrases.sort(
(a, b) => [...b[0]].length - [...a[0]].length || cmpStr(a[0], b[0]),
);
cachedArithmeticTables = { tokens: dedupe(tokens), phrases: dedupe(phrases) };
return cachedArithmeticTables;
}
const PERCENT_OF_CURRENCY_CODES = new Map([
["$", "USD"],
["€", "EUR"],
["¥", "JPY"],
["₹", "INR"],
["₽", "RUB"],
]);
const DEFAULT_CURRENCY_RATES = new Map([
["USD:EUR", 0.92],
["USD:GBP", 0.79],
["USD:JPY", 148.5],
["USD:CHF", 0.88],
["USD:CNY", 7.25],
["USD:RUB", 89.5],
["USD:INR", 86.5],
["USD:CLF", 0.022],
["USD:VND", 25810.0],
["USD:KZT", 470.0],
["EUR:USD", 1.087],
["EUR:GBP", 0.86],
["EUR:JPY", 161.5],
["EUR:CHF", 0.96],
["GBP:USD", 1.27],
["GBP:EUR", 1.16],
]);
const USD_RUB_RATE_EXPRESSION = "1 USD in RUB";
// Issue #386: the canonical ISO 4217 code is the recognizer's output, so it
// stays in code; only the recognition vocabulary lives in the seed. Mirrors the
// role -> code mapping the Rust calculator handlers resolve from the same roles.
function currencyCodeForRole(role) {
if (role === ROLE_CURRENCY_USD_REFERENCE) return "USD";
if (role === ROLE_CURRENCY_EUR_REFERENCE) return "EUR";
if (role === ROLE_CURRENCY_RUB_REFERENCE) return "RUB";
return "";
}
// Issue #386: currency vocabulary is seed data, not a hardcoded declension list.
// Walk the three currency reference roles (USD, then EUR, then RUB — the
// original recognizer's priority) and return the ISO code of the first role a
// surface matches. The matching strategy follows the surface's script, the same
// split surfacePresent already makes: Latin surfaces (the ISO codes and English
// terms, enumerated singular and plural) and CJK/Devanagari surfaces match the
// whole token exactly, so unrelated words like "rubbish" are rejected just as
// the original exact-match list rejected them; Cyrillic surfaces are stems
// matched by prefix, so every Russian declension (доллар… , руб…) is caught
// from доллар / руб without listing each inflected form. The calculator regexes
// only ever feed this Latin or Cyrillic tokens.
function currencyCodeFromWord(value) {
const lower = String(value || "").toLowerCase();
if (!lower) return "";
for (const role of [
ROLE_CURRENCY_USD_REFERENCE,
ROLE_CURRENCY_EUR_REFERENCE,
ROLE_CURRENCY_RUB_REFERENCE,
]) {
for (const word of wordsForRole(role)) {
if (!word) continue;
// Cyrillic block is U+0400-U+04FF; Latin sorts below it and CJK/Devanagari
// above, so the first codepoint tells us which matching strategy to use.
const head = word.charCodeAt(0);
const isCyrillic = head >= 0x0400 && head <= 0x04ff;
if (isCyrillic ? lower.startsWith(word) : lower === word) {
return currencyCodeForRole(role);
}
}
}
return "";
}
function defaultCurrencyRate(from, to) {
if (from === to) return 1;
const direct = DEFAULT_CURRENCY_RATES.get(`${from}:${to}`);
if (direct) return direct;
const inverse = DEFAULT_CURRENCY_RATES.get(`${to}:${from}`);
if (inverse) return 1 / inverse;
if (from !== "USD" && to !== "USD") {
const fromUsd = defaultCurrencyRate(from, "USD");
const usdTo = defaultCurrencyRate("USD", to);
if (fromUsd && usdTo) return fromUsd * usdTo;
}
return null;
}
// Issue #386: the trailing currency word in an "N% of M <currency>" expression
// is seed data, not a hardcoded English list. Build the alternation from the
// same three currency reference roles currencyCodeFromWord resolves, so the
// recognizer captures exactly what the resolver understands — the ISO codes,
// the English singular/plural forms, and the Cyrillic/CJK/Devanagari names all
// come straight from the seed instead of the old usd|eur|rub|dollars?… literal.
// Longest-first under the trailing `$` anchor so "dollars" is preferred over
// "dollar"; each surface is regex-escaped. Cached because the seed is immutable
// for the worker's lifetime (matching the lazy, post-init access pattern
// currencyCodeFromWord uses, which keeps the ROLE_* consts out of the TDZ).
let percentOfExpressionRegexCache = null;
function percentOfExpressionRegex() {
if (percentOfExpressionRegexCache) return percentOfExpressionRegexCache;
const surfaces = [];
const seen = new Set();
for (const role of [
ROLE_CURRENCY_USD_REFERENCE,
ROLE_CURRENCY_EUR_REFERENCE,
ROLE_CURRENCY_RUB_REFERENCE,
]) {
for (const word of wordsForRole(role)) {
const surface = String(word || "").toLowerCase();
if (!surface || seen.has(surface)) continue;
seen.add(surface);
surfaces.push(surface);
}
}
surfaces.sort((a, b) => b.length - a.length || (a < b ? -1 : a > b ? 1 : 0));
const alternation = surfaces
.map((surface) => surface.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
.join("|");
percentOfExpressionRegexCache = new RegExp(
`^([+-]?\\d+(?:\\.\\d+)?)\\s*%\\s+of\\s+([$€¥₹₽])?\\s*([+-]?\\d+(?:\\.\\d+)?)(?:\\s*(${alternation}))?$`,
"i",
);
return percentOfExpressionRegexCache;
}
function evaluatePercentOfExpression(expression) {
const match = String(expression || "")
.trim()
.match(percentOfExpressionRegex());
if (!match) return null;
const percent = Number(match[1]);
const amount = Number(match[3]);
if (!Number.isFinite(percent) || !Number.isFinite(amount)) return null;
const currency =
PERCENT_OF_CURRENCY_CODES.get(match[2] || "") ||
currencyCodeFromWord(match[4]);
const result = formatArithmeticResult((amount * percent) / 100);
return currency ? `${result} ${currency}` : result;
}
function evaluateCurrencyConversionExpression(expression) {
const match = String(expression || "")
.trim()
.match(
/^([+-]?\d+(?:[.,]\d+)?)\s+(.+?)\s+(?:in|as|to|в|во|к)\s+(.+)$/iu,
);
if (!match) return null;
const amount = Number(match[1].replace(",", "."));
if (!Number.isFinite(amount)) return null;
const from = currencyCodeFromWord(match[2].trim());
const to = currencyCodeFromWord(match[3].trim());
if (!from || !to) return null;
const rate = defaultCurrencyRate(from, to);
if (!rate) return null;
return `${formatArithmeticResult(amount * rate)} ${to}`;
}
function usdRubRateDetail() {
const rate = defaultCurrencyRate("USD", "RUB");
if (!rate) return "";
return `Exchange rate: 1 USD = ${formatArithmeticResult(rate)} RUB (source: default (hardcoded), date: unknown)`;
}
function evaluateUsdRubRateBasis() {
const wasmResult = wasmEvaluateArithmetic(USD_RUB_RATE_EXPRESSION);
if (wasmResult && wasmResult.ok) {
return {
formatted: wasmResult.value,
backend: "wasm",
detail: usdRubRateDetail(),
};
}
const currencyConversionResult = evaluateCurrencyConversionExpression(
USD_RUB_RATE_EXPRESSION,
);
if (currencyConversionResult === null) return null;
return {
formatted: currencyConversionResult,
backend: "js-currency",
detail: usdRubRateDetail(),
};
}
// Issue #386: recognise the USD/RUB rate-basis question by *meaning*, not by a
// hardcoded per-language word list. The surface forms live once in
// data/seed/meanings-calculator.lino and are queried by semantic role, matched
// as raw substrings — the JS mirror of mentions_role_raw and of
// asks_for_usd_rate_basis in src/solver_handlers/calculator_rate.rs.
//
// A prompt references US dollars (an exchange_rate between currencies AND the
// us_dollar currency) when both currency roles are present.
function mentionsUsdRate(normalized) {
return (
lexiconMentionsRoleSubstring(ROLE_EXCHANGE_RATE_REFERENCE, normalized) &&
lexiconMentionsRoleSubstring(ROLE_CURRENCY_USD_REFERENCE, normalized)
);
}
// A prompt asks what the assistant uses as the basis for a calculation when the
// calculation_basis role is present (the question side of "which rate do you
// use for calculations").
function mentionsRateCalculationBasis(normalized) {
return lexiconMentionsRoleSubstring(ROLE_CALCULATION_BASIS_REFERENCE, normalized);
}
function tryCalculatorRateBasis(normalized, language) {
if (!mentionsUsdRate(normalized) || !mentionsRateCalculationBasis(normalized)) {
return null;
}
const evaluation = evaluateUsdRubRateBasis();
if (!evaluation) {
const content =
language === "ru"
? "Я распознал вопрос о курсе USD/RUB для расчетов, но калькулятор не смог его вычислить."
: "I recognized this as a question about the USD/RUB rate used for calculations, but the calculator could not evaluate it.";
return {
intent: "calculation_error",
content,
confidence: 0.3,
evidence: ["calculation_error:USD/RUB"],
};
}
const calculationBody = `${USD_RUB_RATE_EXPRESSION} = ${evaluation.formatted}`;
let content;
if (language === "ru") {
content = `При расчетах валюты я использую link-calculator. Для USD/RUB он возвращает: ${calculationBody}.`;
} else if (language === "hi") {
content = `मुद्रा गणनाओं के लिए मैं link-calculator का उपयोग करता हूं। USD/RUB के लिए वह लौटाता है: ${calculationBody}.`;
} else if (language === "zh") {
content = `货币计算时我使用 link-calculator。USD/RUB 返回: ${calculationBody}.`;
} else {
content = `For currency calculations I use link-calculator. For USD/RUB it returns: ${calculationBody}.`;
}
if (evaluation.detail) {
let details = "Calculator rate details";
if (language === "ru") {
details = "Детали курса от калькулятора";
} else if (language === "hi") {
details = "कैलकुलेटर दर विवरण";
} else if (language === "zh") {
details = "计算器汇率详情";
}
content += `\n\n${details}: ${evaluation.detail}`;
}
return {
intent: "calculation",
content,
confidence: 1.0,
evidence: [
`calculation:${calculationBody}`,
`calculation_backend:${evaluation.backend}`,
"calculation_rate_basis:USD/RUB",
],
};
}
// Rewrite "N% of M" percentage-of phrases into explicit arithmetic the parser
// can evaluate: "8% of 500" -> "( 8 * 500 / 100 )". Mirrors the Rust
// `rewrite_percent_of` helper so the JS fallback agrees with the WASM worker:
// "55 * 8% of 500" evaluates to 2200 (issue #334). A bare `%` not followed by
// "of" is left untouched so it still parses as the modulo operator.
function rewritePercentOf(expression) {
const isNumber = (token) => token.length > 0 && /^[0-9.]+$/.test(token);
const tokens = String(expression).split(/\s+/).filter(Boolean);
const out = [];
let index = 0;
while (index < tokens.length) {
let percent = null;
let consumed = 0;
const token = tokens[index];
if (token.endsWith("%") && isNumber(token.slice(0, -1))) {
percent = token.slice(0, -1);
consumed = 1;
} else if (isNumber(token) && tokens[index + 1] === "%") {
percent = token;
consumed = 2;
}
const after = index + consumed;
if (
percent !== null &&
tokens[after] === "of" &&
tokens[after + 1] !== undefined &&
isNumber(tokens[after + 1])
) {
out.push("(", percent, "*", tokens[after + 1], "/", "100", ")");
index = after + 2;
continue;
}
out.push(token);
index += 1;
}
return out.join(" ");
}
function normalizeArithmeticWords(expression) {
const { tokens, phrases } = arithmeticNormalizationTables();
const lower = String(expression).toLowerCase();
// Multi-word operator phrases first, longest-first (the table is pre-sorted),
// each padded with spaces so it only rewrites on a token boundary — exactly as
// the Rust normalize_expression does before it splits on whitespace.
let padded = ` ${lower} `;
for (const [phrase, value] of phrases) {
padded = padded.replaceAll(` ${phrase} `, ` ${value} `);
}
const tokenMap = new Map(tokens);
const mapped = padded
.split(/\s+/)
.filter(Boolean)
.map((token) => tokenMap.get(token) || token)
.join(" ");
return rewritePercentOf(mapped);
}
function evaluateArithmetic(expression) {
const normalized = normalizeArithmeticWords(expression);
const tokens = tokenizeArithmetic(normalized);
if (tokens.length === 0) {
throw new Error("empty");
}
let cursor = 0;
const peek = () => tokens[cursor];
const advance = () => tokens[cursor++];
function parsePrimary() {
const tok = advance();
if (!tok) throw new Error("unparseable");
if (tok.kind === "num") return tok.value;
if (tok.kind === "(") {
const inner = parseAdditive();
const close = advance();
if (!close || close.kind !== ")") throw new Error("unbalanced");
return inner;
}
throw new Error("unparseable");
}
function parsePower() {
let left = parsePrimary();
const tok = peek();
if (tok && tok.kind === "^") {
advance();
left = Math.pow(left, parseUnary());
if (!Number.isFinite(left)) throw new Error("overflow");
}
return left;
}
function parseUnary() {
const tok = peek();
if (tok && tok.kind === "-") {
advance();
return -parseUnary();
}
if (tok && tok.kind === "+") {
advance();
return parseUnary();
}
return parsePower();
}
function parseMultiplicative() {
let left = parseUnary();
while (true) {
const tok = peek();
if (!tok || (tok.kind !== "*" && tok.kind !== "/" && tok.kind !== "%")) {
break;
}
const op = tok.kind;
advance();
const right = parseUnary();
if (op === "*") {
left = left * right;
} else if (right === 0) {
throw new Error("division by zero");
} else if (op === "/") {
left = left / right;
} else {
left = left % right;
}
if (!Number.isFinite(left)) throw new Error("overflow");
}
return left;
}
function parseAdditive() {
let left = parseMultiplicative();
while (true) {
const tok = peek();
if (!tok || (tok.kind !== "+" && tok.kind !== "-")) break;
const isPlus = tok.kind === "+";
advance();
const right = parseMultiplicative();
left = isPlus ? left + right : left - right;
if (!Number.isFinite(left)) throw new Error("overflow");
}
return left;
}
const value = parseAdditive();
if (cursor !== tokens.length) {
throw new Error("unparseable");
}
return value;
}
function formatArithmeticResult(value) {
if (!Number.isFinite(value)) return "non-finite";
if (Math.abs(value % 1) === 0 && Math.abs(value) < 1e15) {
return value.toFixed(0);
}
const rendered = value.toFixed(10);
const trimmed = rendered.replace(/0+$/, "").replace(/\.$/, "");
return trimmed === "" || trimmed === "-" ? "0" : trimmed;
}
function parseLinearExpression(input) {
let position = 0;
let variable = null;
function peek() {
return input[position] || "";
}
function skipWhitespace() {
while (/\s/.test(peek())) position += 1;
}
function consume(expected) {
if (peek() === expected) {
position += 1;
return true;
}
return false;
}
function constant(value) {
return { coefficient: 0, constant: value };
}
function variableValue() {
return { coefficient: 1, constant: 0 };
}
function hasVariable(value) {
return Math.abs(value.coefficient) > Number.EPSILON;
}
function add(left, right) {
return {
coefficient: left.coefficient + right.coefficient,
constant: left.constant + right.constant,
};
}
function subtract(left, right) {
return {
coefficient: left.coefficient - right.coefficient,
constant: left.constant - right.constant,
};
}
function multiply(left, right) {
if (hasVariable(left) && hasVariable(right)) {
throw new Error("non-linear equation");
}
if (hasVariable(left)) {
return {
coefficient: left.coefficient * right.constant,
constant: left.constant * right.constant,
};
}
if (hasVariable(right)) {
return {
coefficient: right.coefficient * left.constant,
constant: right.constant * left.constant,
};
}
return constant(left.constant * right.constant);
}
function divide(left, right) {
if (hasVariable(right)) throw new Error("variable denominator");
if (Math.abs(right.constant) <= Number.EPSILON) throw new Error("division by zero");
return {
coefficient: left.coefficient / right.constant,
constant: left.constant / right.constant,
};
}
function parseExpression() {
let value = parseTerm();
while (true) {
skipWhitespace();
if (consume("+")) {
value = add(value, parseTerm());
} else if (consume("-") || consume("−")) {
value = subtract(value, parseTerm());
} else {
return value;
}
}
}
function parseTerm() {
let value = parseFactor();
while (true) {
skipWhitespace();
if (consume("*") || consume("×") || consume("·")) {
value = multiply(value, parseFactor());
} else if (consume("/") || consume("÷")) {
value = divide(value, parseFactor());
} else {
return value;
}
}
}
function parseFactor() {
skipWhitespace();
if (consume("+")) return parseFactor();
if (consume("-") || consume("−")) {
const value = parseFactor();
return { coefficient: -value.coefficient, constant: -value.constant };
}
if (consume("(")) {
const value = parseExpression();
skipWhitespace();
if (!consume(")")) throw new Error("unbalanced parentheses");
return value;
}
if (/[0-9.]/.test(peek())) return parseNumber();
if (/\p{L}/u.test(peek())) return parseVariable();
throw new Error("expression could not be parsed");
}
function parseNumber() {
const start = position;
let hasDigit = false;
let hasDot = false;
while (/[0-9.]/.test(peek())) {
if (peek() === ".") {
if (hasDot) break;
hasDot = true;
} else {
hasDigit = true;
}
position += 1;
}
if (!hasDigit) throw new Error("expression could not be parsed");
const value = Number(input.slice(start, position));
if (!Number.isFinite(value)) throw new Error("expression could not be parsed");
return constant(value);
}
function parseVariable() {
const start = position;
while (/[\p{L}_]/u.test(peek())) position += 1;
const name = input.slice(start, position);
if (!name) throw new Error("expression could not be parsed");
if (variable && variable !== name) throw new Error("multiple variables");
variable = name;
return variableValue();
}
const value = parseExpression();
skipWhitespace();
if (position !== input.length) throw new Error("expression could not be parsed");
return { value, variable };
}
function solveLinearEquation(expression) {
const parts = String(expression).split("=");
if (parts.length !== 2) throw new Error("expression could not be parsed");
const left = parseLinearExpression(parts[0]);
const right = parseLinearExpression(parts[1]);
const variable = left.variable || right.variable;
if (!variable || (left.variable && right.variable && left.variable !== right.variable)) {
throw new Error("expression could not be parsed");
}
const coefficient = left.value.coefficient - right.value.coefficient;
if (Math.abs(coefficient) <= Number.EPSILON) {
throw new Error("expression could not be parsed");
}
const value = (right.value.constant - left.value.constant) / coefficient;
return `${variable} = ${formatArithmeticResult(value)}`;
}
function hasArithmeticWordOperator(expression) {
// Issue #386: the spelled operators come from the arithmetic_operation
// meanings (addition, subtraction, multiplication, division, modulo) by
// role, not a literal array. Each surface is matched as a whole token — CJK
// surfaces as a substring — the boundary contract the former space-padded
// `.includes` checks enforced. The match goes through the *spelled* query so
// a meaning's value surface (the operator symbol "+") stays neutral here and
// is caught by the symbol parser instead, mirroring contains_word_operator in
// src/calculation.rs.
return lexiconMentionsRoleSpelled(
ROLE_ARITHMETIC_OPERATOR_WORD,
String(expression).toLowerCase(),
);
}
function hasSpelledArithmetic(expression) {
// Issue #386: the cardinal number words come from the cardinal_number
// meanings by role, not a literal array. Pure-numeral surfaces ("10") are
// skipped — a bare digit run is handled by the numeric parser — and each
// spelled surface is matched as a space-bounded whole token, mirroring
// contains_spelled_arithmetic in src/calculation.rs.
const lower = ` ${String(expression).toLowerCase()} `;
const hasNumberWord = roleWordForms(ROLE_CARDINAL_NUMBER_WORD).some((form) => {
if (
[...form.text].every(
(character) => character >= "0" && character <= "9",
)
) {
return false;
}
return lower.includes(` ${form.text} `);
});
return hasNumberWord && hasArithmeticWordOperator(expression);
}
// Issue #334 step 2: the website demo's second agent step was "calculate the
// 10th Fibonacci number and multiply it by 8% of 500. Show me the code and the
// final result." It is not a calculator expression, but it reduces to one once
// the symbolic Fibonacci reference is resolved (F(10) = 55), the spelled-out
// operator is rewritten to `*`, and the trailing instruction sentence is
// dropped — yielding `55 * 8% of 500` = 2200. The helpers below mirror
// `fibonacci_value`, `parse_ordinal`, `bare_word`, `resolve_fibonacci_references`,
// `split_sentences` and `normalize_word_problem` in `src/calculation.rs`.
function fibonacciValue(n) {
if (n === 0) return 0;
let previous = 0;
let current = 1;
for (let step = 1; step < n; step += 1) {
const next = previous + current;
previous = current;
current = next;
}
return current;
}
const ORDINAL_WORDS = {
first: 1,
second: 2,
third: 3,
fourth: 4,
fifth: 5,
sixth: 6,
seventh: 7,
eighth: 8,
ninth: 9,
tenth: 10,
};
// Lowercased, punctuation-trimmed view of a token for keyword comparisons.
function trimNonAlnum(token) {
return String(token || "")
.replace(/^[^\p{L}\p{N}]+/u, "")
.replace(/[^\p{L}\p{N}]+$/u, "");
}
function bareWord(token) {
return trimNonAlnum(token).toLowerCase();
}
// Parse a leading ordinal/cardinal token such as "10th", "10", "3rd" or the
// spelled-out "tenth" into its numeric value. Returns null for anything else.
function parseOrdinal(token) {
const trimmed = trimNonAlnum(token);
if (!trimmed) return null;
const digits = (trimmed.match(/^[0-9]+/) || [""])[0];
if (digits) {
const suffix = trimmed.slice(digits.length);
if (suffix === "" || ["st", "nd", "rd", "th"].includes(suffix)) {
return Number.parseInt(digits, 10);
}
return null;
}
const value = ORDINAL_WORDS[trimmed.toLowerCase()];
return value === undefined ? null : value;
}
// Replace "(the) N-th Fibonacci number" references with their numeric value.
function resolveFibonacciReferences(text) {
if (!text.toLowerCase().includes("fibonacci")) return text;
const tokens = text.split(/\s+/).filter(Boolean);
const out = [];
let index = 0;
while (index < tokens.length) {
const n = parseOrdinal(tokens[index]);
if (
n !== null &&
tokens[index + 1] !== undefined &&
bareWord(tokens[index + 1]) === "fibonacci"
) {
// Drop a determiner we already emitted ("the 10th" -> "55").
if (out.length > 0 && bareWord(out[out.length - 1]) === "the") {
out.pop();
}
out.push(String(fibonacciValue(n)));
index += 2;
// Absorb a trailing "number" / "term" / "sequence" noun.
const noun = tokens[index] !== undefined ? bareWord(tokens[index]) : "";
if (noun === "number" || noun === "term" || noun === "sequence") {
index += 1;
}
continue;
}
out.push(tokens[index]);
index += 1;
}
return out.join(" ");
}
// Split text into sentences on a period that ends a sentence (followed by
// whitespace or the end of the string). A period flanked by digits ("3.14") is
// kept inside its sentence so decimals are never broken apart.
function splitSentences(text) {
const chars = Array.from(String(text || ""));
const sentences = [];
let current = "";
for (let i = 0; i < chars.length; i += 1) {
const ch = chars[i];
const next = chars[i + 1];
if (ch === "." && (next === undefined || /\s/.test(next))) {
const sentence = current.trim();
if (sentence) sentences.push(sentence);
current = "";
continue;
}
current += ch;
}
const last = current.trim();
if (last) sentences.push(last);
return sentences;
}
function wordProblemWords(sentence) {
return String(sentence || "")
.split(/[^\p{L}\p{N}]+/u)
.filter(Boolean)
.map((token) => token.toLowerCase());
}
function parseWordProblemInteger(token) {
const text = String(token || "").toLowerCase();
if (/^[+-]?\d+$/.test(text)) return Number.parseInt(text, 10);
const numbers = {
zero: 0,
one: 1,
a: 1,
an: 1,
two: 2,
three: 3,
four: 4,
five: 5,
six: 6,
seven: 7,
eight: 8,
nine: 9,
ten: 10,
eleven: 11,
twelve: 12,
thirteen: 13,
fourteen: 14,
fifteen: 15,
sixteen: 16,
seventeen: 17,
eighteen: 18,
nineteen: 19,
twenty: 20,
};
return Object.prototype.hasOwnProperty.call(numbers, text) ? numbers[text] : null;
}
function canonicalBoxId(token) {
const cleaned = trimNonAlnum(token).toUpperCase();
return cleaned && cleaned.length <= 3 ? cleaned : "";
}
function parseDeclaredBoxCount(words) {
if (
words.length >= 4 &&
words[0] === "i" &&
words[1] === "have" &&
(words[3] === "box" || words[3] === "boxes")
) {
const count = parseWordProblemInteger(words[2]);
return Number.isInteger(count) ? count : null;
}
return null;
}
function parseBoxRule(words) {
let index = words[0] === "if" ? 1 : 0;
if (words[index] !== "box" || words[index + 2] !== "has") return null;
const target = canonicalBoxId(words[index + 1]);
if (!target) return null;
index += 3;
if (
words[index] === "twice" &&
words[index + 1] === "as" &&
words[index + 2] === "many" &&
words[index + 4] === "as" &&
words[index + 5] === "box"
) {
const source = canonicalBoxId(words[index + 6]);
if (!source) return null;
return {
target,
rule: { kind: "multiple", factor: 2, source },
item: words[index + 3] || "",
};
}
const value = parseWordProblemInteger(words[index]);
if (!Number.isInteger(value)) return null;
if (
words[index + 1] === "more" &&
words[index + 3] === "than" &&
words[index + 4] === "box"
) {
const source = canonicalBoxId(words[index + 5]);
if (!source) return null;
return {
target,
rule: { kind: "add", source, addend: value },
item: words[index + 2] || "",
};
}
return {
target,
rule: { kind: "known", value },
item: words[index + 1] || "",
};
}
function resolveBoxValue(id, rules, memo, stack, reasoningSteps, resultLabel) {
if (memo.has(id)) return memo.get(id);
if (stack.includes(id)) return null;
const rule = rules.get(id);
if (!rule) return null;
stack.push(id);
let value = null;
if (rule.kind === "known") {
value = rule.value;
reasoningSteps.push(`Box ${id} = ${value} ${resultLabel}.`);
} else if (rule.kind === "multiple") {
const sourceValue = resolveBoxValue(
rule.source,
rules,
memo,
stack,
reasoningSteps,
resultLabel,
);
if (!Number.isFinite(sourceValue)) return null;
value = sourceValue * rule.factor;
reasoningSteps.push(
`Box ${id} = ${rule.factor} * ${sourceValue} = ${value} ${resultLabel}.`,
);
} else if (rule.kind === "add") {
const sourceValue = resolveBoxValue(
rule.source,
rules,
memo,
stack,
reasoningSteps,
resultLabel,
);
if (!Number.isFinite(sourceValue)) return null;
value = sourceValue + rule.addend;
reasoningSteps.push(
`Box ${id} = ${sourceValue} + ${rule.addend} = ${value} ${resultLabel}.`,
);
}
stack.pop();
if (!Number.isFinite(value)) return null;
memo.set(id, value);
return value;
}
function normalizeBoxTotalProblem(text) {
const lower = String(text || "").toLowerCase();
if (
!lower.includes("box") ||
!lower.includes("how many") ||
!lower.includes("total") ||
(!lower.includes("twice as many") &&
!(lower.includes("more") && lower.includes("than")))
) {
return null;
}
let declaredCount = null;
let resultLabel = "";
const rules = new Map();
for (const sentence of splitSentences(text)) {
const words = wordProblemWords(sentence);
if (words.length === 0) continue;
if (declaredCount === null) {
declaredCount = parseDeclaredBoxCount(words);
}
const parsed = parseBoxRule(words);
if (!parsed) continue;
if (parsed.item && parsed.item !== "box" && parsed.item !== "boxes") {
resultLabel = parsed.item;
}
rules.set(parsed.target, parsed.rule);
}
if (rules.size < 2) return null;
if (declaredCount !== null && rules.size < declaredCount) return null;
const label = resultLabel || "items";
const memo = new Map();
const reasoningSteps = [];
const ids = Array.from(rules.keys()).sort();
for (const id of ids) {
if (
!Number.isFinite(
resolveBoxValue(id, rules, memo, [], reasoningSteps, label),
)
) {
return null;
}
}
const values = ids.map((id) => memo.get(id));
if (values.some((value) => !Number.isFinite(value))) return null;
const expression = values.join(" + ");
reasoningSteps.push(`Total = ${expression} ${label}.`);
return { expression, reasoningSteps, resultLabel: label };
}
// Rewrite a natural-language "word problem" into a calculator expression, or
// return null when no rewrite applies so callers fall through unchanged.
function normalizeWordProblemDetailed(expression) {
const trimmed = String(expression || "").trim();
if (!trimmed) return null;
const boxProblem = normalizeBoxTotalProblem(trimmed);
if (boxProblem) return boxProblem;
// Keep only sentence fragments that carry arithmetic content, dropping pure
// instruction clauses such as "Show me the code and the final result".
const arithmetic = splitSentences(trimmed).filter(
(sentence) => sentence && (/[0-9]/.test(sentence) || sentence.includes("%")),
);
if (arithmetic.length === 0) return null;
let working = resolveFibonacciReferences(arithmetic.join(". "));
// Rewrite spelled-out operators the calculator does not accept. Longer phrases
// come first so "and multiply it by" wins over "multiply by".
const operatorPhrases = [
[" and multiply it by ", " * "],
[" and multiply by ", " * "],
[" multiply it by ", " * "],
[" multiplied by ", " * "],
[" multiply by ", " * "],
[" and divide it by ", " / "],
[" and divide by ", " / "],
[" divide it by ", " / "],
[" divided by ", " / "],
[" divide by ", " / "],
];
for (const [phrase, symbol] of operatorPhrases) {
const position = working.toLowerCase().indexOf(phrase);
if (position !== -1) {
working =
working.slice(0, position) + symbol + working.slice(position + phrase.length);
}
}
working = working.split(/\s+/).filter(Boolean).join(" ");
if (!working || working.toLowerCase() === trimmed.toLowerCase()) return null;
return { expression: working, reasoningSteps: [], resultLabel: "" };
}
// Issue #386: the calculator-domain signal set is rebuilt from three seed roles
// instead of a 62-entry literal array, so the router reasons over the same
// self-describing lexicon every other handler reads. Each surface is shaped into
// a match pattern by its script and role, mirroring calculator_domain_signals in
// src/calculation.rs:
//
// - math_function_name ("sqrt", "sin", "логарифм", "对数", …): an ASCII name gains
// only a leading space so it still fires when glued to its argument's
// parenthesis ("sqrt(16)"); a non-ASCII name matches as a raw substring because
// those scripts have no inter-word spaces.
// - calculation_domain_term (currencies and measurement units: "usd", "kg",
// "ms", "доллар", "公斤", "месяцев", …): an ASCII surface matches as a whole
// token (leading and trailing space) so a short code like "ms" never fires
// inside "items" nor "mb" inside "number"; a non-ASCII surface matches as a raw
// substring.
// - quantity_conversion_cue, CJK members only ("换成", …): the Chinese conversion
// verbs match as raw substrings; the Latin cues ("to", "into") are excluded
// here because they are far too common to mark a calculation on their own.
//
// The caller pads the lowercased expression with surrounding spaces and tests
// includes against each signal, so the set — not its order — decides.
function calculatorDomainSignals() {
const signals = [];
for (const surface of wordsForRole(ROLE_MATH_FUNCTION_NAME)) {
signals.push(isAsciiText(surface) ? ` ${surface}` : surface);
}
for (const surface of wordsForRole(ROLE_CALCULATION_DOMAIN_TERM)) {
signals.push(isAsciiText(surface) ? ` ${surface} ` : surface);
}
for (const surface of wordsForRole(ROLE_QUANTITY_CONVERSION_CUE)) {
if (containsCjk(surface)) {
signals.push(surface);
}
}
return signals;
}
function extractArithmeticExpression(prompt) {
const trimmed = String(prompt || "").trim();
if (!trimmed) return null;
const interpretations = [];
// Issue #386: the leading calculation cues come from the calculation_request
// meaning by role, not a literal array. Each bare surface is rebuilt into a
// strip prefix following its script — space-delimited scripts gain a trailing
// space so a cue strips only on a word boundary ("calculate" never eats the
// start of "calculated"), while CJK surfaces strip as-is because those
// scripts have no inter-word spaces. wordsForRole preserves declaration
// order, and the Chinese cues are stored longest first, so a more specific
// cue strips before a shorter one it contains. Mirrors
// strip_calculation_wrappers in src/calculation.rs.
const prefixes = wordsForRole(ROLE_CALCULATION_REQUEST_CUE).map((surface) =>
containsCjk(surface) ? surface : `${surface} `,
);
let working = trimmed;
let changed = true;
while (changed) {
changed = false;
const stripped = stripKnownPrefix(working, prefixes);
if (stripped) {
working = stripped.value;
if (stripped.interpretation) interpretations.push(stripped.interpretation);
changed = true;
}
}
working = working.replace(/[?.!]+$/g, "").trim();
// Issue #386: the trailing calculation cues come from the
// calculation_result_query and politeness meanings by role, not a literal
// array of regexes. Each surface is rebuilt into a strip suffix following its
// script — CJK surfaces strip as-is because those scripts have no inter-word
// spaces; a pure-symbol surface like the equals sign strips both bare and on a
// word boundary (so a compact "2*2+2=" is recognised); every other surface
// gains a leading space so the cue strips only on a word boundary. Mirrors
// calculation_wrapper_suffixes in src/calculation.rs.
const suffixes = [];
for (const role of [ROLE_CALCULATION_RESULT_QUERY_CUE, ROLE_POLITENESS_CUE]) {
for (const surface of wordsForRole(role)) {
if (containsCjk(surface)) {
suffixes.push(surface);
} else if (!/\p{L}/u.test(surface)) {
suffixes.push(` ${surface}`);
suffixes.push(surface);
} else {
suffixes.push(` ${surface}`);
}
}
}
changed = true;
while (changed) {
changed = false;
for (const suffix of suffixes) {
// Mirror strip_suffix_case_insensitive in src/calculation.rs: a
// case-insensitive endsWith followed by trimming the trailing whitespace.
if (working.toLowerCase().endsWith(suffix)) {
working = working.slice(0, working.length - suffix.length).trim();
changed = true;
break;
}
}
}
if (!working) return null;
// Issue #334 step 2: rewrite a natural-language word problem into a calculator
// expression ("the 10th Fibonacci number and multiply it by 8% of 500. Show me
// the code ..." -> "55 * 8% of 500") before the symbolic checks below run.
const wordProblem = normalizeWordProblemDetailed(working);
let reasoningSteps = [];
let resultLabel = "";
if (wordProblem) {
working = wordProblem.expression;
reasoningSteps = Array.isArray(wordProblem.reasoningSteps)
? wordProblem.reasoningSteps
: [];
resultLabel = wordProblem.resultLabel || "";
}
const workingLower = working.toLowerCase();
const hasLetter = /\p{L}/u.test(working);
const hasSymbolic = /[+*/%^=×·÷−$€¥₹₽]/.test(working) || (!hasLetter && /-/.test(working));
const hasWordOperator = hasArithmeticWordOperator(working);
const hasSpelled = hasSpelledArithmetic(working);
const hasPercentOf = evaluatePercentOfExpression(working) !== null;
const hasWord =
hasWordOperator ||
calculatorDomainSignals().some((signal) => ` ${workingLower} `.includes(signal));
const hasDigit = /[0-9]/.test(working);
if (!hasDigit && !hasSpelled) return null;
if (!hasSymbolic && !hasWord && hasLetter) return null;
const extracted = { expression: working, interpretations, reasoningSteps, resultLabel };
if (hasPercentOf) return extracted;
if (evaluateCurrencyConversionExpression(working) !== null) {
return extracted;
}
const allowed = /^[0-9+\-*/%().=\s_×·÷−,a-zA-Z]+$/;
if (!allowed.test(working) && !hasWordOperator) return null;
return extracted;
}
function extractFencedBlock(text, languages) {
const fence = "```";
let cursor = 0;
while (true) {
const open = text.indexOf(fence, cursor);
if (open === -1) return null;
const infoStart = open + fence.length;
const newlineRel = text.indexOf("\n", infoStart);
const infoEnd = newlineRel === -1 ? text.length : newlineRel;
const info = text.slice(infoStart, infoEnd).trim().toLowerCase();
const bodyStart = Math.min(infoEnd + 1, text.length);
const closeRel = text.indexOf(fence, bodyStart);
if (closeRel === -1) return null;
const body = text.slice(bodyStart, closeRel).replace(/\n+$/, "");
if (info === "" || languages.some((lang) => info === lang)) {
return body;
}
cursor = closeRel + fence.length;
}
}
function extractJavaScriptProgram(prompt) {
const lower = String(prompt || "").toLowerCase();
const asksToRun =
lower.includes("run this javascript") ||
lower.includes("run this js") ||
lower.includes("execute this javascript") ||
lower.includes("execute this js") ||
lower.includes("run the following javascript") ||
lower.includes("run the following js") ||
lower.includes("evaluate this javascript") ||
lower.includes("evaluate this js");
if (!asksToRun) return null;
const fenced = extractFencedBlock(prompt, ["javascript", "js"]);
if (fenced !== null) return fenced;
const backticks = prompt.match(/`([^`]+)`/);
if (backticks) return backticks[1];
const quoted = prompt.match(/"([^"]+)"/);
return quoted ? quoted[1] : null;
}
// Look up an intent route by id (e.g. "intent_greeting"). Returns `null`
// when the routing table is empty (no `.lino` seed) so callers can decide
// whether to fall back to legacy hardcoded matching.
function findIntentRoute(id) {
if (!INTENT_ROUTING || !Array.isArray(INTENT_ROUTING.intents)) return null;
for (const route of INTENT_ROUTING.intents) {
if (route && route.id === id) return route;
}
return null;
}
function tokensOf(normalized) {
return normalized ? normalized.split(/\s+/).filter(Boolean) : [];
}
function tokenContains(normalized, expected) {
return tokensOf(normalized).includes(String(expected || ""));
}
// Match a normalized prompt against an intent route using the same
// semantics as `src/engine.rs::matches_intent_route`:
// - `keywords` / `phrases`: exact whole-prompt match
// - `tokens`: any whitespace-separated token equals the value
// - `combos`: every combo entry must appear as a token
function matchesIntentRoute(normalized, rawPrompt, id) {
const route = findIntentRoute(id);
if (!route) return false;
const fromWasm = wasmMatchIntentRoute(normalized, rawPrompt, route);
if (fromWasm !== null) return fromWasm;
const raw = String(rawPrompt || "")
.toLowerCase()
.replace(/[?。.!!,,;:]+$/g, "")
.trim();
if (route.keywords && route.keywords.some((kw) => kw === normalized || kw === raw)) {
return true;
}
if (route.phrases && route.phrases.some((ph) => ph === normalized || ph === raw)) {
return true;
}
if (route.tokens && route.tokens.some((tok) => tokenContains(normalized, tok))) {
return true;
}
if (
route.combos &&
route.combos.some(
(combo) =>
Array.isArray(combo) &&
combo.length > 0 &&
combo.every((tok) => tokenContains(normalized, tok)),
)
) {
return true;
}
return false;
}
function isIdentityPrompt(normalized, rawPrompt) {
if (repositoryFromPrompt(rawPrompt)) return false;
return matchesIntentRoute(normalized, rawPrompt, "intent_identity");
}
function isAssistantNamePrompt(normalized, rawPrompt) {
return matchesIntentRoute(normalized, rawPrompt, "intent_assistant_name");
}
function isGreetingPrompt(normalized, rawPrompt) {
return matchesIntentRoute(normalized, rawPrompt, "intent_greeting");
}
function isFarewellPrompt(normalized, rawPrompt) {
return matchesIntentRoute(normalized, rawPrompt, "intent_farewell");
}
function isTestStatusPrompt(normalized, rawPrompt) {
return matchesIntentRoute(normalized, rawPrompt, "intent_test_status");
}
function isCourtesyResponsePrompt(normalized, rawPrompt) {
return matchesIntentRoute(normalized, rawPrompt, "intent_courtesy_response");
}
function isPunctuationOnlyPrompt(prompt) {
const trimmed = String(prompt || "").trim();
return /^[.!?…。?!]+$/.test(trimmed);
}
function stableBehaviorRuleId(prefix, value) {
const fromWasm = wasmStableId(prefix, value);
if (fromWasm) return fromWasm;
let hash = 0xcbf29ce484222325n;
const sourceBytes = new TextEncoder().encode(String(value || ""));
for (const byte of sourceBytes) {
hash ^= BigInt(byte);
hash = BigInt.asUintN(64, hash * 0x100000001b3n);
}
return `${prefix}_${hash.toString(16).padStart(16, "0")}`;
}
function extractQuotedPhrase(text) {
const source = String(text || "");
const pairs = [
['"', '"'],
["'", "'"],
["`", "`"],
["«", "»"],
];
for (const [open, close] of pairs) {
const start = source.indexOf(open);
if (start === -1) continue;
const end = source.indexOf(close, start + open.length);
if (end !== -1) return source.slice(start + open.length, end);
}
return null;
}
// Issue #386 translation roles — mirror the ROLE_TRANSLATION_* constants in
// src/seed/roles.rs. Each role's slot-marked surface words live in
// data/seed/meanings-translation.lino (embedded in MEANINGS_LINO above); the
// detection and extraction helpers ask the lexicon for those forms by meaning,
// slot, and script instead of hardcoding a per-language phrase list. This is
// the JS mirror of src/translation/language_markers.rs and
// src/translation/prompt.rs.
const ROLE_TRANSLATION_SOURCE_MARKER = "translation_source_marker";
const ROLE_TRANSLATION_TARGET_MARKER = "translation_target_marker";
const ROLE_TRANSLATION_TARGET_DIRECTION = "translation_target_direction";
const ROLE_TRANSLATION_UNQUOTED_FRAME = "translation_unquoted_frame";
const ROLE_TRANSLATION_INTO_MARKER = "translation_into_marker";
const ROLE_TRANSLATION_OBJECT_MARKER = "translation_object_marker";
// The ISO 639-1 code of the language_* meaning that defines a marker. Mirrors
// language_code in src/translation/language_markers.rs: the surface *names* of
// each language live in the seed; only this slug -> code bridge stays in code.
function translationLanguageCode(meaning) {
for (const slug of meaning.definedBy) {
if (slug === "language_english") return "en";
if (slug === "language_russian") return "ru";
if (slug === "language_hindi") return "hi";
if (slug === "language_chinese") return "zh";
}
return null;
}
// The first marker meaning of `role` (in declaration order) any of whose
// surface words is a substring of `normalized` reports its language. Plain
// substring matching — not the boundary-aware surfacePresent — is intentional
// and mirrors detect_marker_language in src/translation/language_markers.rs: a
// CJK marker like 从中文 has no word spaces, and a Cyrillic marker like
// "с английского" must match inside a longer sentence.
function detectTranslationMarkerLanguage(role, normalized) {
for (const meaning of meaningsWithRole(role)) {
if (meaning.words.some((word) => normalized.includes(word))) {
const code = translationLanguageCode(meaning);
if (code) return code;
}
}
return null;
}
let cachedTranslationMarkers = null;
// Project the translation-extraction markers out of the meaning lexicon, once.
// Each field is a semantic role narrowed to the slot and script its strategy
// needs, in declaration order — the code names a role and a shape, never a
// surface word. Mirrors markers() in src/translation/prompt.rs.
function translationMarkers() {
if (cachedTranslationMarkers) return cachedTranslationMarkers;
const scriptForms = (role, script) =>
roleWordForms(role)
.filter((form) => script(form.text))
.map((form) => form.text);
const bareScriptForms = (role, script) =>
roleWordForms(role)
.filter((form) => form.slot === "bare" && script(form.text))
.map((form) => form.before);
cachedTranslationMarkers = {
circumfixFrames: roleWordForms(ROLE_TRANSLATION_UNQUOTED_FRAME)
.filter((form) => form.slot === "circumfix")
.map((form) => [form.before, form.after]),
hindiVerbStems: bareScriptForms(
ROLE_TRANSLATION_UNQUOTED_FRAME,
containsDevanagari,
),
hindiTargetMarkers: scriptForms(
ROLE_TRANSLATION_INTO_MARKER,
containsDevanagari,
),
hindiObjectMarkers: scriptForms(
ROLE_TRANSLATION_OBJECT_MARKER,
containsDevanagari,
),
chineseCommandPrefixes: scriptForms(
ROLE_TRANSLATION_OBJECT_MARKER,
containsCjk,
),
chineseCommandMarkers: scriptForms(
ROLE_TRANSLATION_INTO_MARKER,
containsCjk,
),
chineseTranslatePrefixes: bareScriptForms(
ROLE_TRANSLATION_UNQUOTED_FRAME,
containsCjk,
),
chineseTargetMarkers: scriptForms(
ROLE_TRANSLATION_TARGET_DIRECTION,
containsCjk,
),
};
return cachedTranslationMarkers;
}
// Issue #216: extract the surface from unquoted translation prompts such as
// `translate apple to russian`, `переведи яблоко на английский`,
// `apple का हिंदी में अनुवाद करो`, or `把 apple 翻译成中文`. Returns null when
// the prompt already contains a quoted fragment or does not match a supported
// verb + target-marker pattern. Issue #386: every marker is now projected from
// the lexicon by role/slot/script — translationMarkers() above — so this code
// names the *shape* of each frame, never its words.
function extractUnquotedTranslationSurface(text) {
const source = String(text || "").trim();
const trimmed = source.replace(/[.!?。]+$/u, "");
const lower = trimmed.toLowerCase();
const markers = translationMarkers();
for (const [prefix, marker] of markers.circumfixFrames) {
const extracted = extractBetweenPrefixAndMarker(
trimmed,
lower,
prefix,
marker,
);
if (extracted) return extracted;
}
const hindi = extractHindiUnquotedTranslationSurface(trimmed, lower);
if (hindi) return hindi;
return extractChineseUnquotedTranslationSurface(trimmed, lower);
}
// The surface that sits between a circumfix frame's prefix and its trailing
// marker (e.g. "translate " … " to "). Mirrors extract_between_prefix_and_marker
// in src/translation/prompt.rs.
function extractBetweenPrefixAndMarker(original, lower, prefix, marker) {
if (!lower.startsWith(prefix)) return null;
const afterPrefix = lower.slice(prefix.length);
const markerIndex = afterPrefix.indexOf(marker);
if (markerIndex === -1) return null;
return cleanUnquotedTranslationSurface(
original.slice(prefix.length, prefix.length + markerIndex),
);
}
function cleanUnquotedTranslationSurface(candidate) {
const cleaned = String(candidate || "").trim();
if (!cleaned || /["'«»`“”‘’]/u.test(cleaned)) return null;
return cleaned;
}
// Head-final Hindi: "<surface> <object-marker> <target> में अनुवाद". Gated on a
// Devanagari translate stem (अनुवाद); the target markers and object markers are
// the Devanagari forms of the into-marker and object-marker roles. Mirrors
// extract_hindi_unquoted_surface in src/translation/prompt.rs.
function extractHindiUnquotedTranslationSurface(original, lower) {
const markers = translationMarkers();
if (!markers.hindiVerbStems.some((stem) => lower.includes(stem))) return null;
for (const targetMarker of markers.hindiTargetMarkers) {
const targetIndex = lower.indexOf(targetMarker);
if (targetIndex === -1) continue;
const beforeTarget = lower.slice(0, targetIndex);
for (const surfaceMarker of markers.hindiObjectMarkers) {
const surfaceEnd = beforeTarget.lastIndexOf(surfaceMarker);
if (surfaceEnd !== -1) {
return cleanUnquotedTranslationSurface(original.slice(0, surfaceEnd));
}
}
}
return null;
}
function firstMarkerOffset(text, markers) {
let best = null;
for (const marker of markers) {
const offset = text.indexOf(marker);
if (offset !== -1 && (best === null || offset < best)) best = offset;
}
return best;
}
// Head-initial Chinese: a command prefix (把/将) + command marker (翻译成 …), or a
// bare translate stem (翻译/翻譯) + target marker (成/为/到). Both prefix sets and
// marker sets are the Han forms of the object-marker / into-marker / unquoted-
// frame / target-direction roles. Mirrors extract_chinese_unquoted_surface in
// src/translation/prompt.rs.
function extractChineseUnquotedTranslationSurface(original, lower) {
const markers = translationMarkers();
for (const prefix of markers.chineseCommandPrefixes) {
if (!lower.startsWith(prefix)) continue;
const rest = lower.slice(prefix.length);
const markerIndex = firstMarkerOffset(rest, markers.chineseCommandMarkers);
if (markerIndex !== null) {
return cleanUnquotedTranslationSurface(
original.slice(prefix.length, prefix.length + markerIndex),
);
}
}
for (const prefix of markers.chineseTranslatePrefixes) {
if (!lower.startsWith(prefix)) continue;
const rest = lower.slice(prefix.length);
const markerIndex = firstMarkerOffset(rest, markers.chineseTargetMarkers);
if (markerIndex !== null) {
return cleanUnquotedTranslationSurface(
original.slice(prefix.length, prefix.length + markerIndex),
);
}
}
return null;
}
function escapeBehaviorRuleValue(value) {
return String(value || "")
.replaceAll("\\", "\\\\")
.replaceAll('"', '\\"')
.replaceAll("\n", "\\n");
}
function behaviorRuleRecords() {
const greeting = answerFor("greeting", "en");
const farewell = answerFor("farewell", "en");
const identity = answerFor("identity", "en");
const assistantName = answerFor("assistant_name", "en");
return [
{
id: "rule_greeting",
topic: "greetings",
intent: "greeting",
label: "Greeting rule",
matches: "`Hi`, `Hello`, `Hey`, and multilingual greeting seed phrases",
response: greeting,
source: "data/seed/intent-routing.lino + multilingual responses",
whenThen: `When the user says \`Hi\`, \`Hello\`, or \`Hey\` then respond with \`${greeting}\`.`,
},
{
id: "rule_farewell",
topic: "farewells",
intent: "farewell",
label: "Farewell rule",
matches: "`bye`, `goodbye`, `poka`, and multilingual farewell seed phrases",
response: farewell,
source: "data/seed/intent-routing.lino + multilingual responses",
whenThen: `When the user says \`bye\`, \`goodbye\`, or \`пока\` then respond with \`${farewell}\`.`,
},
{
id: "rule_identity",
topic: "identity",
intent: "identity",
label: "Identity rule",
matches: "`Who are you?`, `Кто ты?`, and equivalent identity prompts",
response: identity,
source: "data/seed/identity.lino + multilingual responses",
whenThen: `When the user asks \`Who are you?\` or \`Кто ты?\` then respond with \`${identity}\`.`,
},
{
id: "rule_assistant_name",
topic: "assistant_name",
intent: "assistant_name",
label: "Assistant name rule",
matches: "`What is your name?`, `Как твое имя?`, and equivalent name prompts",
response: assistantName,
source: "data/seed/intent-routing.lino + multilingual responses",
whenThen: `When the user asks \`What is your name?\` or \`Как твое имя?\` then respond with \`${assistantName}\`, unless the assistant name setting is configured.`,
},
{
id: "rule_capabilities",
topic: "capabilities",
intent: "capabilities",
label: "Capabilities rule",
matches: "`What can you do?`, `Что ты умеешь?`, and equivalent capability prompts",
response: "Lists the supported symbolic chat capabilities.",
source: "src/solver_handlers/user_intent.rs",
whenThen:
"When the user asks `What can you do?` or `Что ты умеешь?` then respond with the multilingual capability listing.",
},
{
id: "rule_write_program",
topic: "write_program",
intent: "write_program",
label: "Program template rule",
// Built from the live catalog so the advertised tasks stay in lock-step
// with `WRITE_PROGRAM_TASKS` (mirrors `supported_program_tasks` on the
// Rust side, issue #330).
matches:
"`write_program(language, task)` with languages " +
`${Object.keys(WRITE_PROGRAM_LANGUAGES).join(", ")} and tasks ` +
`${Object.keys(WRITE_PROGRAM_TASKS).join(", ")}`,
response: "Returns a minimal program from the parameterized template catalog.",
source: "data/seed/hello-world-programs.lino + src/coding/catalog.rs",
whenThen:
"When the user requests a program with supported `language` and `task` parameters then respond with the matching template through the single `write_program` intent.",
},
{
id: "rule_unknown",
topic: "unknown_fallback",
intent: "unknown",
label: "Unknown fallback rule",
matches: "Any prompt that no earlier rule or handler can answer",
response: answerFor("unknown", "en"),
source: "data/seed/multilingual-responses.lino",
whenThen:
"When no earlier rule or handler matches the prompt then respond with the multilingual unknown-intent guide (`List behavior rules`, `Show behavior rule`, `When I say … answer …`, `Report issue`, `Export memory`).",
},
];
}
const BEHAVIOR_RULE_TOPIC_ORDER = [
"greetings",
"farewells",
"identity",
"assistant_name",
"capabilities",
"write_program",
"unknown_fallback",
];
function localizedText(language, values) {
return values[language] || values.en;
}
function behaviorRuleTopicLabel(topic, language) {
switch (topic) {
case "greetings":
return localizedText(language, {
en: "Greetings",
ru: "Приветствия",
hi: "अभिवादन",
zh: "问候",
});
case "farewells":
return localizedText(language, {
en: "Farewells",
ru: "Прощания",
hi: "विदाई",
zh: "告别",
});
case "identity":
return localizedText(language, {
en: "Identity",
ru: "Идентичность",
hi: "पहचान",
zh: "身份",
});
case "assistant_name":
return localizedText(language, {
en: "Assistant name",
ru: "Имя ассистента",
hi: "सहायक का नाम",
zh: "助手名称",
});
case "capabilities":
return localizedText(language, {
en: "Capabilities",
ru: "Возможности",
hi: "क्षमताएँ",
zh: "能力",
});
case "write_program":
return localizedText(language, {
en: "Program templates",
ru: "Шаблоны программ",
hi: "Program templates",
zh: "程序模板",
});
case "unknown_fallback":
return localizedText(language, {
en: "Unknown fallback",
ru: "Резервный ответ",
hi: "अज्ञात अनुरोध का वैकल्पिक उत्तर",
zh: "未知请求回退",
});
default:
return localizedText(language, {
en: "Other",
ru: "Другое",
hi: "अन्य",
zh: "其他",
});
}
}
function behaviorRuleTopicOrder(topic) {
const index = BEHAVIOR_RULE_TOPIC_ORDER.indexOf(topic);
return index === -1 ? BEHAVIOR_RULE_TOPIC_ORDER.length : index;
}
function behaviorRuleListIntro(language) {
return localizedText(language, {
en: "Behavior rules I can inspect in this dialog (grouped by topic, each shown as a `When X then Y` statement):",
ru: "Правила поведения, которые я могу показать в этом диалоге (сгруппированы по темам; каждое показано как инструкция `Когда X тогда Y`):",
hi: "व्यवहार नियम जिन्हें मैं इस संवाद में दिखा सकता हूँ (विषय के अनुसार समूहित; हर नियम `जब X तब Y` कथन के रूप में है):",
zh: "我可以查看的行为规则(按主题分组;每条都显示为 `当 X 时 Y` 语句):",
});
}
function runtimeRulesHeading(language) {
return localizedText(language, {
en: "Dialog-local rules taught in this conversation",
ru: "Правила, изученные в этом диалоге",
hi: "इस संवाद में सिखाए गए स्थानीय नियम",
zh: "本对话中学到的局部规则",
});
}
function behaviorRuleListFooter(language) {
if (language === "ru") {
return [
"",
"Прочитать одно правило можно командой `Покажи правило unknown` или `Покажи правило rule_greeting`.",
"Научить этот диалог можно так: ``Когда `ваш запрос` тогда `ваш ответ` ``. Другие формы: ``Когда я скажу `ваш запрос`, ответь `ваш ответ` ``; ``Если я спрошу `ваш запрос`, ответь `ваш ответ` ``; ``Когда `ваш запрос` делай `ваш ответ` ``.",
"Многоязычные формы: английская ``When `X` then `Y` ``, хинди ``जब `X` तब `Y` ``, китайская ``当 `X` 时 `Y` ``.",
"Запись добавляется только в конец: экспортируйте память, чтобы сохранить сообщение с правилом вместе с диалогом.",
];
}
if (language === "hi") {
return [
"",
"एक नियम पढ़ने के लिए `Show behavior rule unknown` या `Show behavior rule rule_greeting` भेजें.",
"इस संवाद को सिखाएँ: ``जब `आपका प्रश्न` तब `आपका उत्तर` ``. अन्य रूप: ``When I say `your prompt`, answer `your answer` ``; ``If I ask `your prompt`, reply `your answer` ``; ``जब `आपका प्रश्न` तो `आपका उत्तर` ``.",
"बहुभाषी रूप: रूसी ``Когда `X` тогда `Y` ``, अंग्रेज़ी ``When `X` then `Y` ``, चीनी ``当 `X` 时 `Y` ``.",
"लेखन केवल append-only है: नियम संदेश को संवाद के साथ रखने के लिए memory export करें.",
];
}
if (language === "zh") {
return [
"",
"要读取一条规则,请发送 `Show behavior rule unknown` 或 `Show behavior rule rule_greeting`。",
"可以这样教当前对话:``当 `你的提示` 时 `你的回答` ``。等价形式:``When I say `your prompt`, answer `your answer` ``;``If I ask `your prompt`, reply `your answer` ``;``当 `你的提示` 则 `你的回答` ``。",
"多语言形式:俄语 ``Когда `X` тогда `Y` ``,印地语 ``जब `X` तब `Y` ``,英语 ``When `X` then `Y` ``。",
"写入是 append-only:导出 memory 可把这条规则消息随对话一起保存。",
];
}
return [
"",
"Read one with `Show behavior rule unknown` or `Show behavior rule rule_greeting`.",
"Teach this dialog with: ``When `your prompt` then `your answer` ``. Equivalent forms: ``When I say `your prompt`, answer `your answer` ``; ``If I ask `your prompt`, reply `your answer` ``; ``When `your prompt` do `your answer` ``.",
"Multilingual forms: Russian ``Когда `X` тогда `Y` `` / ``Когда `X` делай `Y` ``, Hindi ``जब `X` तब `Y` ``, Chinese ``当 `X` 时 `Y` ``.",
"The write is append-only: export memory to preserve the rule message with the dialog.",
];
}
function localizedRuleResponse(rule, language) {
if (rule.id === "rule_write_program") {
return localizedText(language, {
en: "Returns a minimal program from the parameterized template catalog.",
ru: "Возвращает минимальную программу из параметризованного каталога шаблонов.",
hi: "parameterized template catalog से minimal program लौटाता है.",
zh: "从参数化模板目录返回一个最小程序。",
});
}
switch (rule.id) {
case "rule_greeting":
return answerFor("greeting", language);
case "rule_farewell":
return answerFor("farewell", language);
case "rule_identity":
return answerFor("identity", language);
case "rule_assistant_name":
return localizedText(language, {
en: "Returns the assistant-name answer; browser surfaces can override it from the assistant name setting.",
ru: "Возвращает ответ об имени ассистента; браузерные поверхности могут переопределить его настройкой имени ассистента.",
hi: "assistant-name उत्तर लौटाता है; browser surfaces assistant name setting से इसे बदल सकते हैं.",
zh: "返回助手名称回答;浏览器界面可通过助手名称设置覆盖它。",
});
case "rule_capabilities":
return localizedText(language, {
en: "Lists the supported symbolic chat capabilities.",
ru: "Перечисляет поддерживаемые возможности символьного чата.",
hi: "समर्थित symbolic chat क्षमताओं को सूचीबद्ध करता है.",
zh: "列出支持的符号聊天能力。",
});
case "rule_unknown":
return answerFor("unknown", language);
default:
return rule.response;
}
}
function localizedRuleLabel(rule, language) {
if (rule.id === "rule_write_program") {
return localizedText(language, {
en: "Program template rule",
ru: "Правило шаблона программы",
hi: "Program template rule",
zh: "程序模板规则",
});
}
const labels = {
rule_greeting: {
en: "Greeting rule",
ru: "Правило приветствия",
hi: "अभिवादन नियम",
zh: "问候规则",
},
rule_farewell: {
en: "Farewell rule",
ru: "Правило прощания",
hi: "विदाई नियम",
zh: "告别规则",
},
rule_identity: {
en: "Identity rule",
ru: "Правило идентичности",
hi: "पहचान नियम",
zh: "身份规则",
},
rule_assistant_name: {
en: "Assistant name rule",
ru: "Правило имени ассистента",
hi: "सहायक नाम नियम",
zh: "助手名称规则",
},
rule_capabilities: {
en: "Capabilities rule",
ru: "Правило возможностей",
hi: "क्षमता नियम",
zh: "能力规则",
},
rule_unknown: {
en: "Unknown fallback rule",
ru: "Резервное правило для неизвестного запроса",
hi: "अज्ञात अनुरोध का वैकल्पिक नियम",
zh: "未知请求回退规则",
},
};
return labels[rule.id] ? localizedText(language, labels[rule.id]) : rule.label;
}
function localizedRuleMatches(rule, language) {
if (rule.id === "rule_write_program") {
return localizedText(language, {
en: "`write_program(language, task)` with supported languages and tasks",
ru: "`write_program(language, task)` с поддерживаемыми языками и задачами",
hi: "supported languages और tasks वाला `write_program(language, task)`",
zh: "带受支持语言和任务的 `write_program(language, task)`",
});
}
const matches = {
rule_greeting: {
en: "`Hi`, `Hello`, `Hey`, and multilingual greeting seed phrases",
ru: "`Hi`, `Hello`, `Hey` и многоязычные seed-фразы приветствия",
hi: "`Hi`, `Hello`, `Hey` और बहुभाषी greeting seed phrases",
zh: "`Hi`、`Hello`、`Hey` 以及多语言问候 seed 短语",
},
rule_farewell: {
en: "`bye`, `goodbye`, `poka`, and multilingual farewell seed phrases",
ru: "`bye`, `goodbye`, `poka` и многоязычные seed-фразы прощания",
hi: "`bye`, `goodbye`, `poka` और बहुभाषी farewell seed phrases",
zh: "`bye`、`goodbye`、`poka` 以及多语言告别 seed 短语",
},
rule_identity: {
en: "`Who are you?`, `Кто ты?`, and equivalent identity prompts",
ru: "`Who are you?`, `Кто ты?` и равнозначные вопросы об идентичности",
hi: "`Who are you?`, `Кто ты?` और समान identity prompts",
zh: "`Who are you?`、`Кто ты?` 以及等价身份提示",
},
rule_assistant_name: {
en: "`What is your name?`, `Как тебя зовут?`, and equivalent name prompts",
ru: "`What is your name?`, `Как тебя зовут?` и равнозначные вопросы об имени",
hi: "`What is your name?`, `Как тебя зовут?` और समान name prompts",
zh: "`What is your name?`、`Как тебя зовут?` 以及等价名称提示",
},
rule_capabilities: {
en: "`What can you do?`, `Что ты умеешь?`, and equivalent capability prompts",
ru: "`What can you do?`, `Что ты умеешь?` и равнозначные вопросы о возможностях",
hi: "`What can you do?`, `Что ты умеешь?` और समान capability prompts",
zh: "`What can you do?`、`Что ты умеешь?` 以及等价能力提示",
},
rule_unknown: {
en: "Any prompt that no earlier rule or handler can answer",
ru: "Любой запрос, на который не ответило более раннее правило или обработчик",
hi: "कोई भी prompt जिसका उत्तर पहले का rule या handler नहीं दे सकता",
zh: "任何前面的规则或处理器无法回答的提示",
},
};
return matches[rule.id] ? localizedText(language, matches[rule.id]) : rule.matches;
}
function localizedRuleWhenThen(rule, language) {
if (rule.id === "rule_write_program") {
if (language === "ru") {
return "Когда пользователь просит программу с поддерживаемыми параметрами `language` и `task`, ответь соответствующим шаблоном через единое намерение `write_program`.";
}
if (language === "hi") {
return "जब उपयोगकर्ता supported `language` और `task` parameters वाला program माँगे, तब single `write_program` intent से matching template दें.";
}
if (language === "zh") {
return "当用户请求带受支持 `language` 和 `task` 参数的程序时,通过单个 `write_program` 意图选择匹配模板。";
}
return rule.whenThen;
}
const response = localizedRuleResponse(rule, language);
if (rule.id === "rule_greeting") {
if (language === "ru") return `Когда пользователь говорит \`Hi\`, \`Hello\`, \`Hey\` или многоязычную фразу приветствия, ответь \`${response}\`.`;
if (language === "hi") return `जब उपयोगकर्ता \`Hi\`, \`Hello\`, \`Hey\` या बहुभाषी greeting phrase कहे, तब \`${response}\` उत्तर दें.`;
if (language === "zh") return `当用户说 \`Hi\`、\`Hello\`、\`Hey\` 或多语言问候短语时,回答 \`${response}\`。`;
}
if (rule.id === "rule_farewell") {
if (language === "ru") return `Когда пользователь говорит \`bye\`, \`goodbye\`, \`poka\` или многоязычную фразу прощания, ответь \`${response}\`.`;
if (language === "hi") return `जब उपयोगकर्ता \`bye\`, \`goodbye\`, \`poka\` या बहुभाषी farewell phrase कहे, तब \`${response}\` उत्तर दें.`;
if (language === "zh") return `当用户说 \`bye\`、\`goodbye\`、\`poka\` 或多语言告别短语时,回答 \`${response}\`。`;
}
if (rule.id === "rule_identity") {
if (language === "ru") return `Когда пользователь спрашивает \`Who are you?\` или \`Кто ты?\`, ответь \`${response}\`.`;
if (language === "hi") return `जब उपयोगकर्ता \`Who are you?\` या \`Кто ты?\` पूछे, तब \`${response}\` उत्तर दें.`;
if (language === "zh") return `当用户问 \`Who are you?\` 或 \`Кто ты?\` 时,回答 \`${response}\`。`;
}
if (rule.id === "rule_assistant_name") {
if (language === "ru") return "Когда пользователь спрашивает `What is your name?` или `Как тебя зовут?`, ответь сообщением об имени ассистента; если поверхность поддерживает настройку имени, включи настроенное имя.";
if (language === "hi") return "जब उपयोगकर्ता `What is your name?` या `Как тебя зовут?` पूछे, तब assistant-name उत्तर दें; अगर surface में assistant-name setting है, तो configured name शामिल करें.";
if (language === "zh") return "当用户问 `What is your name?` 或 `Как тебя зовут?` 时,回答助手名称;如果界面有助手名称设置,则包含配置的名称。";
}
if (rule.id === "rule_capabilities") {
if (language === "ru") return "Когда пользователь спрашивает `What can you do?` или `Что ты умеешь?`, ответь многоязычным списком возможностей.";
if (language === "hi") return "जब उपयोगकर्ता `What can you do?` या `Что ты умеешь?` पूछे, तब बहुभाषी capability listing दें.";
if (language === "zh") return "当用户问 `What can you do?` 或 `Что ты умеешь?` 时,回答多语言能力列表。";
}
if (rule.id === "rule_unknown") {
if (language === "ru") return "Когда ни одно более раннее правило или обработчик не подходит к запросу, ответь многоязычной подсказкой для неизвестного намерения (`Покажи правила`, `Покажи правило`, `Когда ... тогда ...`, `Сообщить о проблеме`, `Экспорт памяти`).";
if (language === "hi") return "जब कोई पहले का rule या handler prompt से मेल न खाए, तब unknown-intent guide दें (`नियम दिखाएँ`, `rule दिखाएँ`, `जब ... तब ...`, `Report issue`, `Export memory`).";
if (language === "zh") return "当前面的规则或处理器都不匹配提示时,回答未知意图指南(`显示规则`、`显示规则详情`、`当 ... 时 ...`、`报告问题`、`导出 memory`)。";
}
return rule.whenThen;
}
function runtimeRuleWhenThen(rule, language) {
if (language === "ru") {
return `Когда пользователь говорит \`${rule.trigger}\`, ответь \`${rule.answer}\`.`;
}
if (language === "hi") {
return `जब उपयोगकर्ता \`${rule.trigger}\` कहे, तब \`${rule.answer}\` उत्तर दें.`;
}
if (language === "zh") {
return `当用户说 \`${rule.trigger}\` 时,回答 \`${rule.answer}\`。`;
}
return `When the user says \`${rule.trigger}\` then respond with \`${rule.answer}\`.`;
}
function renderBehaviorRuleList(runtimeRules, language = "en") {
const lines = [behaviorRuleListIntro(language), ""];
const groups = new Map();
for (const rule of behaviorRuleRecords()) {
const order = behaviorRuleTopicOrder(rule.topic);
if (!groups.has(order)) {
groups.set(order, { label: behaviorRuleTopicLabel(rule.topic, language), rules: [] });
}
groups.get(order).rules.push(rule);
}
const ordered = Array.from(groups.entries()).sort((a, b) => a[0] - b[0]);
ordered.forEach(([, group], index) => {
lines.push(`### ${group.label}`);
for (const rule of group.rules) {
lines.push(`- \`${rule.id}\` -> ${localizedRuleWhenThen(rule, language)}`);
}
if (index + 1 < ordered.length) lines.push("");
});
if (Array.isArray(runtimeRules) && runtimeRules.length > 0) {
lines.push("", `### ${runtimeRulesHeading(language)}`);
for (const rule of runtimeRules) {
lines.push(
`- \`${rule.id}\` -> ${runtimeRuleWhenThen(rule, language)}`,
);
}
}
lines.push(...behaviorRuleListFooter(language));
return lines.join("\n");
}
function renderBehaviorRuleDetail(rule, language = "en") {
const label = localizedRuleLabel(rule, language);
const whenThen = localizedRuleWhenThen(rule, language);
const matches = localizedRuleMatches(rule, language);
const response = localizedRuleResponse(rule, language);
const changeHint = localizedText(language, {
en: "To change this behavior in the current dialog, send: ``When `your prompt` then `your answer` ``. Equivalent: ``When I say `your prompt`, answer `your answer` ``.",
ru: "Чтобы изменить это поведение в текущем диалоге, отправьте: ``Когда `ваш запрос` тогда `ваш ответ` ``. Также можно: ``Когда я скажу `ваш запрос`, ответь `ваш ответ` ``.",
hi: "इस व्यवहार को वर्तमान संवाद में बदलने के लिए भेजें: ``जब `आपका प्रश्न` तब `आपका उत्तर` ``. दूसरा रूप: ``When I say `your prompt`, answer `your answer` ``.",
zh: "要在当前对话中改变此行为,请发送:``当 `你的提示` 时 `你的回答` ``。也可以发送:``When I say `your prompt`, answer `your answer` ``。",
});
return [
label,
"",
whenThen,
"",
"```links",
rule.id,
` topic "${escapeBehaviorRuleValue(rule.topic)}"`,
` intent "${escapeBehaviorRuleValue(rule.intent)}"`,
` matches "${escapeBehaviorRuleValue(matches)}"`,
` response "${escapeBehaviorRuleValue(response)}"`,
` source "${escapeBehaviorRuleValue(rule.source)}"`,
` when_then "${escapeBehaviorRuleValue(whenThen)}"`,
"```",
"",
changeHint,
].join("\n");
}
function assistantNameStatus(preferences) {
const name = normalizeAssistantNamePreference(
preferences && preferences.assistantName,
);
return name ? `configured:${name}` : "browser_preference_when_set_else_not_configured";
}
const BROWSER_SURFACE = {
slug: "browser",
label: "browser demo with JavaScript and WebAssembly worker",
runtime: "JavaScript UI plus a WebAssembly worker mirror of the solver",
memory: "browser IndexedDB/local storage plus worker state and imported memory",
webSearch: "available through browser CORS-readable providers when online and not blocked",
limits: "browser settings, import/export controls, and IndexedDB-backed memory belong to this surface",
};
function modeStatus(enabled) {
return enabled ? "enabled" : "disabled";
}
function definitionFusionStatus(preferences) {
return preferences && preferences.definitionFusion === "auto"
? "enabled_by_default"
: "explicit_only";
}
function blueprintCompositionStatus(preferences) {
return normalizeBlueprintComposition(
preferences && preferences.blueprintComposition,
);
}
function renderSelfFacts(preferences) {
const assistantName = assistantNameStatus(preferences);
const surface = BROWSER_SURFACE;
return [
"Facts I know about myself in this environment:",
"",
`- **Execution surface**: ${surface.label} (\`${surface.slug}\`).`,
`- **Runtime**: ${surface.runtime}.`,
`- **Memory**: ${surface.memory}.`,
`- **Web search**: ${surface.webSearch}.`,
`- **Surface limits**: ${surface.limits}.`,
"- **Local rules**: local links rules and seed facts are checked first.",
"",
"```links",
"self_fact_model",
' subject "formal-ai"',
' relation "model"',
` object "${escapeBehaviorRuleValue(AGENT_INFO.model || "formal-symbolic-production")}"`,
"self_fact_policy",
' subject "formal-ai"',
' relation "policy"',
' object "deterministic symbolic AI; no neural network inference"',
"self_fact_environment",
' subject "formal-ai"',
' relation "execution_surface"',
` object "${surface.slug}"`,
"self_fact_runtime",
' subject "formal-ai"',
' relation "runtime"',
` object "${escapeBehaviorRuleValue(surface.runtime)}"`,
"self_fact_memory",
' subject "formal-ai"',
' relation "memory"',
` object "${escapeBehaviorRuleValue(surface.memory)}"`,
"self_fact_web_search",
' subject "formal-ai"',
' relation "web_search"',
` object "${escapeBehaviorRuleValue(surface.webSearch)}"`,
"self_fact_assistant_name",
' subject "formal-ai"',
' relation "assistant_name"',
` object "${escapeBehaviorRuleValue(assistantName)}"`,
"self_fact_agent_mode",
' subject "formal-ai"',
' relation "agent_mode"',
` object "${modeStatus(preferences && preferences.agentMode)}"`,
"self_fact_diagnostics",
' subject "formal-ai"',
' relation "diagnostic_mode"',
` object "${modeStatus(preferences && preferences.diagnosticsMode)}"`,
"self_fact_definition_fusion",
' subject "formal-ai"',
' relation "definition_fusion"',
` object "${definitionFusionStatus(preferences)}"`,
"self_fact_blueprint_composition",
' subject "formal-ai"',
' relation "blueprint_composition"',
` object "${blueprintCompositionStatus(preferences)}"`,
"```",
"",
"Read behavior with `List behavior rules`; teach one with When `prompt` then `answer` (or When I say `prompt`, answer `answer`).",
].join("\n");
}
function renderKnownFacts(language, preferences) {
const surface = BROWSER_SURFACE;
const assistantName = assistantNameStatus(preferences);
const links = [
"```links",
"known_fact_local_seed",
' source "local_links_notation_seed"',
' scope "built-in rules, concepts, facts, tools, and response templates"',
"known_fact_internet",
' source "environment_aware_web_search"',
` scope "${escapeBehaviorRuleValue(surface.webSearch)}"`,
"known_fact_memory",
' source "conversation_memory"',
` scope "${escapeBehaviorRuleValue(surface.memory)}"`,
"known_fact_environment",
' subject "formal-ai"',
' relation "execution_surface"',
` object "${surface.slug}"`,
"known_fact_self",
' subject "formal-ai"',
' relation "model"',
` object "${escapeBehaviorRuleValue(AGENT_INFO.model || "formal-symbolic-production")}"`,
"known_fact_assistant_name",
' subject "formal-ai"',
' relation "assistant_name_setting"',
` object "${escapeBehaviorRuleValue(assistantName)}"`,
"known_fact_surface_limits",
' source "environment_directory"',
` scope "${escapeBehaviorRuleValue(surface.limits)}"`,
"```",
].join("\n");
if (language === "ru") {
return [
`Я могу использовать несколько классов фактов в текущей среде \`${surface.slug}\`:`,
"",
"- **Локальные факты и правила**: встроенный seed Links Notation, включая правила, понятия, инструменты и ответы.",
`- **Интернет**: ${surface.webSearch}; это не означает, что весь интернет предзагружен в локальную память.`,
`- **Память диалога**: ${surface.memory}.`,
"- **Факты о себе**: модель `formal-symbolic-production`, политика исполнения, поверхность и источники ответов.",
`- **Ограничения среды**: ${surface.limits}.`,
"",
links,
"",
"Для конкретного факта задайте прямой вопрос; порядок проверки: локальные правила, память, затем веб-поиск, если он доступен в этой среде.",
].join("\n");
}
if (language === "hi") {
return [
`मैं current \`${surface.slug}\` environment में इन fact sources का उपयोग कर सकता हूँ:`,
"",
"- **Local facts and rules**: Links Notation seed में rules, concepts, tools और response templates.",
`- **Internet**: ${surface.webSearch}; पूरा internet local memory में preload नहीं है.`,
`- **Conversation memory**: ${surface.memory}.`,
"- **Self facts**: model `formal-symbolic-production`, execution surface और answer sources.",
`- **Surface limits**: ${surface.limits}.`,
"",
links,
"",
"किसी खास fact के लिए सीधे पूछें; मैं local rules और memory पहले देखता हूँ, फिर environment अनुमति दे तो web search इस्तेमाल करता हूँ.",
].join("\n");
}
if (language === "zh") {
return [
`在当前 \`${surface.slug}\` 环境中, 我可以使用这些事实来源:`,
"",
"- **本地事实和规则**: Links Notation seed 中的规则、概念、工具和回复模板。",
`- **Internet**: ${surface.webSearch}; 整个互联网不会预加载到本地记忆中。`,
`- **Conversation memory**: ${surface.memory}。`,
"- **Self facts**: model `formal-symbolic-production`, execution surface 和 answer sources。",
`- **Surface limits**: ${surface.limits}。`,
"",
links,
"",
"如果需要某个具体事实, 请直接提问; 我会先检查本地规则和记忆, 环境允许时再使用 web search。",
].join("\n");
}
return [
`I can use several classes of facts in the current \`${surface.slug}\` environment:`,
"",
"- **Local facts and rules**: built-in Links Notation seed data, including rules, concepts, tools, and response templates.",
`- **Internet**: ${surface.webSearch}; the whole internet is not preloaded into local memory.`,
`- **Conversation memory**: ${surface.memory}.`,
"- **Self facts**: model `formal-symbolic-production`, execution policy, active surface, and answer sources.",
`- **Surface limits**: ${surface.limits}.`,
"",
links,
"",
"Ask for a specific fact directly; I check local rules and memory first, then use web search only when this environment allows it.",
].join("\n");
}
function renderRuntimeRuleUpdate(rule, language = "en") {
const whenThenText = runtimeRuleWhenThen(rule, language);
const title = localizedText(language, {
en: "Behavior rule recorded for this dialog.",
ru: "Правило поведения записано для этого диалога.",
hi: "इस संवाद के लिए व्यवहार नियम record किया गया.",
zh: "已为本对话记录行为规则。",
});
const sendHint =
language === "ru"
? `Отправьте \`${rule.trigger}\` сейчас, и я отвечу настроенным ответом. Экспортируйте память, чтобы сохранить это правило вместе с диалогом.`
: language === "hi"
? `\`${rule.trigger}\` अभी भेजें और मैं configured response से उत्तर दूँगा. इस rule message को dialog के साथ रखने के लिए memory export करें.`
: language === "zh"
? `现在发送 \`${rule.trigger}\`,我会使用配置的回答。导出 memory 可把这条规则消息随对话一起保存。`
: `Send \`${rule.trigger}\` now and I will answer with the configured response. Export memory to keep this rule message with the dialog.`;
return [
title,
"",
whenThenText,
"",
"```links",
rule.id,
' type "behavior_rule_runtime"',
` match_prompt "${escapeBehaviorRuleValue(rule.trigger)}"`,
` answer "${escapeBehaviorRuleValue(rule.answer)}"`,
` when_then "${escapeBehaviorRuleValue(whenThenText)}"`,
' source "user_message"',
"```",
"",
sendHint,
].join("\n");
}
// Issue #386: recognise a request to list the assistant's behavior rules by
// *meaning*, not a hardcoded per-language phrase list. The standalone phrases
// (role rule_listing_phrase) and the three compositional dimensions
// (rule_listing_subject / rule_listing_request / rule_listing_scope) live in
// data/seed/meanings-behavior-rules.lino. Mirror of is_behavior_rules_list in
// src/solver_handlers/behavior_rules.rs.
function isBehaviorRulesList(normalized) {
return (
matchesBehaviorRulesListSeedPattern(normalized) ||
lexiconMentionsRoleSubstring(ROLE_RULE_LISTING_PHRASE, normalized) ||
isSupportedLanguageBehaviorRulesListQuery(normalized)
);
}
function matchesBehaviorRulesListSeedPattern(normalized) {
return PROMPT_PATTERNS.some((pattern) => {
if (!pattern || pattern.intent !== "behavior_rules_list" || !pattern.text) {
return false;
}
const text = normalizePrompt(pattern.text);
if (!text) return false;
switch (pattern.kind) {
case "keyword":
case "phrase":
return normalized === text || normalized.includes(text);
case "prefix":
return normalized.startsWith(text);
case "suffix":
return normalized.endsWith(text);
default:
return false;
}
});
}
// True when the prompt, within one supported language's vocabulary, names the
// rule subject, asks to enumerate it, and scopes the request to the assistant's
// own behavior. The three dimensions are read from the meaning lexicon
// (rule_listing_subject / rule_listing_request / rule_listing_scope) rather than
// hardcoded per-language word lists. The per-language AND is preserved: every
// dimension must be evidenced within the SAME language (wordsForRoleInLanguages),
// matched as a raw substring to keep the legacy stem match byte-for-byte. Mirror
// of is_supported_language_behavior_rules_list_query in
// src/solver_handlers/behavior_rules.rs.
function isSupportedLanguageBehaviorRulesListQuery(normalized) {
const present = (role, language) =>
wordsForRoleInLanguages(role, [language]).some((word) =>
normalized.includes(word),
);
return ["en", "ru", "hi", "zh"].some(
(language) =>
present(ROLE_RULE_LISTING_SUBJECT, language) &&
present(ROLE_RULE_LISTING_REQUEST, language) &&
present(ROLE_RULE_LISTING_SCOPE, language),
);
}
// Issue #386: recognise a request to list the assistant's own facts by
// *meaning*, not a hardcoded per-language phrase list. The self_fact_query role
// gathers every surface from data/seed/meanings-intent.lino; mirror of
// is_self_fact_query in src/solver_handlers/self_awareness.rs. The prompt is
// re-normalized first because some call sites pass a merely-lowercased string
// (trailing "?" intact) and the boundary-aware matcher expects punctuation
// already collapsed to spaces.
function isSelfFactQuery(normalized) {
return lexiconMentionsRole(ROLE_SELF_FACT_QUERY, normalizePrompt(normalized));
}
// Issue #386: recognise "introduce yourself" / "расскажи о себе" /
// "अपना परिचय दो" / "介绍一下你自己" by the self_introduction_request meaning
// role. The pre-check is preserved verbatim: an empty prompt, or one that is
// really a self-fact query, must not be treated as an introduction request, so
// "list all facts you know about yourself" still routes to the self-fact
// branch. Mirror of is_self_introduction_query in
// src/solver_handlers/self_awareness.rs.
function isSelfIntroductionQuery(normalized) {
const cleaned = normalizePrompt(normalized);
if (!cleaned || isSelfFactQuery(cleaned)) return false;
return lexiconMentionsRole(ROLE_SELF_INTRODUCTION_REQUEST, cleaned);
}
function selfAwarenessLanguage(prompt, normalized) {
// Issue #386: language is detected purely by Unicode script ranges. The
// Cyrillic range below already subsumes the former second-person pronoun
// list (ty/tebya/tvoy/vy/...), every member of which is Cyrillic, so no raw
// word list is needed -- the script range is the universal signal. Mirror of
// self_awareness_language in src/solver_handlers/self_awareness.rs.
const text = `${String(prompt || "").toLowerCase()} ${String(normalized || "")}`;
if (/[\u0400-\u04ff]/u.test(text)) return "ru";
if (/[\u0900-\u097f]/u.test(text)) return "hi";
if (/[\u4e00-\u9fff]/u.test(text)) return "zh";
return detectLanguage(prompt);
}
function selfIntroductionContent(language, preferences) {
const identity = answerFor("identity", language);
const name = normalizeAssistantNamePreference(
preferences && preferences.assistantName,
);
if (!name) return identity;
if (language === "ru") return `Меня зовут ${name}. ${identity}`;
if (language === "hi") return `मेरा नाम ${name} है। ${identity}`;
if (language === "zh") return `我的名字是 ${name}。${identity}`;
return `My name is ${name}. ${identity}`;
}
function cleanConversationTopic(raw) {
return String(raw || "")
.trim()
.replace(/^[`"':._,\-\s!?]+|[`"':._,\-\s!?]+$/gu, "");
}
function conversationTopic(prompt, normalized) {
// Recognized surfaces — the let-us-talk-about-X phrasings in every supported
// language — carry the conversation_topic_opener role; each is a prefix whose
// text before the … slot is the matchable opener, in declaration order. A
// form whose action is "scan" is also matched anywhere in the prompt, not only
// at the start, so an opener that follows a greeting is still found. No
// per-language opener list lives here — only the concept. Mirrors
// conversation_topic in src/solver_handlers/benchmark_prompts.rs (issue #386).
const forms = roleWordForms(ROLE_CONVERSATION_TOPIC_OPENER);
for (const form of forms) {
if (normalized.startsWith(form.before)) {
return cleanConversationTopic(normalized.slice(form.before.length));
}
}
const lower = String(prompt || "").toLowerCase();
for (const form of forms) {
if (form.action !== "scan") continue;
const index = lower.indexOf(form.before);
if (index >= 0) {
return cleanConversationTopic(lower.slice(index + form.before.length));
}
}
return "";
}
function conversationTopicContent(topic, language) {
if (language === "ru") {
return `Можем. Тема: ${topic}. Я могу начать с краткого определения, контекста или конкретного вопроса; если веб-поиск доступен, публичные факты можно уточнить через внешний источник.`;
}
if (language === "hi") {
return `हम बात कर सकते हैं. विषय: ${topic}. मैं छोटी परिभाषा, संदर्भ, या किसी конкрет प्रश्न से शुरू कर सकता हूँ; web search उपलब्ध हो तो public facts बाहरी स्रोत से जाँचे जा सकते हैं.`;
}
if (language === "zh") {
return `可以聊。主题: ${topic}。我可以从简短定义、上下文或具体问题开始; 如果 web search 可用, 公开事实可以通过外部来源核对。`;
}
return `We can talk about ${topic}. I can start with a short definition, context, or a specific question; when web search is available, public facts can be checked against an external source.`;
}
// Issue #386: a known-facts inventory query is recognised by composing meaning
// roles, not by matching raw words per language. The universal algorithm is
// identical for every language: the prompt either names the knowledge `fact`
// noun together with an enumerating interrogative and a second-person
// attribution of knowing, or it matches one of the complete standalone
// phrasings that ask what the assistant knows even without the noun. The
// prompt is re-normalised first so the boundary-aware matcher sees punctuation
// collapsed to spaces. Mirror of is_known_fact_query in
// src/solver_handlers/self_awareness.rs.
function isKnownFactQuery(normalized) {
if (isSelfFactQuery(normalized)) return false;
const cleaned = normalizePrompt(normalized);
const composed =
lexiconMentionsRole(ROLE_KNOWLEDGE_INVENTORY_NOUN, cleaned) &&
lexiconMentionsRole(ROLE_KNOWLEDGE_INVENTORY_INTERROGATIVE, cleaned) &&
lexiconMentionsRole(ROLE_KNOWLEDGE_POSSESSION, cleaned);
return (
composed || lexiconMentionsRole(ROLE_KNOWLEDGE_INVENTORY_PHRASE, cleaned)
);
}
function cleanRuleQuery(raw) {
return String(raw || "")
.trim()
.replace(/^[\s`"':._,\-?!]+|[\s`"':._,\-?!]+$/g, "")
.toLowerCase();
}
function detailQuery(prompt) {
const lower = String(prompt || "").toLowerCase();
const prefixes = [
"show behavior rule",
"read behavior rule",
"describe behavior rule",
"show rule",
"read rule",
"details for rule",
"детали правила",
"покажи правило",
"прочитай правило",
];
for (const prefix of prefixes) {
if (lower.startsWith(prefix)) {
return cleanRuleQuery(String(prompt || "").slice(prefix.length));
}
}
if (lower.includes("rule_unknown")) return "unknown";
return "";
}
function findBehaviorRule(query) {
const cleaned = cleanRuleQuery(query);
const withoutPrefix = cleaned.startsWith("rule_") ? cleaned.slice(5) : cleaned;
return behaviorRuleRecords().find(
(rule) =>
rule.id === cleaned ||
rule.id === `rule_${withoutPrefix}` ||
rule.intent === cleaned ||
rule.intent === withoutPrefix ||
rule.label.toLowerCase().includes(withoutPrefix),
);
}
function codeSpans(text) {
return String(text || "")
.split("`")
.map((part, index) => (index % 2 === 1 ? part.trim() : ""))
.filter(Boolean);
}
// Issue #144 / #386: recognize behavior-rule updates expressed as `When X then
// Y` (and translations) in addition to the explicit `When I say … answer …`
// grammar. No keyword is named here any more — every surface lives in the
// embedded meaning lexicon (data/seed/meanings-skill-compiler.lino) and is read
// by semantic role, mirroring explicit_teaching_form + looks_like_skill_description
// in src/skill_compiler.rs:
// * a teaching trigger lead that co-occurs with a teaching response verb, or a
// standalone behaviour-rule edit directive (the explicit teaching form); and
// * a when-then frame whose circumfix surface brackets the trigger and answer —
// the literal before the … (U+2026) is the head, the literal after it is the
// link; both must appear, head before link, with at least one backtick on
// each side so the runtime extractor can pull the trigger and answer
// deterministically.
const ROLE_SKILL_TEACHING_TRIGGER_LEAD = "skill_teaching_trigger_lead";
const ROLE_SKILL_TEACHING_RESPONSE_VERB = "skill_teaching_response_verb";
const ROLE_BEHAVIOR_RULE_EDIT_DIRECTIVE = "behavior_rule_edit_directive";
const ROLE_SKILL_WHEN_THEN_PAIR = "skill_when_then_pair";
function looksLikeRuntimeRuleUpdate(text) {
const raw = String(text || "");
const lower = raw.toLowerCase();
if (
(lexiconMentionsRoleSubstring(ROLE_SKILL_TEACHING_TRIGGER_LEAD, lower) &&
lexiconMentionsRoleSubstring(ROLE_SKILL_TEACHING_RESPONSE_VERB, lower)) ||
lexiconMentionsRoleSubstring(ROLE_BEHAVIOR_RULE_EDIT_DIRECTIVE, lower)
) {
return true;
}
for (const form of roleWordForms(ROLE_SKILL_WHEN_THEN_PAIR)) {
if (form.slot !== "circumfix") continue;
const head = form.before;
const link = form.after;
const headPos = lower.indexOf(head);
if (headPos === -1) continue;
const tail = lower.slice(headPos + head.length);
const linkPos = tail.indexOf(link);
if (linkPos === -1) continue;
const absoluteLinkPos = headPos + head.length + linkPos;
const beforeLink = raw.slice(headPos, absoluteLinkPos);
const afterLink = raw.slice(absoluteLinkPos + link.length);
if (beforeLink.includes("`") && afterLink.includes("`")) return true;
}
return false;
}
function runtimeRuleFromText(text) {
if (!looksLikeRuntimeRuleUpdate(text)) return null;
const spans = codeSpans(text);
if (spans.length < 2) return null;
const trigger = spans[0].trim();
const answer = spans[1].trim();
if (!trigger || !answer) return null;
return {
id: stableBehaviorRuleId("behavior_rule_runtime", `${trigger}\n${answer}`),
trigger,
answer,
};
}
function runtimeRuleForPrompt(prompt, history) {
const normalizedPrompt = normalizePrompt(prompt);
const turns = Array.isArray(history) ? history : [];
for (let index = turns.length - 1; index >= 0; index -= 1) {
const turn = turns[index] || {};
if (String(turn.role || "").toLowerCase() !== "user") continue;
const rule = runtimeRuleFromText(turn.content);
if (rule && normalizePrompt(rule.trigger) === normalizedPrompt) {
return rule;
}
}
return null;
}
function collectRuntimeRules(history) {
const turns = Array.isArray(history) ? history : [];
const seen = new Set();
const rules = [];
for (const turn of turns) {
const role = String((turn || {}).role || "").toLowerCase();
if (role !== "user") continue;
const rule = runtimeRuleFromText((turn || {}).content);
if (rule && !seen.has(rule.id)) {
seen.add(rule.id);
rules.push(rule);
}
}
return rules;
}
function tryBehaviorRules(prompt, normalized, history, preferences) {
const language = detectLanguage(prompt);
const updateRule = runtimeRuleFromText(prompt);
if (updateRule) {
return {
intent: "behavior_rule_update",
content: renderRuntimeRuleUpdate(updateRule, language),
confidence: 1.0,
evidence: ["behavior_rule:update", updateRule.id],
};
}
if (isBehaviorRulesList(normalized)) {
return {
intent: "behavior_rules_list",
content: renderBehaviorRuleList(collectRuntimeRules(history), language),
confidence: 1.0,
evidence: ["behavior_rules:list", "all"],
};
}
const query = detailQuery(prompt);
if (query) {
const rule = findBehaviorRule(query);
if (rule) {
return {
intent: "behavior_rule_detail",
content: renderBehaviorRuleDetail(rule, language),
confidence: 1.0,
evidence: ["behavior_rule:read", rule.id],
};
}
}
if (isSelfIntroductionQuery(normalized)) {
const language = selfAwarenessLanguage(prompt, normalized);
return {
intent: "identity",
content: selfIntroductionContent(language, preferences),
confidence: 1.0,
evidence: [
"identity:self_introduction",
`language:${language}`,
`assistant_name:${assistantNameStatus(preferences)}`,
],
};
}
if (isArchitectureQuestion(normalized)) {
const language = architectureLanguage(prompt, normalized);
return {
intent: "meta_explanation",
content: architectureExplanationContent(language),
confidence: 1.0,
evidence: [
"response:meta_explanation",
"meta_explanation:self_awareness",
`language:${language}`,
],
};
}
if (isSelfFactQuery(normalized)) {
return {
intent: "self_facts",
content: renderSelfFacts(preferences),
confidence: 1.0,
evidence: ["self_facts:list", "formal-ai"],
};
}
if (isKnownFactQuery(normalized)) {
const language = selfAwarenessLanguage(prompt, normalized);
return {
intent: "known_facts",
content: renderKnownFacts(language, preferences),
confidence: 1.0,
evidence: ["known_facts:list", "formal-ai", `language:${language}`],
};
}
const topic = conversationTopic(prompt, normalized);
if (topic) {
const language = selfAwarenessLanguage(prompt, normalized);
return {
intent: "conversation_topic",
content: conversationTopicContent(topic, language),
confidence: 0.75,
evidence: [`conversation_topic:${topic}`, `language:${language}`],
};
}
const runtimeRule = runtimeRuleForPrompt(prompt, history);
if (runtimeRule) {
return {
intent: "behavior_rule_custom",
content: runtimeRule.answer,
confidence: 1.0,
evidence: ["behavior_rule:match", runtimeRule.id],
};
}
return null;
}
function containsAny(normalized, values) {
if (!normalized || !Array.isArray(values)) return false;
return values.some((value) => value && normalized.includes(String(value).toLowerCase()));
}
// Issue #386 feature-capability roles — mirror the ROLE_FEATURE_* consts in
// src/seed/roles.rs. Their surface forms live in
// data/seed/meanings-feature-capability.lino (embedded in MEANINGS_LINO above).
// detectFeatureCapability walks the `feature_capability_alias` meanings in seed
// declaration order (= the historical FEATURE_CAPABILITIES priority) and takes
// the first whose multilingual aliases occur as a raw substring; the question
// gate and the two action gates reference the other roles. No surface word is
// named here — they all live in the data.
const ROLE_FEATURE_CAPABILITY_ALIAS = "feature_capability_alias";
const ROLE_FEATURE_CAPABILITY_QUESTION = "feature_capability_question";
const ROLE_FEATURE_ACTION_ARITHMETIC = "feature_action_arithmetic";
const ROLE_FEATURE_ACTION_PLANNING = "feature_action_planning";
const FEATURE_CAPABILITIES = [
{
slug: "web_search",
state: "web_search",
labels: { en: "web search", ru: "веб-поиск", hi: "web search", zh: "web search" },
examples: {
en: "Search the web for Nikola Tesla",
ru: "Найди в интернете Никола Тесла",
hi: "Search the web for Nikola Tesla",
zh: "Search the web for Nikola Tesla",
},
},
{
slug: "diagnostics",
state: "diagnostics",
labels: { en: "diagnostics", ru: "диагностика", hi: "diagnostics", zh: "诊断" },
examples: {
en: "Turn on diagnostics",
ru: "Включи диагностику",
hi: "Turn on diagnostics",
zh: "开启诊断",
},
},
{
slug: "agent_mode",
state: "agent_mode",
labels: { en: "agent mode", ru: "agent mode", hi: "agent mode", zh: "agent mode" },
examples: {
en: "Turn on agent mode",
ru: "Включи agent mode",
hi: "Turn on agent mode",
zh: "开启 agent mode",
},
},
{
slug: "definition_fusion",
state: "definition_fusion",
labels: {
en: "automatic definition fusion",
ru: "автоматическое слияние определений",
hi: "automatic definition fusion",
zh: "自动 definition fusion",
},
examples: {
en: "Turn on definition fusion",
ru: "Включи слияние определений",
hi: "Turn on definition fusion",
zh: "开启 definition fusion",
},
},
{
slug: "configuration",
state: "always",
labels: {
en: "message-driven configuration",
ru: "настройка через сообщения",
hi: "message-driven configuration",
zh: "消息驱动设置",
},
examples: {
en: "Switch to dark theme",
ru: "Переключи тему на темную",
hi: "Switch to dark theme",
zh: "切换到深色主题",
},
},
{
slug: "memory_actions",
state: "always",
labels: {
en: "memory import/export",
ru: "импорт и экспорт памяти",
hi: "memory import/export",
zh: "记忆导入/导出",
},
examples: {
en: "Export memory",
ru: "Экспортируй память",
hi: "Export memory",
zh: "导出记忆",
},
},
{
slug: "greeting",
state: "always",
labels: { en: "greetings", ru: "приветствия", hi: "अभिवादन", zh: "问候" },
examples: { en: "Hello", ru: "Привет", hi: "नमस्ते", zh: "你好" },
},
{
slug: "write_program",
state: "always",
labels: {
en: "program template generation",
ru: "генерация программ",
hi: "program template generation",
zh: "程序生成",
},
examples: {
en: "Write a Python program that counts to three",
ru: "Напиши hello world на Rust",
hi: "Write a Python program that counts to three",
zh: "Write a Python program that counts to three",
},
},
{
slug: "concept_lookup",
state: "always",
labels: { en: "concept lookup", ru: "поиск понятий", hi: "concept lookup", zh: "概念查找" },
examples: {
en: "What is Wikipedia?",
ru: "Что такое Википедия?",
hi: "विकिपीडिया क्या है?",
zh: "什么是维基百科?",
},
},
{
slug: "arithmetic",
state: "always",
labels: { en: "arithmetic", ru: "арифметика", hi: "अंकगणित", zh: "算术" },
examples: {
en: "What is 2 + 2?",
ru: "Сколько будет 2 + 2?",
hi: "2 + 2 क्या है?",
zh: "2 + 2 等于多少?",
},
},
{
slug: "translation",
state: "always",
labels: { en: "translation", ru: "перевод", hi: "अनुवाद", zh: "翻译" },
examples: {
en: 'Translate "hello" to Russian',
ru: 'Переведи "hello" на русский',
hi: 'Translate "hello" to Hindi',
zh: 'Translate "hello" to Chinese',
},
},
{
slug: "memory",
state: "always",
labels: {
en: "conversation memory",
ru: "память разговора",
hi: "conversation memory",
zh: "会话记忆",
},
examples: {
en: "My name is Ada. What is my name?",
ru: "Меня зовут Ада. Как меня зовут?",
hi: "My name is Ada. What is my name?",
zh: "My name is Ada. What is my name?",
},
},
{
slug: "demo_mode",
state: "always",
labels: { en: "demo mode", ru: "демо-режим", hi: "demo mode", zh: "演示模式" },
examples: { en: "Turn off demo mode", ru: "Выключи демо", hi: "Turn off demo mode", zh: "关闭演示" },
},
{
slug: "http_url",
state: "always",
labels: {
en: "URL fetch/navigation",
ru: "HTTP-запросы и переходы по URL",
hi: "URL fetch/navigation",
zh: "URL fetch/navigation",
},
examples: {
en: "Navigate to example.com",
ru: "Перейди на example.com",
hi: "Navigate to example.com",
zh: "Navigate to example.com",
},
},
{
slug: "javascript_execution",
state: "always",
labels: {
en: "JavaScript execution",
ru: "выполнение JavaScript",
hi: "JavaScript execution",
zh: "JavaScript execution",
},
examples: {
en: "Run JavaScript: 1 + 1",
ru: "Выполни JavaScript: 1 + 1",
hi: "Run JavaScript: 1 + 1",
zh: "Run JavaScript: 1 + 1",
},
},
{
slug: "planning",
state: "always",
labels: {
en: "summaries, brainstorming, roleplay, and project planning",
ru: "резюме, брейншторминг, роли и планирование проектов",
hi: "summaries, brainstorming, roleplay, and project planning",
zh: "总结、头脑风暴、角色扮演和项目计划",
},
examples: {
en: "Brainstorm 5 project ideas",
ru: "Предложи 5 идей проекта",
hi: "Brainstorm 5 project ideas",
zh: "Brainstorm 5 project ideas",
},
},
];
function localizedValue(record, language) {
if (!record || typeof record !== "object") return "";
return record[language] || record.en || "";
}
// Walk the `feature_capability_alias` meanings in seed declaration order — the
// historical FEATURE_CAPABILITIES priority — and return the first capability
// whose multilingual forms occur as a raw substring of `normalized`, checked in
// the prompt's own language plus English (English prompts check English only).
// The matched meaning's slug, minus its `feature_capability_` prefix, keys
// FEATURE_CAPABILITIES, so no surface alias is named here. Mirrors
// detect_feature_capability in src/solver_handlers/feature_capability.rs (#386).
function detectFeatureCapability(normalized, language) {
const languages = language === "en" ? ["en"] : [language, "en"];
const meaning = firstRoleMatchInLanguagesRaw(
ROLE_FEATURE_CAPABILITY_ALIAS,
normalized,
languages,
);
if (!meaning) return null;
const prefix = "feature_capability_";
if (!meaning.slug.startsWith(prefix)) return null;
const slug = meaning.slug.slice(prefix.length);
return FEATURE_CAPABILITIES.find((feature) => feature.slug === slug) || null;
}
// A prompt is a capability question when one of the `feature_capability_question`
// interrogative cues occurs as a raw substring, checked in the prompt's own
// detected language only. English prompts additionally accept a grammatical
// "is/are ... enabled/available" frame computed in code. Mirrors
// is_feature_capability_question in
// src/solver_handlers/feature_capability.rs (#386).
function isFeatureCapabilityQuestion(normalized, language) {
const mentions = (lang) =>
mentionsRoleInLanguagesRaw(ROLE_FEATURE_CAPABILITY_QUESTION, normalized, [lang]);
if (language === "ru") return mentions("ru");
if (language === "zh") return mentions("zh");
if (language === "hi") return mentions("hi");
return mentions("en") || isEnglishAvailabilityQuestion(normalized);
}
// English-only grammatical "is/are ... enabled/available" availability frame —
// a grammatical pattern (not a vocabulary list), so it stays in code. Mirrors
// is_english_availability_question in
// src/solver_handlers/feature_capability.rs (#386).
function isEnglishAvailabilityQuestion(normalized) {
return /\b(?:is|are)\s+(?:your\s+|the\s+|this\s+|formal-ai\s+)?[\w\s/-]{1,80}\s+(?:enabled|available)\b/.test(
normalized,
);
}
// True when a detected capability question is actually an action request that a
// dedicated handler should answer. The English action frames live in the
// `feature_action_arithmetic` / `feature_action_planning` meanings; they are
// read through wordsForRoleInLanguages restricted to English and reconstructed
// as space-padded forms (prefix for arithmetic, anywhere for planning), so no
// frame is named here. Mirrors is_feature_action_request in
// src/solver_handlers/feature_capability.rs (#386).
function isFeatureActionRequest(normalized, feature) {
if (!feature) return false;
if (feature.slug === "arithmetic") {
return wordsForRoleInLanguages(ROLE_FEATURE_ACTION_ARITHMETIC, ["en"]).some(
(frame) => normalized.startsWith(`${frame} `),
);
}
if (feature.slug === "planning") {
return wordsForRoleInLanguages(ROLE_FEATURE_ACTION_PLANNING, ["en"]).some(
(frame) => normalized.includes(`${frame} `),
);
}
return false;
}
function webSearchStatusContent(language, available, providers) {
const providerList = providers || "none";
const rrfK = webSearchRrfK();
if (language === "ru") {
return available
? `Да. В этой конфигурации веб-поиск включен: я могу использовать DuckDuckGo Instant Answer по умолчанию и доступные CORS-провайдеры (\`${providerList}\`) для явных запросов вроде \`Найди в интернете Никола Тесла\`. Результаты из top-10 по каждому провайдеру объединяются через reciprocal rank fusion (k = ${rrfK}). Если провайдеры отключены или заблокированы в браузерной сессии, я сообщу об этом вместо ответа "да".`
: "Нет. В этой браузерной сессии веб-поиск сейчас недоступен: браузер offline или все CORS-readable поисковые провайдеры отключены после ошибок. Я могу отвечать по локальным правилам и кэшу, но не буду обращаться к поисковым системам.";
}
if (language === "zh") {
return available
? `可以。当前配置启用了 web search:我会默认使用 DuckDuckGo Instant Answer,并可使用这些 CORS-readable provider(\`${providerList}\`)处理明确的搜索请求,例如 \`Search the web for Nikola Tesla\`。每个 provider 的 top-10 结果会用 reciprocal rank fusion 合并(k = ${rrfK})。如果浏览器会话中所有 provider 被禁用或阻止,我会说明不可用,而不是回答可以。`
: "不可以。当前浏览器会话中 web search 不可用:浏览器 offline,或所有 CORS-readable 搜索 provider 都因错误被禁用。我仍可使用本地规则和缓存回答,但不会调用搜索引擎。";
}
if (language === "hi") {
return available
? `हाँ। इस configuration में web search enabled है: मैं default रूप से DuckDuckGo Instant Answer और उपलब्ध CORS-readable providers (\`${providerList}\`) का उपयोग explicit prompts जैसे \`Search the web for Nikola Tesla\` के लिए कर सकता हूँ। हर provider के top-10 results reciprocal rank fusion (k = ${rrfK}) से merge होते हैं। अगर browser session में providers disabled या blocked हों, तो मैं "हाँ" कहने के बजाय स्थिति बताऊँगा।`
: "नहीं। इस browser session में web search अभी available नहीं है: browser offline है या सभी CORS-readable search providers errors के बाद disabled हैं। मैं local rules और cache से जवाब दे सकता हूँ, लेकिन search engines को call नहीं करूँगा।";
}
return available
? `Yes. Web search is enabled in this configuration: I can use DuckDuckGo Instant Answer by default plus the configured CORS-readable providers (\`${providerList}\`) for explicit prompts such as \`Search the web for Nikola Tesla\`. The top-10 results from each provider are merged with reciprocal rank fusion (k = ${rrfK}). If the browser session disables or blocks every provider, I will say that instead of claiming search is available.`
: "No. Web search is unavailable in this browser session: the browser is offline or every CORS-readable search provider has been disabled after errors. I can still answer from local rules and cache, but I will not call search engines.";
}
function featureAvailability(feature, preferences) {
if (!feature) return { available: false, reason: "unknown" };
if (feature.state === "web_search") {
const providers = WEB_SEARCH_PROVIDERS.filter((provider) => !webSearchIsDisabled(provider.id));
const online = typeof navigator === "undefined" || navigator.onLine !== false;
return {
available: online && providers.length > 0,
reason: online && providers.length > 0 ? "none" : "offline_or_no_providers",
providers,
};
}
if (feature.state === "diagnostics") {
const available = Boolean(preferences && preferences.diagnosticsMode);
return { available, reason: available ? "none" : "diagnostics_off" };
}
if (feature.state === "agent_mode") {
const available = Boolean(preferences && preferences.agentMode);
return { available, reason: available ? "none" : "agent_mode_off" };
}
if (feature.state === "definition_fusion") {
const available = definitionFusionByDefault(preferences || {});
return { available, reason: available ? "none" : "definition_fusion_explicit" };
}
return { available: true, reason: "none" };
}
function unavailableReasonText(reason, language) {
const reasons = {
offline_or_no_providers: {
en: "the browser is offline or no search providers are available",
ru: "браузер offline или нет доступных поисковых провайдеров",
hi: "browser offline है या कोई search provider available नहीं है",
zh: "浏览器 offline,或没有可用搜索 provider",
},
diagnostics_off: {
en: "diagnostics are off; enable them to show traces",
ru: "диагностика выключена; включите ее, чтобы видеть трассировку",
hi: "diagnostics off है; trace दिखाने के लिए इसे enable करें",
zh: "诊断已关闭;开启后才会显示 trace",
},
agent_mode_off: {
en: "agent mode is off; multi-step actions require explicit opt-in",
ru: "agent mode выключен; для многошаговых действий нужен явный opt-in",
hi: "agent mode off है; multi-step actions के लिए explicit opt-in चाहिए",
zh: "agent mode 已关闭;多步骤操作需要显式启用",
},
definition_fusion_explicit: {
en: "automatic definition fusion is set to explicit-only",
ru: "автоматическое слияние определений работает только после включения режима auto",
hi: "automatic definition fusion के लिए auto mode enable करना होगा",
zh: "自动 definition fusion 需要切换到 auto 模式",
},
};
return localizedValue(reasons[reason] || { en: "not available" }, language);
}
function featureCapabilityContent(feature, language, availability) {
if (feature.slug === "web_search") {
const providers = availability.providers || [];
return webSearchStatusContent(
language,
availability.available,
providers.map((provider) => provider.id).join(", "),
);
}
const label = localizedValue(feature.labels, language);
const example = localizedValue(feature.examples, language);
if (availability.available) {
if (language === "ru") {
return `Да. Возможность «${label}» доступна в этой конфигурации. Пример сообщения: \`${example}\`.`;
}
if (language === "zh") {
return `可以。当前配置中「${label}」可用。示例消息:\`${example}\`。`;
}
if (language === "hi") {
return `हाँ। इस configuration में \`${label}\` available है। Example message: \`${example}\`.`;
}
return `Yes. ${label} is available in this configuration. Example message: \`${example}\`.`;
}
const reason = unavailableReasonText(availability.reason, language);
if (language === "ru") {
return `Нет. Возможность «${label}» сейчас недоступна в этой конфигурации: ${reason}. Пример сообщения после включения: \`${example}\`.`;
}
if (language === "zh") {
return `不可以。当前配置中「${label}」不可用:${reason}。启用后的示例消息:\`${example}\`。`;
}
if (language === "hi") {
return `नहीं। इस configuration में \`${label}\` अभी available नहीं है: ${reason}. Enable करने के बाद example message: \`${example}\`.`;
}
return `No. ${label} is not available in this configuration: ${reason}. Example message after enabling it: \`${example}\`.`;
}
function tryFeatureCapabilityStatus(prompt, normalized, language, preferences) {
if (!isFeatureCapabilityQuestion(normalized, language)) return null;
const feature = detectFeatureCapability(normalized, language);
if (!feature) return null;
if (isFeatureActionRequest(normalized, feature)) return null;
const availability = featureAvailability(feature, preferences || {});
const providers = WEB_SEARCH_PROVIDERS.filter((provider) => !webSearchIsDisabled(provider.id));
return {
intent: "capabilities",
content: featureCapabilityContent(feature, language, availability),
confidence: availability.available ? 0.95 : 0.6,
evidence: [
"handler:capabilities",
`feature:question:${feature.slug}`,
availability.available
? `feature:available:${feature.slug}`
: `feature:unavailable:${feature.slug}:${availability.reason}`,
...(feature.slug === "web_search" ? providers.map((provider) => `web_search:provider:${provider.id}`) : []),
`language:${language}`,
],
};
}
// Issue #386: recognise "what else can you do" / "что ещё ты умеешь" /
// "और क्या कर सकते" / "你还能做什么" by the capability_query_more meaning role
// rather than a hardcoded per-language phrase list. Recognition is
// language-agnostic because the surface words are script-specific; the response
// body is still chosen by the caller from detectLanguage. The prompt is
// re-normalised so trailing punctuation collapses to the canonical spacing the
// seed stores. Mirror of is_more_capabilities_prompt in
// src/solver_handlers/user_intent.rs.
function isMoreCapabilitiesPrompt(normalized) {
return lexiconMentionsRole(ROLE_CAPABILITY_QUERY_MORE, normalizePrompt(normalized));
}
// Issue #386: recognise "what can you do" / "что ты умеешь" / "что за дичь" /
// "आप क्या कर सकते" / "你能做什么" by the capability_query meaning role — plus
// its follow-up capability_query_more, so "what else can you do" still counts —
// rather than a hardcoded per-language phrase list. Mirror of
// is_capability_query in src/solver_handlers/user_intent.rs.
function isCapabilityQuery(normalized) {
const cleaned = normalizePrompt(normalized);
return (
lexiconMentionsRole(ROLE_CAPABILITY_QUERY, cleaned) ||
lexiconMentionsRole(ROLE_CAPABILITY_QUERY_MORE, cleaned)
);
}
function historyMentionsWebSearch(history) {
if (!Array.isArray(history)) return false;
return history.some((turn) => {
const content = String(turn && turn.content ? turn.content : "").toLowerCase();
return lexiconMentionsRoleSubstring(ROLE_WEB_SEARCH_HISTORY_SIGNAL, content);
});
}
function additionalCapabilitiesContent(language) {
if (language === "ru") {
return "Кроме уже названных возможностей, могу ещё:\n\n- **Арифметика**: вычислять выражения вроде «Сколько будет 2 + 2?»\n- **Перевод**: переводить короткие фразы между поддерживаемыми языками.\n- **Поиск понятий**: объяснять термины, например «Что такое Википедия?»\n- **Hello World**: генерировать минимальные программы на Rust, Python, JavaScript, Go, C и других языках.\n- **Память диалога**: использовать предыдущие сообщения текущей сессии.\n- **Правила поведения**: показывать встроенные правила через `Покажи правила поведения` и `Покажи правило unknown`.\n- **Настройки и действия**: включать диагностику/демо/agent mode, менять тему, язык, стиль чата, экспортировать и импортировать память.";
}
return "Beyond the capability already discussed, I can also:\n\n- **Arithmetic**: evaluate expressions like `2 + 2`.\n- **Translation**: translate short phrases between supported languages.\n- **Concept lookup**: explain terms such as `What is Wikipedia?`.\n- **Hello World**: generate small programs in Rust, Python, JavaScript, Go, C, and more.\n- **Conversation memory**: use earlier messages from the current session.\n- **Behavior rules**: show built-in rules with `List behavior rules` and `Show behavior rule unknown`.\n- **Settings and actions**: configure diagnostics, demo mode, agent mode, theme, language, chat style, and memory import/export.";
}
// True when the prompt asks how the assistant itself is built rather than
// requesting a task. Decomposes exactly like the Rust is_architecture_question:
// the prompt must address the assistant — carry an assistant_self_reference
// surface — and name an architecture_concept such as a language model, neural
// network, or the project's local rules. Both are matched as raw substrings
// across all four languages; no architecture word is hardcoded here.
function isArchitectureQuestion(normalized) {
if (!lexiconMentionsRoleSubstring(ROLE_ASSISTANT_SELF_REFERENCE, normalized)) {
return false;
}
return lexiconMentionsRoleSubstring(ROLE_ARCHITECTURE_CONCEPT, normalized);
}
function architectureLanguage(prompt, normalized) {
return selfAwarenessLanguage(prompt, normalized);
}
function architectureExplanationContent(language) {
const surface = BROWSER_SURFACE;
if (language === "ru") {
return `Я не LLM-рантайм и не выполняю нейросетевой инференс. Текущая среда: ${surface.label} (\`${surface.slug}\`). Рантайм: ${surface.runtime}. У проекта есть OpenAI-совместимые API-форматы, но ответы строит детерминированный solver: сначала он проверяет локальный seed Links Notation, правила и память (${surface.memory}); затем веб-поиск используется только с учетом среды: ${surface.webSearch}. Весь интернет не загружен в локальные правила целиком.`;
}
if (language === "hi") {
return `मैं LLM runtime नहीं हूँ और neural inference नहीं चलाता. Current environment: ${surface.label} (\`${surface.slug}\`). Runtime: ${surface.runtime}. Project OpenAI-compatible API shapes देता है, लेकिन जवाब deterministic solver बनाता है: पहले local Links Notation seed, rules और memory (${surface.memory}) देखता है; फिर web search केवल environment अनुमति दे तो उपयोग करता है: ${surface.webSearch}. पूरा internet local rules में preload नहीं है.`;
}
if (language === "zh") {
return `我不是 LLM runtime, 也不执行神经网络推理。当前环境: ${surface.label} (\`${surface.slug}\`)。Runtime: ${surface.runtime}。项目提供 OpenAI-compatible API 形状, 但回答由确定性的 solver 生成: 先检查本地 Links Notation seed、规则和记忆 (${surface.memory}); 然后只在当前环境允许时使用 web search: ${surface.webSearch}。整个互联网不会预加载到本地规则中。`;
}
return `I am not an LLM runtime and I do not perform neural inference. Current environment: ${surface.label} (\`${surface.slug}\`). Runtime: ${surface.runtime}. The project exposes OpenAI-compatible API shapes, but answers come from a deterministic solver: it checks the local Links Notation seed, rules, and memory (${surface.memory}) first; web search is used only when this environment allows it: ${surface.webSearch}. The whole internet is not preloaded into local rules.`;
}
function tryArchitectureExplanation(prompt, normalized) {
if (!isArchitectureQuestion(normalized)) return null;
const language = architectureLanguage(prompt, normalized);
return {
intent: "meta_explanation",
content: architectureExplanationContent(language),
confidence: 1.0,
evidence: ["response:meta_explanation", "meta_explanation:architecture", `language:${language}`],
};
}
function tryCapabilities(prompt, normalized, preferences, history) {
const language = detectLanguage(prompt);
const featureStatus = tryFeatureCapabilityStatus(prompt, normalized, language, preferences);
if (featureStatus) return featureStatus;
const moreCapabilities = isMoreCapabilitiesPrompt(normalized);
if (!isCapabilityQuery(normalized)) return null;
if (moreCapabilities) {
const priorSearch = historyMentionsWebSearch(history);
return {
intent: "capabilities",
content: additionalCapabilitiesContent(language),
confidence: 1.0,
evidence: [
"handler:capabilities",
"capabilities:follow_up",
...(priorSearch ? ["capabilities:history:prior_web_search"] : []),
`language:${language}`,
],
};
}
const content =
language === "ru"
? "Я formal-ai — детерминированный символьный ИИ. Вот что я умею:\n\n- **Приветствия**: отвечаю на «Привет», «Здравствуйте» и т.п.\n- **Hello World**: генерирую программы на Rust, Python, JavaScript, Go, C и других языках.\n- **Веб-поиск**: ищу в интернете через DuckDuckGo, Wikipedia и Wikidata, когда поиск доступен.\n- **Поиск понятий**: объясняю термины — попробуйте «Что такое Википедия?»\n- **Арифметика**: вычисляю выражения — например, «Сколько будет 2 + 2?»\n- **Перевод**: перевожу фразы между языками.\n- **Память**: помню контекст разговора в рамках сессии.\n- **Настройки и действия**: через сообщения можно включать диагностику/демо/agent mode, менять тему, язык, стиль чата и экспортировать или импортировать память.\n\nЯ работаю на основе локальных символьных правил, без нейросетевого инференса."
: language === "zh"
? "我是 formal-ai —— 一个确定性的符号化 AI。以下是我的功能:\n\n- **问候**:回应「你好」等问候语。\n- **Hello World**:生成 Rust、Python、JavaScript、Go、C 等语言的示例程序。\n- **Web search**:在可用时通过 DuckDuckGo、Wikipedia 和 Wikidata 搜索互联网。\n- **概念查找**:解释术语,例如「什么是维基百科?」\n- **算术**:计算表达式,例如「2 + 2 等于多少?」\n- **翻译**:在语言之间翻译短语。\n- **记忆**:在会话中记住上下文。\n- **设置和操作**:可通过消息开启诊断、演示、agent mode,切换主题、语言、聊天样式,并导出或导入记忆。\n\n我基于本地符号规则运行,不进行神经网络推理。"
: language === "hi"
? "मैं formal-ai हूँ — एक नियतात्मक प्रतीकात्मक AI। मैं यह कर सकता हूँ:\n\n- **अभिवादन**: «नमस्ते» आदि का जवाब देना।\n- **Hello World**: Rust, Python, JavaScript, Go, C आदि में प्रोग्राम बनाना।\n- **Web search**: उपलब्ध होने पर DuckDuckGo, Wikipedia, और Wikidata से इंटरनेट में खोजना।\n- **अवधारणा खोज**: शब्दों को समझाना — जैसे «विकिपीडिया क्या है?»\n- **अंकगणित**: गणनाएँ — जैसे «2 + 2 क्या है?»\n- **अनुवाद**: भाषाओं के बीच अनुवाद।\n- **स्मृति**: सत्र में संदर्भ याद रखना।\n- **Settings और actions**: messages से diagnostics/demo/agent mode बदलना, theme/language/chat style बदलना, और memory export/import करना।\n\nमैं स्थानीय प्रतीकात्मक नियमों पर चलता हूँ, कोई न्यूरल इन्फेरेन्स नहीं।"
: "I am formal-ai, a deterministic symbolic AI. Here is what I can do:\n\n- **Greetings**: respond to «Hi», «Hello», and similar.\n- **Hello World**: generate programs in Rust, Python, JavaScript, Go, C, and more.\n- **Web search**: search the internet through DuckDuckGo, Wikipedia, and Wikidata when available.\n- **Concept lookup**: explain terms — try «What is Wikipedia?»\n- **Arithmetic**: evaluate expressions — try «What is 2 + 2?»\n- **Translation**: translate phrases between languages.\n- **Memory**: recall context within the current session.\n- **Settings and actions**: configure diagnostics, demo mode, agent mode, theme, language, chat style, and memory import/export from messages.\n\nI run on local symbolic rules, without any neural network inference.";
return {
intent: "capabilities",
content,
confidence: 1.0,
evidence: ["handler:capabilities", `language:${language}`],
};
}
// Issue #386: the source/target language of a translation prompt is read from
// the lexicon, not a hardcoded per-language phrase ladder. Each translation
// source/target marker meaning enumerates its surfaces across all four
// languages and is defined_by the language_* meaning it names; detection walks
// those meanings in declaration order (en, ru, hi, zh) and resolves the code
// through defined_by. Mirrors detect_source_language / detect_target_language
// in src/translation/language_markers.rs.
function detectTranslationSourceLanguage(normalized) {
return detectTranslationMarkerLanguage(
ROLE_TRANSLATION_SOURCE_MARKER,
normalized,
);
}
function detectTranslationTargetLanguage(normalized) {
return detectTranslationMarkerLanguage(
ROLE_TRANSLATION_TARGET_MARKER,
normalized,
);
}
// Offline meaning registry for the browser worker.
//
// The Rust pipeline (`src/translation/pipeline.rs`) resolves any pair
// of surfaces through Wiktionary + Wikidata using cached HTTP
// responses. The worker mirrors that with a live `liveWiktionaryTranslate`
// fallback below (MediaWiki action API is CORS-friendly via
// `origin=*`), but keeps this small in-memory registry of greetings and
// stock phrases so the demo stays snappy when the network is slow.
// `primary` is the canonical form deformalization renders; `aliases` is a
// list of normalized alternative surfaces used during formalization.
const TRANSLATION_MEANING_REGISTRY = [
{
token: "greeting",
primary: { en: "Hello", ru: "Привет", hi: "नमस्ते", zh: "你好" },
aliases: {
en: ["hello", "hi", "hey"],
ru: ["привет", "здравствуйте", "здравствуй"],
hi: ["नमस्ते", "नमस्कार"],
zh: ["你好", "您好"],
},
},
{
token: "greeting_how_are_you",
primary: {
en: "How are you?",
ru: "Как у тебя дела?",
hi: "आप कैसे हैं?",
zh: "你好吗?",
},
aliases: {
en: ["howareyou", "hellohowareyou", "hihowareyou"],
ru: [
"какдела",
"какутебядела",
"какувасдела",
"какваши дела",
"какватидела",
"какваши",
"приветкакдела",
"здравствуйтекаквашидела",
],
hi: ["आपकैसेहैं", "तुमकैसेहो"],
zh: ["你好吗", "你怎么样"],
},
},
{
token: "thank_you",
primary: { en: "Thank you", ru: "Спасибо", hi: "धन्यवाद", zh: "谢谢" },
aliases: {
en: ["thanks", "thankyou", "thankyouverymuch"],
ru: ["спасибо", "благодарю", "большоеспасибо"],
hi: ["धन्यवाद", "शुक्रिया"],
zh: ["谢谢", "多谢", "感谢"],
},
},
{
token: "you_are_welcome",
primary: {
en: "You are welcome",
ru: "Пожалуйста",
hi: "आपका स्वागत है",
zh: "不客气",
},
aliases: {
en: ["youarewelcome", "yourewelcome", "nottoworry"],
ru: ["пожалуйста", "незачто"],
hi: ["आपकास्वागतहै", "कोईबातनहीं"],
zh: ["不客气", "不用谢"],
},
},
{
token: "goodbye",
primary: { en: "Goodbye", ru: "До свидания", hi: "अलविदा", zh: "再见" },
aliases: {
en: ["goodbye", "bye", "seeyou", "byebye"],
ru: ["досвидания", "пока", "прощай"],
hi: ["अलविदा", "फिरमिलेंगे"],
zh: ["再见", "拜拜"],
},
},
{
token: "good_morning",
primary: { en: "Good morning", ru: "Доброе утро", hi: "सुप्रभात", zh: "早上好" },
aliases: {
en: ["goodmorning"],
ru: ["доброеутро"],
hi: ["सुप्रभात", "शुभप्रभात"],
zh: ["早上好", "早安"],
},
},
{
token: "good_evening",
primary: { en: "Good evening", ru: "Добрый вечер", hi: "शुभ संध्या", zh: "晚上好" },
aliases: {
en: ["goodevening"],
ru: ["добрыйвечер"],
hi: ["शुभसंध्या"],
zh: ["晚上好", "晚安"],
},
},
{
token: "what_is_your_name",
primary: {
en: "What is your name?",
ru: "Как тебя зовут?",
hi: "तुम्हारा नाम क्या है?",
zh: "你叫什么名字?",
},
aliases: {
en: ["whatisyourname", "whatsyourname"],
ru: ["кактебязовут", "каквасзовут"],
hi: ["तुम्हारानामक्याहै", "आपकानामक्याहै"],
zh: ["你叫什么名字", "您叫什么名字"],
},
},
{
token: "who_are_you",
primary: {
en: "Who are you?",
ru: "Кто ты такой?",
hi: "तुम कौन हो?",
zh: "你是谁?",
},
aliases: {
en: ["whoareyou"],
ru: ["ктоты", "ктотытакой", "ктотытакая", "ктовы", "ктовытакой", "ктовытакая"],
hi: ["तुमकौनहो", "आपकौनहैं"],
zh: ["你是谁", "您是谁"],
},
},
{
token: "what_is_this",
primary: {
en: "What is this?",
ru: "Что это такое?",
hi: "यह क्या है?",
zh: "这是什么?",
},
aliases: {
en: ["whatisthis", "whatisit"],
ru: ["чтоэто", "чтоэтотакое"],
hi: ["यहक्याहै", "येक्याहै"],
zh: ["这是什么", "這是什麼"],
},
},
{
token: "i_am_fine",
primary: { en: "I am fine", ru: "У меня всё хорошо", hi: "मैं ठीक हूँ", zh: "我很好" },
aliases: {
en: ["iamfine", "imfine", "imdoingfine", "imdoingwell"],
ru: ["уменявсёхорошо", "уменявсехорошо", "всёхорошо"],
hi: ["मैंठीकहूँ", "मैंठीकहूं"],
zh: ["我很好", "我挺好的"],
},
},
{
token: "yes",
primary: { en: "Yes", ru: "Да", hi: "हाँ", zh: "是" },
aliases: {
en: ["yes", "yeah", "yep", "aye"],
ru: ["да", "ага", "конечно"],
hi: ["हाँ", "हां", "जी"],
zh: ["是", "是的", "对"],
},
},
{
token: "no",
primary: { en: "No", ru: "Нет", hi: "नहीं", zh: "不" },
aliases: {
en: ["no", "nope", "nah"],
ru: ["нет", "неа"],
hi: ["नहीं", "ना"],
zh: ["不", "不是"],
},
},
// Issue #216 / #217: the apple noun must be translatable in both
// directions from the browser demo, including unquoted prompts.
{
token: "apple",
primary: { en: "apple", ru: "яблоко", hi: "सेब", zh: "苹果" },
aliases: {
en: ["apple", "apples"],
ru: [
"яблоко",
"яблока",
"яблоку",
"яблоком",
"яблоке",
"яблоки",
"яблок",
"яблокам",
"яблоками",
"яблоках",
],
hi: ["सेब"],
zh: ["苹果"],
},
},
];
const TRANSLATION_TERMINAL_PUNCTUATION = ["?", "!", ".", "。", "?", "!", "."];
function normalizeTranslationAlias(surface) {
return Array.from(String(surface || "").toLowerCase())
.filter((character) => /[\p{L}\p{N}]/u.test(character))
.join("");
}
function formalizeSurface(surface, source) {
const normalized = normalizeTranslationAlias(surface);
if (!normalized) return null;
for (const entry of TRANSLATION_MEANING_REGISTRY) {
const aliases = (entry.aliases && entry.aliases[source]) || [];
if (aliases.some((alias) => normalizeTranslationAlias(alias) === normalized)) {
return entry.token;
}
const primary = entry.primary && entry.primary[source];
if (primary && normalizeTranslationAlias(primary) === normalized) {
return entry.token;
}
}
return null;
}
function deformalizeMeaning(token, target) {
for (const entry of TRANSLATION_MEANING_REGISTRY) {
if (entry.token !== token) continue;
const primary = entry.primary && entry.primary[target];
return primary || null;
}
return null;
}
function canonicalTokenForNormalized(normalized) {
if (!normalized) return null;
for (const entry of TRANSLATION_MEANING_REGISTRY) {
const aliasesByLang = entry.aliases || {};
for (const lang of Object.keys(aliasesByLang)) {
const aliases = aliasesByLang[lang] || [];
if (aliases.some((alias) => normalizeTranslationAlias(alias) === normalized)) {
return entry.token;
}
}
const primaryByLang = entry.primary || {};
for (const lang of Object.keys(primaryByLang)) {
if (normalizeTranslationAlias(primaryByLang[lang]) === normalized) {
return entry.token;
}
}
}
return null;
}
function canonicalMeaningToken(raw) {
return canonicalTokenForNormalized(raw) || raw;
}
function normalizeMeaningText(surface) {
const raw = normalizeTranslationAlias(surface);
return canonicalMeaningToken(raw);
}
function matchSourceFormatting(target, source) {
const targetTrimmed = String(target || "").trim();
if (!targetTrimmed) return "";
const sourceTrimmed = String(source || "").trim();
let sourceTerminal = null;
if (sourceTrimmed.length > 0) {
const lastChar = Array.from(sourceTrimmed).pop();
if (TRANSLATION_TERMINAL_PUNCTUATION.includes(lastChar)) sourceTerminal = lastChar;
}
let targetNoTerminal = targetTrimmed;
while (
targetNoTerminal.length > 0 &&
TRANSLATION_TERMINAL_PUNCTUATION.includes(Array.from(targetNoTerminal).pop())
) {
const lastChar = Array.from(targetNoTerminal).pop();
targetNoTerminal = targetNoTerminal.slice(0, targetNoTerminal.length - lastChar.length);
}
const withTerminal = sourceTerminal ? targetNoTerminal + sourceTerminal : targetNoTerminal;
const sourceFirstLetter = Array.from(sourceTrimmed).find((character) =>
/\p{L}/u.test(character),
);
if (!sourceFirstLetter) return withTerminal;
const targetChars = Array.from(withTerminal);
const targetFirstIdx = targetChars.findIndex((character) => /\p{L}/u.test(character));
if (targetFirstIdx === -1) return withTerminal;
const targetFirstLetter = targetChars[targetFirstIdx];
const sourceLower = sourceFirstLetter.toLowerCase() === sourceFirstLetter
&& sourceFirstLetter.toUpperCase() !== sourceFirstLetter;
const sourceUpper = sourceFirstLetter.toUpperCase() === sourceFirstLetter
&& sourceFirstLetter.toLowerCase() !== sourceFirstLetter;
const targetLower = targetFirstLetter.toLowerCase() === targetFirstLetter
&& targetFirstLetter.toUpperCase() !== targetFirstLetter;
const targetUpper = targetFirstLetter.toUpperCase() === targetFirstLetter
&& targetFirstLetter.toLowerCase() !== targetFirstLetter;
if (sourceLower && targetUpper) {
targetChars[targetFirstIdx] = targetFirstLetter.toLowerCase();
return targetChars.join("");
}
if (sourceUpper && targetLower) {
targetChars[targetFirstIdx] = targetFirstLetter.toUpperCase();
return targetChars.join("");
}
return withTerminal;
}
function normalizeComposableSurface(surface) {
return String(surface || "")
.trim()
.replace(/[?!.。?!.]+$/u, "")
.toLowerCase()
.split(/\s+/u)
.filter(Boolean)
.join(" ");
}
// Issue #386 compositional-translation roles — mirror ROLE_COMPOSITIONAL_LEMMA,
// ROLE_COMPOSITIONAL_PHRASE and ROLE_COMPOSITIONAL_GENITIVE_HEAD in
// src/seed/roles.rs. The per-word lemma fallbacks, fixed phrases, genitive-
// governing heads and the single genitive-tagged complement that used to be
// hardcoded here all live in the embedded MEANINGS_LINO
// (data/seed/meanings-translation.lino); the functions below name only the
// semantic roles and the ru→en language pair, never the surface words. The
// query helpers (roleSurfaceTranslation, roleListsSurface,
// roleActionSurfaceTranslation, wordIn) are defined alongside meaningLexicon.
const ROLE_COMPOSITIONAL_LEMMA = "compositional_lemma";
const ROLE_COMPOSITIONAL_PHRASE = "compositional_phrase";
const ROLE_COMPOSITIONAL_GENITIVE_HEAD = "compositional_genitive_head";
function capitalizeAsciiFirst(surface) {
const text = String(surface || "");
if (!text) return "";
return text[0].toUpperCase() + text.slice(1);
}
function translateRussianWordSequence(words) {
const translated = [];
for (let index = 0; index < words.length; index += 1) {
const word = words[index];
const next = words[index + 1];
if (
next &&
roleListsSurface(ROLE_COMPOSITIONAL_GENITIVE_HEAD, "ru", word) &&
roleActionSurfaceTranslation(ROLE_COMPOSITIONAL_LEMMA, "genitive", "ru", "en", next)
) {
translated.push(
roleSurfaceTranslation(ROLE_COMPOSITIONAL_LEMMA, "ru", "en", word),
"of",
roleActionSurfaceTranslation(ROLE_COMPOSITIONAL_LEMMA, "genitive", "ru", "en", next),
);
index += 1;
continue;
}
const surface = roleSurfaceTranslation(ROLE_COMPOSITIONAL_LEMMA, "ru", "en", word);
if (!surface) return null;
translated.push(surface);
}
return capitalizeAsciiFirst(translated.join(" "));
}
function translateCompositionalSurface(surface, source, target) {
if (source !== "ru" || target !== "en") return null;
const normalized = normalizeComposableSurface(surface);
const phrase = roleSurfaceTranslation(ROLE_COMPOSITIONAL_PHRASE, "ru", "en", normalized);
if (phrase) return phrase;
const words = normalized.split(/\s+/u).filter(Boolean);
if (words.length < 2 || words.length > 8) return null;
return translateRussianWordSequence(words);
}
function detectLanguageSlug(text) {
let latin = 0;
let cyrillic = 0;
let devanagari = 0;
let cjk = 0;
let other = 0;
for (const character of String(text || "")) {
const code = character.codePointAt(0);
if (/[a-z]/i.test(character)) latin += 1;
else if (code >= 0x0400 && code <= 0x04ff) cyrillic += 1;
else if (code >= 0x0900 && code <= 0x097f) devanagari += 1;
else if (code >= 0x4e00 && code <= 0x9fff) cjk += 1;
else if (/\p{L}/u.test(character)) other += 1;
}
const total = latin + cyrillic + devanagari + cjk + other;
if (total === 0) return "en";
if (other > latin && other >= cyrillic && other >= devanagari && other >= cjk) {
return "unknown";
}
if (cyrillic >= Math.max(latin, devanagari, cjk) && cyrillic > 0) return "ru";
if (devanagari >= Math.max(latin, cyrillic, cjk) && devanagari > 0) return "hi";
if (cjk >= Math.max(latin, cyrillic, devanagari) && cjk > 0) return "zh";
return "en";
}
function inferTranslationSource(prompt) {
const lower = String(prompt || "").toLowerCase();
const surface = extractQuotedPhrase(prompt) || extractUnquotedTranslationSurface(prompt);
if (surface) {
const detected = detectLanguageSlug(surface);
if (detected !== "unknown") return detected;
}
// Issue #386: the source language of an un-annotated request is the language
// the user issued the *translation command* in. Ask the lexicon which
// language's command verb the prompt carries — the stems live once in the
// embedded translate meaning; this code knows only the concept and the
// language-code bridge. English is the default when no command verb is present.
return (
firstRoleLanguage(ROLE_TRANSLATION_ACTION, lower, ["ru", "hi", "zh"]) || "en"
);
}
// Live Wiktionary fallback (issue #221). When the offline meaning
// registry above does not cover `surface`, fetch the Wiktionary page
// for `source` and pull the first `{{tt+|<target>|...}}` (or `{{t+}}` /
// `{{t}}`) entry. Mirrors the Rust pipeline's Stage 1a in
// `src/translation/pipeline.rs`: if the main page delegates noun
// translations via `{{see translation subpage|...}}`, fetch the
// subpage and search it first. Keeps the worker mobile-friendly: no
// offline dictionary bundled, just a single CORS-safe HTTP call.
async function fetchWiktionaryWikitext(pageTitle, language) {
if (typeof fetch !== "function" || !pageTitle) return null;
const host = WIKTIONARY_SEARCH_HOSTS[language] || WIKTIONARY_SEARCH_HOSTS.en;
const url = `${host}?action=parse&page=${encodeURIComponent(
pageTitle,
)}&prop=wikitext&format=json&origin=*`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
},
});
if (!response || !response.ok) return null;
const data = await response.json();
return (data && data.parse && data.parse.wikitext && data.parse.wikitext["*"]) || null;
} catch (_error) {
return null;
}
}
function stripCombiningMarks(value) {
// Russian Wiktionary entries are stored with combining stress marks
// (U+0301) so readers can see where the accent falls. The surface
// form must drop them so the result matches the lemma (помидо́р →
// помидор) and downstream substring assertions still hit.
return typeof value === "string" && value.normalize
? value.normalize("NFD").replace(/[̀-ͯ]/g, "").normalize("NFC")
: value;
}
function extractWiktionaryTranslation(wikitext, targetLang) {
if (!wikitext || !targetLang) return null;
// English-edition templates: {{t|<lang>|...}}, {{t+|<lang>|...}},
// {{tt|<lang>|...}}, {{tt+|<lang>|...}}.
const enPattern = new RegExp(
`\\{\\{tt?\\+?\\|${targetLang}\\|([^|}\\n]+)`,
"i",
);
const enMatch = enPattern.exec(wikitext);
if (enMatch) {
const surface = stripCombiningMarks(String(enMatch[1] || "").trim());
if (surface) return surface;
}
// Russian-edition translation blocks: `{{перев-блок|...|<lang>=[[surface]]\n|...}}`.
// The language code may appear at the very start (no leading newline)
// or after `\n|`; the surface can be inside `[[...]]`, optionally
// followed by transliteration in parentheses we drop.
const ruPattern = new RegExp(
`[|\\n]${targetLang}\\s*=\\s*(?:\\[\\[([^\\]|]+)(?:\\|[^\\]]+)?\\]\\]|([^\\n|}]+))`,
"i",
);
const ruMatch = ruPattern.exec(wikitext);
if (ruMatch) {
const raw = (ruMatch[1] || ruMatch[2] || "").trim();
const surface = stripCombiningMarks(raw.replace(/\s*\([^)]*\)\s*$/, "").trim());
if (surface) return surface;
}
return null;
}
async function resolveWiktionaryLemma(surface, language) {
// Inflected forms (e.g. Russian plural `помидоры`) are not always stored
// as separate pages on the source-language Wiktionary. OpenSearch returns
// the closest matching titles; the first hit is the dictionary lemma
// (`помидор`) we want to look up next.
if (typeof fetch !== "function" || !surface) return null;
const host = WIKTIONARY_SEARCH_HOSTS[language] || WIKTIONARY_SEARCH_HOSTS.en;
const url = `${host}?action=opensearch&search=${encodeURIComponent(
surface,
)}&limit=1&format=json&origin=*`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
},
});
if (!response || !response.ok) return null;
const data = await response.json();
const titles = Array.isArray(data) && Array.isArray(data[1]) ? data[1] : [];
const lemma = titles[0];
if (typeof lemma !== "string" || !lemma || lemma === surface) return null;
return lemma;
} catch (_error) {
return null;
}
}
async function liveWiktionaryTranslate(surface, source, target) {
// Run the direct page fetch and the OpenSearch lemma resolution in
// parallel. For inflected forms (e.g. `помидоры`) the direct fetch
// 404s, and chaining the lemma lookup sequentially after it added a
// third sequential round-trip that pushed CI past the 5s expect cap.
const [direct, lemma] = await Promise.all([
fetchWiktionaryWikitext(surface, source),
resolveWiktionaryLemma(surface, source),
]);
let main = direct;
if (!main && lemma) {
main = await fetchWiktionaryWikitext(lemma, source);
}
if (!main) return null;
let wikitext = main;
if (/\{\{see translation subpage\|/i.test(main)) {
const subpage = await fetchWiktionaryWikitext(`${surface}/translations`, source);
if (subpage) wikitext = `${subpage}\n${main}`;
}
return extractWiktionaryTranslation(wikitext, target);
}
async function translateSurface(surface, source, target) {
if (source === target) {
return { surface: String(surface || ""), gap: false };
}
const token = formalizeSurface(surface, source);
if (token) {
const primary = deformalizeMeaning(token, target);
if (primary) return { surface: primary, gap: false };
}
if (surface) {
const live = await liveWiktionaryTranslate(surface, source, target);
if (live) return { surface: live, gap: false };
}
const compositional = translateCompositionalSurface(surface, source, target);
if (compositional) return { surface: compositional, gap: false };
return { surface: null, gap: true };
}
function renderTranslationGap(surface, source, target) {
const trimmed = String(surface || "").trim();
if (!trimmed) {
return `I could not identify a source phrase to translate from ${source} to ${target}.`;
}
return `I could not translate "${trimmed}" from ${source} to ${target} with the available formalization data. I recorded this as a translation gap for follow-up.`;
}
async function tryTranslation(prompt, normalized) {
const targetHint = detectTranslationTargetLanguage(normalized);
// Issue #386: recognise a translation command by *meaning*, not by hardcoded
// verbs. The command stems live once in the embedded translate meaning; this
// code knows the concept and the head-initial/head-final typology. Clause-
// initial English/Russian commands are matched as a prefix; head-final
// Hindi/Chinese place the verb later, so they are matched anywhere but gated
// by a target marker to avoid firing on an incidental verb noun.
const headInitialCommand = wordsForRoleInLanguages(ROLE_TRANSLATION_ACTION, [
"en",
"ru",
]).some((stem) => normalized.startsWith(stem));
const headFinalCommand =
Boolean(targetHint) &&
wordsForRoleInLanguages(ROLE_TRANSLATION_ACTION, ["hi", "zh"]).some((stem) =>
normalized.includes(stem),
);
const isTranslationRequest = headInitialCommand || headFinalCommand;
if (!isTranslationRequest) return null;
// Issue #216: fall back to an unquoted surface (`translate apple to
// russian`) when no quoted fragment is present so the offline registry
// can still resolve a meaning token.
const surface =
extractQuotedPhrase(prompt) || extractUnquotedTranslationSurface(prompt) || "";
const surfaceMeaning = surface || prompt;
const source = detectTranslationSourceLanguage(normalized) || inferTranslationSource(prompt);
const target = targetHint || "en";
const meaningId = stableBehaviorRuleId("meaning", normalizeMeaningText(surfaceMeaning));
const translation = await translateSurface(surface, source, target);
let content;
if (translation.gap) {
content = renderTranslationGap(surface, source, target);
} else {
const translatedSurface = matchSourceFormatting(translation.surface || "", surface);
content = surface ? `"${translatedSurface}"` : translatedSurface;
}
const evidence = [
"handler:translation",
`language_from:${source}`,
`language_to:${target}`,
`meaning:${meaningId}`,
];
if (translation.gap && surface) evidence.push(`translation_gap:${surface}`);
return {
intent: `translate_${source}_to_${target}`,
content,
confidence: 1.0,
evidence,
};
}
// The number of brainstorm items returned when the prompt names no count.
const DEFAULT_BRAINSTORM_COUNT = 5;
// Read the integer value of a cardinal-number meaning from its own data. Each
// cardinal carries a numeral word form (e.g. "10") — the script-independent
// surface that spells the value — so the count is derived from the seed rather
// than restated as a literal. Mirrors cardinal_value in
// src/solver_handlers/benchmark_prompts.rs (issue #386).
function cardinalValue(meaning) {
if (!meaning || !Array.isArray(meaning.words)) return null;
const numeral = meaning.words.find((word) => /^[0-9]+$/.test(word));
if (numeral === undefined) return null;
const value = Number.parseInt(numeral, 10);
return Number.isNaN(value) ? null : value;
}
// Parse the number of items the user asked for, defaulting to
// DEFAULT_BRAINSTORM_COUNT when no explicit count is present. The only
// non-default count the brainstorm prompts exercise is ten, so the recogniser
// asks the seed whether the `ten` cardinal is evidenced in the prompt (in any
// supported language) and reads the value from that cardinal's own numeral
// surface. Mirrors requested_brainstorm_count in
// src/solver_handlers/benchmark_prompts.rs (issue #386).
function requestedBrainstormCount(normalized) {
const ten = findMeaning("ten");
if (ten && meaningEvidencedIn(ten, normalized)) {
const value = cardinalValue(ten);
if (value !== null) return value;
}
return DEFAULT_BRAINSTORM_COUNT;
}
function numbered(items, count) {
return items
.slice(0, count)
.map((item, index) => `${index + 1}. ${item}`)
.join("\n");
}
function tryBrainstormingRequest(prompt, normalized) {
const seeds = BRAINSTORM_SEEDS || {};
if (!containsAny(normalized, seeds.triggers)) return null;
const categories = Array.isArray(seeds.categories) ? seeds.categories : [];
const category =
categories.find((entry) => containsAny(normalized, entry.detectionKeywords)) ||
categories.find((entry) => !entry.detectionKeywords || entry.detectionKeywords.length === 0);
if (!category || !Array.isArray(category.items) || category.items.length === 0) {
return null;
}
const count = requestedBrainstormCount(normalized);
return {
intent: category.intent || "brainstorm_project_ideas",
content: numbered(category.items, count),
confidence: 0.8,
evidence: [`brainstorm:category:${category.slug || "project_ideas"}`],
};
}
function localizedFactFor(record, language) {
const localized = Array.isArray(record.localized) ? record.localized : [];
return (
localized.find((entry) => entry && entry.language === language) ||
localized.find((entry) => entry && entry.language === "en") ||
null
);
}
function tryFactLookup(prompt, normalized) {
const record = FACTS.find(
(fact) =>
containsAny(normalized, fact.subjectAliases) &&
containsAny(normalized, fact.questionKeywords),
);
if (!record) return null;
const language = detectLanguage(prompt);
const localized = localizedFactFor(record, language);
const summary = (localized && localized.summary) || record.summary;
const source = (localized && localized.source) || record.source;
const evidence = [
`fact_lookup:hit:${record.slug}`,
`language:${language}`,
...((record.wikidata || []).map((qid) => `wikidata:${qid}`)),
];
if (source) evidence.push(`source:${humanizeUrl(source)}`);
return {
intent: "fact_lookup",
content: summary,
confidence: 0.9,
evidence,
};
}
function renderRoleplayBody(persona, body) {
const template =
(PERSONA_SEEDS && PERSONA_SEEDS.bodyTemplate) ||
"Roleplay frame recorded for <persona>. I will keep the persona explicit and factual: <body>";
return template.replace(/<persona>/g, persona).replace(/<body>/g, body);
}
function tryRoleplayRequest(prompt, normalized) {
const seeds = PERSONA_SEEDS || {};
if (!containsAny(normalized, seeds.triggers)) return null;
const personas = Array.isArray(seeds.personas) ? seeds.personas : [];
const persona = personas.find((entry) => containsAny(normalized, entry.aliases));
const topics = Array.isArray(seeds.topics) ? seeds.topics : [];
const topic = topics.find((entry) => containsAny(normalized, entry.detectionKeywords));
const displayName =
(persona && persona.displayName) || seeds.defaultPersona || "requested persona";
const body =
(topic && topic.body) ||
seeds.fallbackBody ||
"relativity says measurements of space and time depend on the observer's motion, while the laws of physics stay consistent.";
const evidence = [`roleplay:persona:${displayName}`];
if (persona && persona.wikidata) evidence.push(`wikidata:${persona.wikidata}`);
if (topic && topic.slug) evidence.push(`roleplay:topic:${topic.slug}`);
return {
intent: "roleplay_explanation",
content: renderRoleplayBody(displayName, body),
confidence: 0.8,
evidence,
};
}
function tryKupiSlona(prompt, normalized) {
// Recognition is data-driven: the idiom surfaces (the «купи слона» phrase and
// its buy-an-elephant calque in every supported language) live in
// data/seed/meanings-policy.lino under the circular_joke_phrase role, matched
// as raw substrings. The worker has no localized-response lookup, so the
// canonical Russian explanation stays inline (mirrors the Rust fallback).
if (!lexiconMentionsRoleSubstring(ROLE_CIRCULAR_JOKE_PHRASE, normalized))
return null;
return {
intent: "kupi_slona",
content:
"«Купи слона» — это известная русская детская фраза-игра. На любой ответ следует продолжение: «Все так говорят, а ты купи слона!» Правильный ответ по правилам игры: «У всех есть слон, а у меня нет».",
confidence: 1.0,
evidence: ["handler:kupi_slona", "language:ru"],
};
}
function extractName(text) {
const patterns = [
/\bmy name is\s+([A-Z][a-zA-Z'-]+(?:\s+[A-Z][a-zA-Z'-]+)*)/,
/\bi am\s+([A-Z][a-zA-Z'-]+(?:\s+[A-Z][a-zA-Z'-]+)*)/,
/\bi'm\s+([A-Z][a-zA-Z'-]+(?:\s+[A-Z][a-zA-Z'-]+)*)/,
/\bcall me\s+([A-Z][a-zA-Z'-]+(?:\s+[A-Z][a-zA-Z'-]+)*)/,
];
for (const pattern of patterns) {
const match = pattern.exec(text);
if (match) return match[1];
}
return null;
}
function tryRecallName(history) {
if (!Array.isArray(history) || history.length === 0) return null;
for (let i = history.length - 1; i >= 0; i -= 1) {
const turn = history[i];
if (turn && turn.role === "user") {
const name = extractName(String(turn.content || ""));
if (name) {
return {
intent: "recall_name",
content: `Your name is ${name}.`,
confidence: 0.95,
evidence: [`recall_name:${name}`, "prior_turn:user"],
};
}
}
}
return null;
}
function tryRecallLastQuestion(history) {
if (!Array.isArray(history) || history.length === 0) return null;
for (let i = history.length - 1; i >= 0; i -= 1) {
const turn = history[i];
if (turn && turn.role === "user") {
const content = String(turn.content || "").trim();
if (content) {
return {
intent: "recall_last_question",
content: `Your previous question was: ${content}`,
confidence: 0.9,
evidence: ["recall_last_question", "prior_turn:user"],
};
}
}
}
return null;
}
// Issue #27: deterministic, logical summarisation — no neural net. We
// project the conversation onto a small set of features (turn counts, intents,
// concepts, languages, unanswered questions) and render them as a structured
// Markdown report. Every value is derived directly from the append-only event
// log so reruns on the same input produce byte-identical output.
function trySummarizeConversation(history) {
if (!Array.isArray(history) || history.length === 0) return null;
const turns = history.filter((turn) => turn && turn.content);
if (turns.length === 0) return null;
let userCount = 0;
let assistantCount = 0;
const intentCounts = new Map();
const languages = new Map();
const concepts = new Set();
const calculations = [];
const programTemplates = new Set();
const unanswered = [];
let lastUser = null;
for (const turn of turns) {
const role = turn.role || "assistant";
const language = detectLanguage(turn.content);
languages.set(language, (languages.get(language) || 0) + 1);
if (role === "user") {
userCount += 1;
lastUser = turn.content;
} else {
assistantCount += 1;
if (lastUser) {
lastUser = null;
}
const intent = String(turn.intent || "unknown");
intentCounts.set(intent, (intentCounts.get(intent) || 0) + 1);
if (intent === "calculation" && typeof turn.content === "string") {
const match = turn.content.match(/^([^=]+=\s*[^\n]+)/);
if (match) calculations.push(match[1].trim());
}
if (intent === "write_program") {
const evidence = Array.isArray(turn.evidence) ? turn.evidence : [];
const languageEvidence = evidence.find((item) =>
String(item || "").startsWith("program_parameter:language:"),
);
const taskEvidence = evidence.find((item) =>
String(item || "").startsWith("program_parameter:task:"),
);
const generatedLanguage = languageEvidence
? String(languageEvidence).slice("program_parameter:language:".length)
: "unknown";
const generatedTask = taskEvidence
? String(taskEvidence).slice("program_parameter:task:".length)
: "program";
programTemplates.add(`${generatedTask}/${generatedLanguage}`);
}
if (intent.startsWith("hello_world_")) {
programTemplates.add(`hello_world/${intent.slice("hello_world_".length)}`);
}
if (intent.startsWith("concept_lookup")) {
const evidence = Array.isArray(turn.evidence) ? turn.evidence : [];
for (const item of evidence) {
if (typeof item !== "string") continue;
const conceptMatch = item.match(/^concept_lookup:request:(.+)$/);
if (conceptMatch) concepts.add(conceptMatch[1]);
}
}
}
}
if (lastUser) {
unanswered.push(lastUser);
}
const lines = [];
lines.push("## Conversation summary");
lines.push("");
lines.push(
`- ${turns.length} turn(s): ${userCount} user, ${assistantCount} assistant`,
);
if (languages.size > 0) {
const list = Array.from(languages.entries())
.sort((a, b) => b[1] - a[1])
.map(([lang, count]) => `${lang} (${count})`)
.join(", ");
lines.push(`- Languages: ${list}`);
}
if (intentCounts.size > 0) {
const list = Array.from(intentCounts.entries())
.sort((a, b) => b[1] - a[1])
.map(([intent, count]) => `${intent} (${count})`)
.join(", ");
lines.push(`- Intents: ${list}`);
}
if (concepts.size > 0) {
lines.push(`- Concepts looked up: ${Array.from(concepts).join(", ")}`);
}
if (calculations.length > 0) {
lines.push(`- Calculations: ${calculations.join("; ")}`);
}
if (programTemplates.size > 0) {
lines.push(
`- Program templates generated: ${Array.from(programTemplates).join(", ")}`,
);
}
if (unanswered.length > 0) {
lines.push(`- Unanswered: ${unanswered.join(" | ")}`);
}
const evidence = [
"summarize_conversation",
`turns:${turns.length}`,
`users:${userCount}`,
`assistants:${assistantCount}`,
];
if (intentCounts.size > 0) {
evidence.push(`intents:${Array.from(intentCounts.keys()).join("|")}`);
}
return {
intent: "summarize_conversation",
content: lines.join("\n"),
confidence: 0.9,
evidence,
};
}
function tryCompoundInterest(prompt, normalized, history) {
const request = parseCompoundInterestRequest(prompt, normalized);
if (request) return answerCompoundInterest(request);
const conversion = parseFinalAmountConversionRequest(normalized, history);
if (conversion) return answerFinalAmountConversion(conversion);
return null;
}
function answerCompoundInterest(request) {
const annualRate = request.annualRatePercent / 100;
const periodsPerYear = request.compoundsPerYear;
const periodicRate = annualRate / periodsPerYear;
const periods = periodsPerYear * request.years;
const finalAmount =
request.principal * Math.pow(1 + periodicRate, periods);
const evidence = [
`calculation:compound_interest:P=${formatCompoundNumber(request.principal)};r=${formatCompoundRate(annualRate)};n=${periodsPerYear};t=${formatCompoundNumber(request.years)}`,
"calculation:formula:A=P(1+r/n)^(n*t)",
];
const lines = [
"Compound interest calculation",
"",
"Formula: A = P(1 + r/n)^(n*t)",
`P = ${formatCompoundNumber(request.principal)} USD`,
`r = ${formatCompoundRate(annualRate)} (${formatCompoundNumber(request.annualRatePercent)}% annual)`,
`n = ${periodsPerYear} (${compoundLabel(periodsPerYear)})`,
`t = ${formatCompoundNumber(request.years)} years`,
"",
`Step 1: periodic rate = r/n = ${formatCompoundRate(annualRate)}/${periodsPerYear} = ${formatCompoundRate(periodicRate)}`,
`Step 2: number of periods = n*t = ${periodsPerYear}*${formatCompoundNumber(request.years)} = ${formatCompoundNumber(periods)}`,
`Step 3: A = ${formatCompoundNumber(request.principal)} * (1 + ${formatCompoundRate(periodicRate)})^${formatCompoundNumber(periods)}`,
`Final amount: ${formatCompoundMoney(finalAmount)} USD`,
];
if (request.targetCurrency) {
appendCompoundConversionLines(
lines,
evidence,
finalAmount,
"USD",
request.targetCurrency,
request.asksForWebRate,
);
}
return {
intent: "calculation",
content: lines.join("\n"),
confidence: 1.0,
evidence,
};
}
function answerFinalAmountConversion(conversion) {
const evidence = ["calculation:final_amount_conversion"];
const lines = [
"Final amount conversion",
`Source amount: ${formatCompoundMoney(conversion.amount)} ${conversion.sourceCurrency}`,
];
appendCompoundConversionLines(
lines,
evidence,
conversion.amount,
conversion.sourceCurrency,
conversion.targetCurrency,
conversion.asksForWebRate,
);
return {
intent: "calculation",
content: lines.join("\n"),
confidence: 1.0,
evidence,
};
}
function appendCompoundConversionLines(
lines,
evidence,
amount,
sourceCurrency,
targetCurrency,
asksForWebRate,
) {
const rate = compoundCurrencyRate(sourceCurrency, targetCurrency);
if (!rate) {
evidence.push(`calculation:currency_conversion:error:${sourceCurrency}->${targetCurrency}`);
lines.push("");
lines.push(
`I calculated the USD amount, but no ${sourceCurrency}->${targetCurrency} exchange rate is available locally.`,
);
return;
}
const displayedAmount = roundCompoundMoney(amount);
const converted = displayedAmount * rate.rate;
evidence.push(
`calculation:currency_conversion:${formatCompoundMoney(displayedAmount)} ${sourceCurrency} to ${targetCurrency} at ${formatCompoundRate(rate.rate)}`,
);
lines.push("");
lines.push(`Conversion: ${sourceCurrency} -> ${targetCurrency}`);
lines.push(`${rate.expression} = ${rate.formatted}`);
lines.push(
`${formatCompoundMoney(displayedAmount)} ${sourceCurrency} * ${formatCompoundRate(rate.rate)} = ${formatCompoundMoney(converted)} ${targetCurrency}`,
);
if (rate.sourceDetail) {
lines.push(`Rate detail: ${rate.sourceDetail}`);
}
if (asksForWebRate) {
lines.push(
"Live web freshness is not independently verified here; this uses the exchange-rate source available through the local calculator.",
);
}
}
function parseCompoundInterestRequest(prompt, normalized) {
// The investment / interest / compounding cues are language-independent
// meanings carried by the finance lexicon; we test the raw substring of the
// already-normalized prompt against every surface form (the English forms
// reproduce the original invest/interest/compound markers, the other
// languages broaden coverage). Mirrors parse_compound_interest_request.
if (
!lexiconMentionsRoleSubstring(ROLE_INVESTMENT_CUE, normalized) ||
!lexiconMentionsRoleSubstring(ROLE_INTEREST_CUE, normalized) ||
!lexiconMentionsRoleSubstring(ROLE_COMPOUNDING_ACTION_CUE, normalized)
) {
return null;
}
const principal = parseCompoundCurrencyAmount(prompt);
const annualRatePercent = parseCompoundPercentBeforeSymbol(prompt);
const compoundsPerYear = parseCompoundsPerYear(normalized);
const years = parseCompoundYears(normalized);
if (
principal === null ||
annualRatePercent === null ||
compoundsPerYear === null ||
years === null
) {
return null;
}
return {
principal,
annualRatePercent,
compoundsPerYear,
years,
targetCurrency: targetCurrencyFromText(normalized),
asksForWebRate: asksForWebRate(normalized),
};
}
function parseFinalAmountConversionRequest(normalized, history) {
// "convert" and "final amount" are themselves meanings: a conversion action
// applied to the final-amount reference produced by a prior turn. Mirrors
// parse_final_amount_conversion_request.
if (
!lexiconMentionsRoleSubstring(ROLE_CONVERSION_ACTION_CUE, normalized) ||
!lexiconMentionsRoleSubstring(ROLE_FINAL_AMOUNT_REFERENCE, normalized)
) {
return null;
}
const targetCurrency = targetCurrencyFromText(normalized);
if (!targetCurrency) return null;
const prior = priorFinalAmount(history);
if (!prior) return null;
return {
amount: prior.amount,
sourceCurrency: prior.currency,
targetCurrency,
asksForWebRate: asksForWebRate(normalized),
};
}
function priorFinalAmount(history) {
if (!Array.isArray(history)) return null;
for (let index = history.length - 1; index >= 0; index -= 1) {
const turn = history[index];
if (!turn || turn.role !== "assistant") continue;
const parsed = parseFinalAmountFromText(String(turn.content || ""));
if (parsed) return parsed;
}
return null;
}
function parseFinalAmountFromText(text) {
const match = /final amount:\s*([+-]?\d[\d,.]*)\s*([A-Za-z]{3}|dollars?|euros?|rubles?)/i.exec(
text,
);
if (!match) return null;
const amount = parseCompoundNumberText(match[1]);
const currency = currencyCodeFromWord(match[2]);
if (amount === null || !currency) return null;
return { amount, currency };
}
function parseCompoundCurrencyAmount(prompt) {
const text = String(prompt || "");
const dollarIndex = text.indexOf("$");
if (dollarIndex >= 0) {
return parseCompoundNumberRight(text, dollarIndex + 1);
}
// The spelled-out US-dollar markers are language data: reconstruct the regex
// alternation from the currency_usd_reference English surface forms (usd,
// dollar, dollars) instead of hardcoding them. Mirrors parse_currency_amount,
// which scans the same forms; the `$` glyph stays in code as a symbol.
const usdWords = wordsForRoleInLanguages(ROLE_CURRENCY_USD_REFERENCE, ["en"]);
if (!usdWords.length) return null;
const alternation = usdWords.map((word) => escapeRegExp(word)).join("|");
const pattern = new RegExp(`([+-]?\\d[\\d,.]*)\\s*(?:${alternation})`, "i");
const match = pattern.exec(text);
return match ? parseCompoundNumberText(match[1]) : null;
}
function parseCompoundPercentBeforeSymbol(prompt) {
const text = String(prompt || "");
const percentIndex = text.indexOf("%");
return percentIndex >= 0 ? parseCompoundNumberLeft(text, percentIndex) : null;
}
function parseCompoundYears(normalized) {
// The duration unit is a meaning (year_unit_cue); locate the earliest of its
// surface forms (English "year", plus the other languages) and read the
// number to its left. Mirrors years_in_prompt.
const text = String(normalized || "");
let earliest = -1;
for (const word of wordsForRole(ROLE_YEAR_UNIT_CUE)) {
const index = text.indexOf(word);
if (index >= 0 && (earliest < 0 || index < earliest)) earliest = index;
}
return earliest >= 0 ? parseCompoundNumberLeft(text, earliest) : null;
}
function parseCompoundsPerYear(normalized) {
// The compounding frequency is a cluster of meanings (monthly, quarterly,
// weekly, daily, annual), each carrying its surface forms and listed in
// priority order in the finance lexicon. Pick the first whose surface appears
// in the prompt and map its slug to the periods-per-year count. Mirrors
// parse_compounds_per_year.
const meaning = meaningsWithRole(ROLE_COMPOUNDING_FREQUENCY_CUE).find((candidate) =>
candidate.words.some((word) => normalized.includes(word)),
);
return meaning ? compoundsPerYearForSlug(meaning.slug) : null;
}
function compoundsPerYearForSlug(slug) {
switch (slug) {
case "compounding_monthly":
return 12;
case "compounding_quarterly":
return 4;
case "compounding_weekly":
return 52;
case "compounding_daily":
return 365;
case "compounding_annual":
return 1;
default:
return null;
}
}
function targetCurrencyFromText(normalized) {
// The target currency is whichever currency meaning the prompt names as a
// whole token. EUR wins over USD wins over RUB to preserve the original
// priority; the € glyph stays in code as a symbol alongside the EUR meaning.
// Token-bounded matching mirrors target_currency / mentions_role on the Rust
// side, so a code like "eur" never fires inside another word.
if (
lexiconMentionsRole(ROLE_CURRENCY_EUR_REFERENCE, normalized) ||
normalized.includes("€")
) {
return "EUR";
}
if (lexiconMentionsRole(ROLE_CURRENCY_USD_REFERENCE, normalized)) {
return "USD";
}
if (lexiconMentionsRole(ROLE_CURRENCY_RUB_REFERENCE, normalized)) {
return "RUB";
}
return "";
}
function asksForWebRate(normalized) {
// "fetch the live/current rate from the web" is the live_rate_freshness_cue
// meaning; its surface forms (web, current exchange, current rate, exchange
// rate) live in the finance lexicon. Matched as raw substrings to mirror
// asks_for_web_rate / mentions_role_raw on the Rust side.
return lexiconMentionsRoleSubstring(ROLE_LIVE_RATE_FRESHNESS_CUE, normalized);
}
function compoundCurrencyRate(sourceCurrency, targetCurrency) {
const expression = `1 ${sourceCurrency} in ${targetCurrency}`;
if (sourceCurrency === targetCurrency) {
return {
rate: 1,
expression,
formatted: `1 ${targetCurrency}`,
sourceDetail: "",
};
}
const wasmResult = wasmEvaluateArithmetic(expression);
if (wasmResult && wasmResult.ok) {
const rate = parseCompoundLeadingNumber(wasmResult.value);
if (rate !== null) {
return {
rate,
expression,
formatted: wasmResult.value,
sourceDetail: `Exchange rate: 1 ${sourceCurrency} = ${formatCompoundRate(rate)} ${targetCurrency} (source: calculator)`,
};
}
}
const rate = defaultCurrencyRate(sourceCurrency, targetCurrency);
if (!rate) return null;
return {
rate,
expression,
formatted: `${formatCompoundRate(rate)} ${targetCurrency}`,
sourceDetail: `Exchange rate: 1 ${sourceCurrency} = ${formatCompoundRate(rate)} ${targetCurrency} (source: default (hardcoded))`,
};
}
function parseCompoundNumberLeft(text, end) {
const before = String(text || "").slice(0, end);
const match = /([+-]?\d[\d,.]*)\s*$/.exec(before);
return match ? parseCompoundNumberText(match[1]) : null;
}
function parseCompoundNumberRight(text, start) {
const after = String(text || "").slice(start);
const match = /^\s*([+-]?\d[\d,.]*)/.exec(after);
return match ? parseCompoundNumberText(match[1]) : null;
}
function parseCompoundLeadingNumber(text) {
const match = /([+-]?\d[\d,.]*)/.exec(String(text || ""));
return match ? parseCompoundNumberText(match[1]) : null;
}
function parseCompoundNumberText(value) {
let cleaned = String(value || "").trim();
if (!/\d/.test(cleaned)) return null;
if (cleaned.includes(",") && !cleaned.includes(".")) {
const parts = cleaned.split(",");
cleaned =
parts.length === 2 && parts[1].length <= 2
? `${parts[0]}.${parts[1]}`
: parts.join("");
} else {
cleaned = cleaned.replace(/,/g, "");
}
const parsed = Number(cleaned);
return Number.isFinite(parsed) ? parsed : null;
}
function compoundLabel(compoundsPerYear) {
switch (compoundsPerYear) {
case 1:
return "annually";
case 4:
return "quarterly";
case 12:
return "monthly";
case 52:
return "weekly";
case 365:
return "daily";
default:
return "times per year";
}
}
function formatCompoundNumber(value) {
if (Math.abs(value % 1) < 1e-10) return value.toFixed(0);
return trimCompoundDecimal(value.toFixed(10));
}
function formatCompoundMoney(value) {
return value.toFixed(2);
}
function roundCompoundMoney(value) {
return Math.round(value * 100) / 100;
}
function formatCompoundRate(value) {
return trimCompoundDecimal(value.toFixed(15));
}
function trimCompoundDecimal(value) {
return String(value).replace(/0+$/, "").replace(/\.$/, "");
}
function tryArithmetic(prompt) {
const extracted = extractArithmeticExpression(prompt);
if (!extracted) return null;
const expression = extracted.expression;
const interpretations = Array.isArray(extracted.interpretations)
? extracted.interpretations
: [];
const reasoningSteps = Array.isArray(extracted.reasoningSteps)
? extracted.reasoningSteps
: [];
const resultLabel = typeof extracted.resultLabel === "string" ? extracted.resultLabel : "";
try {
const isEquation = expression.includes("=");
let formatted;
let backend = "js";
const wasmResult = wasmEvaluateArithmetic(expression);
if (wasmResult && wasmResult.ok) {
formatted = wasmResult.value;
backend = "wasm";
} else {
const percentOfResult = evaluatePercentOfExpression(expression);
const currencyConversionResult = evaluateCurrencyConversionExpression(expression);
if (currencyConversionResult !== null) {
formatted = currencyConversionResult;
backend = "js-currency";
} else if (percentOfResult) {
formatted = percentOfResult;
backend = "js-percent-of";
} else if (isEquation) {
formatted = solveLinearEquation(expression);
} else {
formatted = formatArithmeticResult(evaluateArithmetic(expression));
}
}
const calculationLine = isEquation
? `${expression.trim()} => ${formatted}`
: `${expression.trim()} = ${formatted}`;
const sections = [];
if (reasoningSteps.length > 0) {
sections.push(
reasoningSteps
.map((step, index) => `Step ${index + 1}: ${step}`)
.join("\n"),
);
}
sections.push(calculationLine);
if (resultLabel) {
sections.push(`Therefore, there are ${formatted} ${resultLabel} in total.`);
}
const content = sections.join("\n\n");
const evidence = [
`calculation:${content}`,
`calculation_backend:${backend}`,
];
if (reasoningSteps.length > 0) {
evidence.push(`calculation_reasoning_steps:${reasoningSteps.length}`);
}
if (resultLabel) evidence.push(`calculation_result_label:${resultLabel}`);
return {
intent: "calculation",
content: content,
confidence: 1.0,
evidence,
interpretations,
};
} catch (error) {
const message = String(error && error.message ? error.message : error);
return {
intent: "calculation_error",
content: `I could not evaluate \`${expression.trim()}\`: ${message}.`,
confidence: 0.4,
evidence: [`calculation_error:${message}`],
interpretations,
};
}
}
const SYNTHESIS_NUMBER_WORDS = new Map([
["zero", 0],
["one", 1],
["a", 1],
["an", 1],
["two", 2],
["three", 3],
["four", 4],
["five", 5],
["six", 6],
["seven", 7],
["eight", 8],
["nine", 9],
["ten", 10],
["eleven", 11],
["twelve", 12],
["thirteen", 13],
["fourteen", 14],
["fifteen", 15],
["sixteen", 16],
["seventeen", 17],
["eighteen", 18],
["nineteen", 19],
["twenty", 20],
]);
const SYNTHESIS_OBJECT_CATEGORIES = new Map([
[
"musical instrument",
new Set([
"clarinet",
"flute",
"guitar",
"harmonica",
"piano",
"saxophone",
"trumpet",
"violin",
"drum",
]),
],
["fruit", new Set(["apple", "banana", "orange", "pear", "grape"])],
["vegetable", new Set(["carrot", "onion", "potato", "tomato", "pepper"])],
["animal", new Set(["cat", "dog", "horse", "cow", "bird"])],
["vehicle", new Set(["car", "truck", "bus", "bicycle", "train"])],
["tool", new Set(["hammer", "saw", "wrench", "screwdriver", "drill"])],
["utensil", new Set(["spoon", "fork", "knife", "ladle"])],
["furniture", new Set(["chair", "table", "sofa", "desk", "bed"])],
["clothing", new Set(["shirt", "coat", "hat", "shoe", "dress"])],
]);
const PYTHON_SYNTHESIS_CANDIDATES = {
has_close_elements: {
id: "pairwise_threshold_distance",
defaultSignature:
"has_close_elements(numbers: list[float], threshold: float) -> bool",
body: [
{
kind: "for_loop",
semanticNode: "pairwise_outer_loop",
target: "left_index, left",
iterator: "enumerate(numbers)",
body: [
{
kind: "for_loop",
semanticNode: "pairwise_inner_loop",
target: "right",
iterator: "numbers[left_index + 1:]",
body: [
{
kind: "if_return",
semanticNode: "threshold_match_return",
condition: "abs(left - right) < threshold",
value: "True",
},
],
},
],
},
{
kind: "return",
semanticNode: "no_pair_matches_return",
expression: "False",
},
],
tests: [
"assert has_close_elements([1.0, 2.0, 3.0], 0.5) is False",
"assert has_close_elements([1.0, 2.0, 3.0], 1.1) is True",
"assert has_close_elements([1.0, 2.8, 3.0], 0.3) is True",
"assert has_close_elements([], 0.1) is False",
],
fragments: [
"python:def_function",
"loop:pairwise_distinct_values",
"predicate:absolute_difference_less_than_threshold",
"branch:return_false_when_no_pair_matches",
],
},
similar_elements: {
id: "tuple_intersection_set",
defaultSignature: "similar_elements(test_tup1, test_tup2)",
body: [
{
kind: "return",
semanticNode: "deterministic_tuple_intersection_return",
expression: "tuple(sorted(set(test_tup1) & set(test_tup2)))",
},
],
tests: [
"assert similar_elements((3, 4, 5, 6), (5, 7, 4, 10)) == (4, 5)",
"assert similar_elements((1, 2), (3, 4)) == ()",
"assert similar_elements(('a', 'b'), ('b', 'c')) == ('b',)",
],
fragments: [
"python:def_function",
"collection:set_intersection",
"collection:deterministic_tuple_order",
],
},
count_vowels: {
id: "count_matching_characters",
defaultSignature: "count_vowels(text: str) -> int",
body: [
{
kind: "assign",
semanticNode: "vowel_membership_set_assignment",
target: "vowels",
expression: 'set("aeiouAEIOU")',
},
{
kind: "return",
semanticNode: "matching_character_count_return",
expression: "sum(1 for character in text if character in vowels)",
},
],
tests: [
"assert count_vowels('hello') == 2",
"assert count_vowels('sky') == 0",
"assert count_vowels('Formal AI') == 4",
],
fragments: [
"python:def_function",
"collection:membership_set",
"aggregation:sum_generator",
],
},
};
function synthesisStableId(prefix, value) {
const wasmId = wasmStableId(prefix, value);
if (wasmId) return wasmId;
const text = `${String(prefix || "")}\n${String(value || "")}`;
let hash = 0xcbf29ce484222325n;
const prime = 0x100000001b3n;
const mask = 0xffffffffffffffffn;
for (let index = 0; index < text.length; index += 1) {
hash ^= BigInt(text.charCodeAt(index));
hash = (hash * prime) & mask;
}
return `${prefix}:${hash.toString(16).padStart(16, "0")}`;
}
function truncateSynthesisTrace(value, limit = 80) {
const text = String(value || "").replace(/\s+/g, " ").trim();
return text.length > limit ? `${text.slice(0, limit)}...` : text;
}
function synthesisSubResults(prompt) {
return String(prompt || "")
.split(/(?:[.;?]+|\band\b)/i)
.map((part) => part.trim())
.filter(Boolean)
.slice(0, 6)
.map((part, index) => ({
id: synthesisStableId("sub_result", `${index}:${part}`),
text: part,
}));
}
function subResultEvidence(subResults) {
return subResults.map(
(item) => `sub_result:${item.id}:${truncateSynthesisTrace(item.text, 60)}`,
);
}
function evaluateSynthesisArithmetic(expression) {
const wasmResult = wasmEvaluateArithmetic(expression);
if (wasmResult && wasmResult.ok) {
return { formatted: wasmResult.value, backend: "wasm" };
}
return {
formatted: formatArithmeticResult(evaluateArithmetic(expression)),
backend: "js-fallback",
};
}
function extractAlgebraAssignments(prompt) {
const assignments = [];
const seen = new Set();
const pattern = /(?:^|[\s,;(])([A-Za-z_][A-Za-z0-9_]{0,1})\s*=\s*([+-]?\d+(?:\.\d+)?)/g;
let match;
while ((match = pattern.exec(String(prompt || ""))) !== null) {
const variable = match[1].toLowerCase();
if (seen.has(variable)) continue;
seen.add(variable);
assignments.push({ variable, value: match[2] });
}
return assignments;
}
function extractRequestedAlgebraExpression(prompt) {
const text = String(prompt || "");
const lower = text.toLowerCase();
for (const marker of ["value of", "evaluate", "calculate", "compute"]) {
const start = lower.indexOf(marker);
if (start < 0) continue;
const tail = text.slice(start + marker.length).trim();
const sentence = tail.replace(/[?.!]+$/g, "").trim();
const cleaned = sentence
.replace(/^(?:the\s+)?(?:expression|value|result)\s+(?:of\s+)?/i, "")
.replace(/^then\s+/i, "")
.trim();
if (cleaned && /[A-Za-z_]/.test(cleaned)) return cleaned;
}
return null;
}
function lastNonWhitespace(value) {
for (let index = value.length - 1; index >= 0; index -= 1) {
const ch = value[index];
if (!/\s/.test(ch)) return ch;
}
return "";
}
function substituteAlgebraVariables(expression, assignments) {
const values = new Map(assignments.map((item) => [item.variable, item.value]));
let out = "";
let index = 0;
while (index < expression.length) {
const ch = expression[index];
if (/[A-Za-z_]/.test(ch)) {
const start = index;
index += 1;
while (index < expression.length && /[A-Za-z_]/.test(expression[index])) {
index += 1;
}
const token = expression.slice(start, index).toLowerCase();
const value = values.get(token);
if (value !== undefined) {
const previous = lastNonWhitespace(out);
if (/[0-9)]/.test(previous)) out += "*";
out += value;
if (expression[index] === "(") out += "*";
} else {
out += expression.slice(start, index);
}
continue;
}
if (ch === "(" && /[0-9]/.test(lastNonWhitespace(out))) out += "*";
out += ch;
index += 1;
}
return out;
}
function composeAlgebraSubstitution(prompt, subResults) {
const assignments = extractAlgebraAssignments(prompt);
if (assignments.length === 0) return null;
const expression = extractRequestedAlgebraExpression(prompt);
if (!expression) return null;
if (
!assignments.some((assignment) =>
new RegExp(`(^|[^A-Za-z_])${assignment.variable}([^A-Za-z_]|$)`, "i").test(
expression,
),
)
) {
return null;
}
try {
const substituted = substituteAlgebraVariables(expression, assignments);
const evaluation = evaluateSynthesisArithmetic(substituted);
const assignmentText = assignments
.map((assignment) => `${assignment.variable}=${assignment.value}`)
.join(", ");
const evaluationText = `${substituted} = ${evaluation.formatted}`;
const evidence = [
...subResultEvidence(subResults),
`composition:substitution:${assignmentText} -> ${substituted}`,
`composition:evaluation:${evaluationText}`,
`calculation_backend:${evaluation.backend}`,
];
return {
intent: "algebra_substitution",
content: `Substituting ${assignmentText} into ${expression} gives ${evaluationText}.`,
confidence: 1.0,
evidence,
trace: [
`composition:substitution:${assignmentText}`,
`composition:evaluation:${evaluationText}`,
],
};
} catch (_error) {
return null;
}
}
function extractSynthesisQuantities(prompt) {
return String(prompt || "")
.split(/[^A-Za-z0-9$.-]+/)
.map((raw) =>
raw
.replace(/^\$+/, "")
.replace(/^[.-]+/, "")
.replace(/[.-]+$/, ""),
)
.filter(Boolean)
.map((token) => {
if (/^\d+$/.test(token)) return Number(token);
return SYNTHESIS_NUMBER_WORDS.get(token.toLowerCase());
})
.filter((value) => Number.isInteger(value));
}
function composeRemainderSale(prompt, subResults) {
const lower = String(prompt || "").toLowerCase();
if (!lower.includes("remainder") || !lower.includes("sell")) return null;
const quantities = extractSynthesisQuantities(prompt);
if (quantities.length < 4) return null;
const total = quantities[0];
const price = quantities[quantities.length - 1];
const consumed = quantities.slice(1, -1).reduce((sum, value) => sum + value, 0);
if (total <= 0 || price <= 0 || consumed < 0 || consumed >= total) return null;
const remaining = total - consumed;
const expression = `(${total} - ${consumed}) * ${price}`;
try {
const evaluation = evaluateSynthesisArithmetic(expression);
const evaluationText = `${expression} = ${evaluation.formatted}`;
return {
intent: "arithmetic_word_problem",
content:
`The remainder is ${total} - ${consumed} = ${remaining}. ` +
`Selling ${remaining} at ${price} each gives ${evaluation.formatted}.`,
confidence: 1.0,
evidence: [
...subResultEvidence(subResults),
`composition:remainder:total=${total} consumed=${consumed} remainder=${remaining} price=${price}`,
`composition:evaluation:${evaluationText}`,
`calculation_backend:${evaluation.backend}`,
],
trace: [
`composition:remainder:total=${total} consumed=${consumed} remainder=${remaining} price=${price}`,
`composition:evaluation:${evaluationText}`,
],
};
} catch (_error) {
return null;
}
}
function singularizeSynthesisToken(token) {
if (token.length > 4 && token.endsWith("ies")) return `${token.slice(0, -3)}y`;
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss")) {
return token.slice(0, -1);
}
return token;
}
function normalizeCountPhrase(value) {
return String(value || "")
.toLowerCase()
.split(/[^a-z0-9]+/)
.filter(Boolean)
.filter((token) => !["a", "an", "the", "of"].includes(token))
.map(singularizeSynthesisToken)
.join(" ");
}
function cleanCountedItem(raw) {
return String(raw || "")
.trim()
.replace(/^[\s.,:;!?]+|[\s.,:;!?]+$/g, "")
.replace(/^(?:a|an|the|one)\s+/i, "")
.trim();
}
function extractCountedItems(prompt) {
const match = String(prompt || "").match(/\bi\s+have\s+(.+?)\.\s*how\s+many\b/i);
const segment = match ? match[1] : "";
if (!segment) return [];
return segment
.replace(/\s+and\s+/gi, ", ")
.split(",")
.map(cleanCountedItem)
.filter(Boolean);
}
function extractRequestedCountCategory(prompt) {
const match = String(prompt || "").match(
/\bhow\s+many\s+(.+?)(?:\s+do\s+i\s+have|\s+are\s+there|[?.!]|$)/i,
);
return match ? normalizeCountPhrase(match[1]) : "";
}
function composeObjectCount(prompt, subResults) {
const items = extractCountedItems(prompt);
if (items.length === 0) return null;
const category = extractRequestedCountCategory(prompt);
const accepted = SYNTHESIS_OBJECT_CATEGORIES.get(category);
if (!accepted) return null;
const matched = items.filter((item) => accepted.has(normalizeCountPhrase(item)));
if (matched.length === 0) return null;
const categoryLabel = category.endsWith("s") ? category : `${category}s`;
return {
intent: "object_counting",
content:
`Matching ${categoryLabel} in the list gives ${matched.join(", ")}. ` +
`Count: ${matched.length}.`,
confidence: 1.0,
evidence: [
...subResultEvidence(subResults),
`composition:category:category=${categoryLabel} items=${items.join("|")}`,
`composition:count:matched=${matched.join("|")} count=${matched.length}`,
],
trace: [
`composition:category:category=${categoryLabel}`,
`composition:count:matched=${matched.join("|")} count=${matched.length}`,
],
};
}
function tryLinkNativeSynthesis(prompt) {
const subResults = synthesisSubResults(prompt);
if (subResults.length === 0) return null;
return (
composeAlgebraSubstitution(prompt, subResults) ||
composeRemainderSale(prompt, subResults) ||
composeObjectCount(prompt, subResults)
);
}
// Does `normalized` read like a request to synthesise a Python function?
// Mirrors looks_like_python_function_request in
// src/solver_handlers/program_synthesis.rs: a function *subject*, a *domain*
// signal (Python or a data kind), and an *action* verb — all supplied by the
// meaning lexicon, never hardcoded. `def ` is Python syntax a user may paste,
// so a literal signature satisfies both the subject and action sides.
function looksLikePythonFunctionSynthesis(prompt, normalized) {
const hasDef = String(prompt || "").toLowerCase().includes("def ");
return (
(lexiconMentionsRole(ROLE_PROGRAM_SYNTHESIS_SUBJECT, normalized) || hasDef) &&
lexiconMentionsRole(ROLE_PROGRAM_SYNTHESIS_DOMAIN, normalized) &&
(lexiconMentionsRole(ROLE_PROGRAM_SYNTHESIS_ACTION, normalized) || hasDef)
);
}
// Is `task` evidenced by its signals — is every `program_synthesis_signal`
// meaning it is `defined_by` present in `normalized`? A task with no signal
// definitions is never matched this way (it can still match by declared name).
// Mirrors synthesis_task_evidenced in src/solver_handlers/program_synthesis.rs.
function synthesisTaskEvidenced(task, normalized) {
let required = 0;
for (const target of task.definedBy) {
const signal = findMeaning(target);
if (!signal || !signal.roles.includes(ROLE_PROGRAM_SYNTHESIS_SIGNAL)) continue;
required += 1;
if (!meaningEvidencedIn(signal, normalized)) return false;
}
return required > 0;
}
// The canonical function name of the first synthesis task (declaration order)
// whose signals are all evidenced in `normalized`, or "". The slug is the
// Python function name. Mirrors match_synthesis_task in
// src/solver_handlers/program_synthesis.rs.
function matchSynthesisTask(normalized) {
const task = meaningsWithRole(ROLE_PROGRAM_SYNTHESIS_TASK).find((candidate) =>
synthesisTaskEvidenced(candidate, normalized),
);
return task ? task.slug : "";
}
function identifierAfterAsciiMarker(prompt, marker) {
const text = String(prompt || "");
const lower = text.toLowerCase();
const start = lower.indexOf(marker);
if (start < 0) return "";
let name = "";
let started = false;
for (const character of text.slice(start + marker.length)) {
if (/[A-Za-z0-9_]/.test(character)) {
name += character;
started = true;
} else if (started) {
break;
} else if (!/\s/.test(character)) {
return "";
}
}
return name;
}
// The Python function name a prompt asks for: the matched task's slug (which
// *is* its function name), else the identifier in a literal `function `/`def `
// signature. Mirrors extract_function_name in
// src/solver_handlers/program_synthesis.rs (the declared-signature scan there is
// covered here by the marker fallbacks the worker has always used).
function extractPythonFunctionName(prompt, normalized) {
const task = matchSynthesisTask(normalized);
if (task) return task;
return (
identifierAfterAsciiMarker(prompt, "function ") ||
identifierAfterAsciiMarker(prompt, "def ")
);
}
function matchingCloseParen(text, openIndex) {
let depth = 0;
for (let index = openIndex; index < text.length; index += 1) {
const character = text[index];
if (character === "(") {
depth += 1;
} else if (character === ")") {
depth -= 1;
if (depth === 0) return index + 1;
if (depth < 0) return -1;
}
}
return -1;
}
// A character that may appear inside a Python return annotation (`-> list[int]`).
// A whitelist, mirroring is_return_annotation_char in
// src/solver_handlers/program_synthesis.rs: this is what lets the annotation
// scan stop at the first non-Latin character — Hindi/Chinese prose, the
// Devanagari danda `।`, or the ideographic full stop `。` — instead of relying
// on an ASCII-only `[.;\n]` terminator that those scripts never contain.
function isReturnAnnotationChar(character) {
return /[A-Za-z0-9]/.test(character) || "_[](),'\"|".includes(character);
}
// The first non-whitespace character at or after `start`, or "".
// Mirrors next_non_whitespace in src/solver_handlers/program_synthesis.rs.
function nextNonWhitespace(text, start) {
for (let index = start; index < text.length; ) {
const character = String.fromCodePoint(text.codePointAt(index));
if (!/\s/.test(character)) return character;
index += character.length;
}
return "";
}
// The end index (exclusive) of a `->` return annotation that begins at
// `arrowStart`, or -1 if none. Walks annotation characters, allowing internal
// spaces (`-> dict[str, int]`) only while more annotation characters follow.
// Mirrors return_annotation_end in src/solver_handlers/program_synthesis.rs.
function returnAnnotationEnd(text, arrowStart) {
let cursor = arrowStart + "->".length;
let seenAnnotation = false;
let lastAnnotationEnd = -1;
while (cursor < text.length) {
const character = String.fromCodePoint(text.codePointAt(cursor));
const next = cursor + character.length;
if (/\s/.test(character)) {
if (seenAnnotation && isReturnAnnotationChar(nextNonWhitespace(text, next))) {
cursor = next;
continue;
}
if (seenAnnotation) break;
cursor = next;
continue;
}
if (!isReturnAnnotationChar(character)) break;
seenAnnotation = true;
lastAnnotationEnd = next;
cursor = next;
}
return lastAnnotationEnd;
}
function declaredPythonSignature(prompt, functionName) {
const text = String(prompt || "");
const marker = `${functionName}(`;
const start = text.toLowerCase().indexOf(marker.toLowerCase());
if (start < 0) return "";
let end = matchingCloseParen(text, start + functionName.length);
if (end < 0) return "";
const tail = text.slice(end);
const trimmed = tail.trimStart();
if (trimmed.startsWith("->")) {
const returnStart = end + (tail.length - trimmed.length);
const annotationEnd = returnAnnotationEnd(text, returnStart);
if (annotationEnd >= 0) end = annotationEnd;
}
return text.slice(start, end).trim().replace(/\.+$/, "");
}
function renderPythonStatement(statement, indentLevel) {
const indent = " ".repeat(indentLevel);
if (statement.kind === "assign") {
return `${indent}${statement.target} = ${statement.expression}\n`;
}
if (statement.kind === "return") {
return `${indent}return ${statement.expression}\n`;
}
if (statement.kind === "if_return") {
return `${indent}if ${statement.condition}:\n${indent} return ${statement.value}\n`;
}
if (statement.kind === "for_loop") {
return (
`${indent}for ${statement.target} in ${statement.iterator}:\n` +
statement.body
.map((child) => renderPythonStatement(child, indentLevel + 1))
.join("")
);
}
return "";
}
function renderPythonFunction(functionTree) {
let code = `def ${functionTree.signature}:\n`;
for (const statement of functionTree.body) {
code += renderPythonStatement(statement, 1);
}
return code;
}
function pythonStatementLinks(lines, statement, depth) {
const indent = " ".repeat(depth + 1);
if (statement.kind === "assign") {
lines.push(
`${indent}semantic_node ${statement.semanticNode} target=${JSON.stringify(statement.target)} expression=${JSON.stringify(statement.expression)}`,
);
} else if (statement.kind === "return") {
lines.push(
`${indent}semantic_node ${statement.semanticNode} expression=${JSON.stringify(statement.expression)}`,
);
} else if (statement.kind === "if_return") {
lines.push(
`${indent}semantic_node ${statement.semanticNode} condition=${JSON.stringify(statement.condition)} value=${JSON.stringify(statement.value)}`,
);
} else if (statement.kind === "for_loop") {
lines.push(
`${indent}semantic_node ${statement.semanticNode} target=${JSON.stringify(statement.target)} iterator=${JSON.stringify(statement.iterator)}`,
);
for (const child of statement.body) {
pythonStatementLinks(lines, child, depth + 1);
}
}
}
function pythonFunctionLinks(functionTree) {
const lines = [
"python_function_syntax_tree",
` semantic_node function_definition signature=${JSON.stringify(functionTree.signature)}`,
];
for (const statement of functionTree.body) {
pythonStatementLinks(lines, statement, 1);
}
return lines.join("\n");
}
// Build the Python candidate for whichever synthesis task the prompt names or
// evidences. The task slug keys both the meaning lexicon and the verbatim
// PYTHON_SYNTHESIS_CANDIDATES blueprint (function tree, tests, fragments).
// Mirrors synthesize_python_candidate in
// src/solver_handlers/program_synthesis.rs.
function synthesizePythonCandidate(prompt, normalized, functionName) {
const task = meaningsWithRole(ROLE_PROGRAM_SYNTHESIS_TASK).find(
(candidate) =>
functionName === candidate.slug || synthesisTaskEvidenced(candidate, normalized),
);
if (!task) return null;
const definition = PYTHON_SYNTHESIS_CANDIDATES[task.slug];
if (!definition) return null;
const signature =
declaredPythonSignature(prompt, functionName) || definition.defaultSignature;
const functionTree = { signature, body: definition.body };
return Object.assign({}, definition, {
functionName: task.slug,
functionTree,
});
}
// Issue #395: does the prompt evidence the operation with this canonical slug,
// in any supported language? Mirrors OperationVocabulary::matches in
// src/seed/operation_vocabulary.rs (substring match per phrase/combo).
function operationMatchesSlug(slug, normalized) {
for (const operation of operationVocabulary()) {
if (operation.slug === slug && operationFormMatches(normalized, operation)) {
return true;
}
}
return false;
}
// Issue #395: type ontology for the universal list coding algorithm.
// Byte mirror of data/seed/numeric-list-operations.lino — each operation maps to a
// family (list_transformation / list_reduction) and a result kind
// (list / scalar), so the worker reasons about the task from data instead of a
// per-case handler. Transformations support numeric lists and quoted text lists;
// reductions remain numeric.
const NUMERIC_LIST_OPERATIONS_LINO = [
"numeric_list_operations",
' operation "sort"',
' family "list_transformation"',
' result_kind "list"',
' direction "ascending"',
' operation "reverse_sort"',
' family "list_transformation"',
' result_kind "list"',
' direction "descending"',
' operation "reverse"',
' family "list_transformation"',
' result_kind "list"',
' direction "given_order_reversed"',
' operation "sum"',
' family "list_reduction"',
' result_kind "scalar"',
' identity "0"',
' operation "product"',
' family "list_reduction"',
' result_kind "scalar"',
' identity "1"',
' operation "minimum"',
' family "list_reduction"',
' result_kind "scalar"',
' selects "extreme"',
' operation "maximum"',
' family "list_reduction"',
' result_kind "scalar"',
' selects "extreme"',
].join("\n");
let cachedNumericListOntology = null;
// Parse the numeric-list ontology into
// { canonical: { family, resultKind, direction } }. Mirrors result_kind_for /
// family_for / direction_for in src/solver_handlers/numeric_list/mod.rs.
function numericListOntology() {
if (cachedNumericListOntology) return cachedNumericListOntology;
const root = parseLinoTree(NUMERIC_LIST_OPERATIONS_LINO);
const container =
root.children.find((child) => child.name === "numeric_list_operations") ||
root;
const map = {};
for (const node of container.children) {
if (node.name !== "operation") continue;
const familyNode = node.children.find((c) => c.name === "family");
const kindNode = node.children.find((c) => c.name === "result_kind");
const directionNode = node.children.find((c) => c.name === "direction");
map[node.value] = {
family: familyNode ? familyNode.value : "list_transformation",
resultKind: kindNode ? kindNode.value : "list",
direction: directionNode ? directionNode.value : "",
};
}
cachedNumericListOntology = map;
return cachedNumericListOntology;
}
function numericListResultKind(canonical) {
const entry = numericListOntology()[canonical];
return entry && entry.resultKind === "scalar" ? "scalar" : "list";
}
function numericListFamily(canonical) {
const entry = numericListOntology()[canonical];
return entry && entry.family === "list_reduction"
? "list_reduction"
: "list_transformation";
}
// Issue #395: lift every number token (signed / decimal) from the prompt, in
// order. Mirrors parse_numbers in src/solver_handlers/numeric_list/mod.rs: a
// leading sign only starts a literal when it is not glued to a preceding letter
// or digit, and the surface text is preserved verbatim for echoing and codegen.
function parseNumericListNumbers(prompt) {
const chars = Array.from(String(prompt || ""));
const isDigit = (ch) => ch >= "0" && ch <= "9";
const isAlnum = (ch) => /[\p{L}\p{N}]/u.test(ch);
const numbers = [];
let index = 0;
while (index < chars.length) {
const ch = chars[index];
let sign = "";
if (
(ch === "-" || ch === "+") &&
index + 1 < chars.length &&
isDigit(chars[index + 1]) &&
!(index > 0 && isAlnum(chars[index - 1]))
) {
sign = ch;
index += 1;
} else if (!isDigit(ch)) {
index += 1;
continue;
}
const start = index;
while (index < chars.length && isDigit(chars[index])) index += 1;
if (
index < chars.length &&
chars[index] === "." &&
index + 1 < chars.length &&
isDigit(chars[index + 1])
) {
index += 1;
while (index < chars.length && isDigit(chars[index])) index += 1;
}
if (start === index) continue;
const text = sign + chars.slice(start, index).join("");
const value = Number(text);
if (!Number.isNaN(value)) numbers.push({ text, value, kind: "number" });
}
return numbers;
}
function parseNumericListQuotedStrings(prompt) {
const chars = Array.from(String(prompt || ""));
const items = [];
let index = 0;
while (index < chars.length) {
const quote = chars[index];
if (quote !== '"' && quote !== "'") {
index += 1;
continue;
}
index += 1;
let text = "";
while (index < chars.length) {
const ch = chars[index];
if (ch === "\\" && index + 1 < chars.length) {
text += chars[index + 1];
index += 2;
continue;
}
if (ch === quote) break;
text += ch;
index += 1;
}
if (index < chars.length && chars[index] === quote) {
index += 1;
if (text.length) items.push({ text, value: text, kind: "string" });
}
}
return items;
}
function parseNumericListItems(prompt, canonical) {
const quoted = parseNumericListQuotedStrings(prompt);
if (numericListFamily(canonical) === "list_transformation" && quoted.length >= 2) {
return quoted;
}
return parseNumericListNumbers(prompt);
}
// Issue #395: render the array literal nodes. When projected to source, callers
// join these surfaces with ", ". Keeping them as an array first lets the worker
// build and trace a CST/AST-like program tree before source rendering.
// When the list mixes integers and decimals, integer surfaces gain a `.0` suffix
// so statically-typed targets keep a single element type. Mirrors number_literals.
function numericListStringLiteral(value) {
return `"${String(value)
.replace(/\\/g, "\\\\")
.replace(/"/g, '\\"')
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
.replace(/\t/g, "\\t")}"`;
}
function numericListLiterals(items, valueType) {
return items.map((item) => {
if (item.kind === "string") return numericListStringLiteral(item.text);
return valueType.label === "float" && !item.text.includes(".")
? `${item.text}.0`
: item.text;
});
}
// Issue #395: localized phrasing for the four supported UI languages. The
// numbers, code, and result are language-independent; only the surrounding prose
// differs. Mirrors Localization in src/solver_handlers/numeric_list/mod.rs; the
// `sort` / `reverse_sort` sentences are byte-identical to the original handler so
// existing golden assertions stay green.
const NUMERIC_LIST_LOCALIZATION = {
ru: {
resultLabel: "Результат:",
intro: (canonical, lang, given) =>
({
sort: `Вот код на ${lang}, который сортирует числа ${given} по возрастанию:`,
reverse_sort: `Вот код на ${lang}, который сортирует числа ${given} по убыванию:`,
reverse: `Вот код на ${lang}, который переворачивает числа ${given}:`,
sum: `Вот код на ${lang}, который суммирует числа ${given}:`,
product: `Вот код на ${lang}, который перемножает числа ${given}:`,
minimum: `Вот код на ${lang}, который находит наименьшее из чисел ${given}:`,
maximum: `Вот код на ${lang}, который находит наибольшее из чисел ${given}:`,
})[canonical],
},
hi: {
resultLabel: "परिणाम:",
intro: (canonical, lang, given) =>
({
sort: `यह ${lang} कोड है जो संख्याओं ${given} को आरोही क्रम में क्रमबद्ध करता है:`,
reverse_sort: `यह ${lang} कोड है जो संख्याओं ${given} को अवरोही क्रम में क्रमबद्ध करता है:`,
reverse: `यह ${lang} कोड है जो संख्याओं ${given} को उलट देता है:`,
sum: `यह ${lang} कोड है जो संख्याओं ${given} का योग करता है:`,
product: `यह ${lang} कोड है जो संख्याओं ${given} का गुणनफल निकालता है:`,
minimum: `यह ${lang} कोड है जो संख्याओं ${given} में से सबसे छोटी ढूँढता है:`,
maximum: `यह ${lang} कोड है जो संख्याओं ${given} में से सबसे बड़ी ढूँढता है:`,
})[canonical],
},
zh: {
resultLabel: "结果:",
intro: (canonical, lang, given) =>
({
sort: `这是用 ${lang} 编写的将数字 ${given} 按升序排序的代码:`,
reverse_sort: `这是用 ${lang} 编写的将数字 ${given} 按降序排序的代码:`,
reverse: `这是用 ${lang} 编写的将数字 ${given} 反转的代码:`,
sum: `这是用 ${lang} 编写的对数字 ${given} 求和的代码:`,
product: `这是用 ${lang} 编写的计算数字 ${given} 乘积的代码:`,
minimum: `这是用 ${lang} 编写的求数字 ${given} 最小值的代码:`,
maximum: `这是用 ${lang} 编写的求数字 ${given} 最大值的代码:`,
})[canonical],
},
en: {
resultLabel: "Result:",
intro: (canonical, lang, given, valueTypeLabel) => {
const noun = valueTypeLabel === "string" ? "strings" : "numbers";
return ({
sort: `Here is ${lang} code that sorts the ${noun} ${given} in ascending order:`,
reverse_sort: `Here is ${lang} code that sorts the ${noun} ${given} in descending order:`,
reverse: `Here is ${lang} code that reverses the ${noun} ${given}:`,
sum: `Here is ${lang} code that sums the ${noun} ${given}:`,
product: `Here is ${lang} code that multiplies the ${noun} ${given}:`,
minimum: `Here is ${lang} code that finds the smallest of the ${noun} ${given}:`,
maximum: `Here is ${lang} code that finds the largest of the ${noun} ${given}:`,
})[canonical];
},
},
};
// Issue #395: recognize which numeric-list operation the prompt asks for, in
// priority order. Sort phrasings are checked first because "sort in reverse
// order" legitimately contains the bare `reverse` verb; the descending variant
// wins whenever the `reverse_sort` phrasing is present. Mirrors detect_operation.
function detectNumericListOperation(normalized) {
if (
operationMatchesSlug("sort", normalized) ||
operationMatchesSlug("reverse_sort", normalized)
) {
return operationMatchesSlug("reverse_sort", normalized) ? "reverse_sort" : "sort";
}
if (operationMatchesSlug("reverse", normalized)) return "reverse";
for (const canonical of ["sum", "product", "minimum", "maximum"]) {
if (operationMatchesSlug(canonical, normalized)) return canonical;
}
return null;
}
// Issue #395: format a computed scalar so its textual form matches the runnable
// code's stdout: an integer with no decimal point when every input was an
// integer. Mirrors format_scalar.
function numericListFormatScalar(value, isFloat) {
return isFloat ? String(value) : String(Math.round(value));
}
// Issue #395: apply the operation to the parsed numbers and return the surface
// tokens to display — the reordered list for a transformation, or a single
// computed scalar for a reduction. Mirrors compute.
function numericListCompare(left, right) {
if (left.kind === "number" && right.kind === "number") {
return left.value < right.value ? -1 : left.value > right.value ? 1 : 0;
}
return left.text < right.text ? -1 : left.text > right.text ? 1 : 0;
}
function computeNumericList(canonical, items, isFloat) {
if (numericListFamily(canonical) === "list_transformation") {
const ordered = items.slice();
if (canonical === "sort") {
ordered.sort(numericListCompare);
} else if (canonical === "reverse_sort") {
ordered.sort((a, b) => numericListCompare(b, a));
} else {
ordered.reverse();
}
return ordered.map((n) => n.text);
}
const numbers = items.filter((item) => item.kind === "number");
let value;
if (canonical === "sum") value = numbers.reduce((acc, n) => acc + n.value, 0);
else if (canonical === "product") value = numbers.reduce((acc, n) => acc * n.value, 1);
else if (canonical === "minimum") value = numbers.reduce((acc, n) => Math.min(acc, n.value), Infinity);
else value = numbers.reduce((acc, n) => Math.max(acc, n.value), -Infinity);
return [numericListFormatScalar(value, isFloat)];
}
// Issue #395: structural numeric-list program tree. The worker mirrors the
// Rust `NumericProgram`: source code is a projection of these semantic nodes,
// and the tree is preserved in evidence/trace for inspection. The value-class
// label doubles as the `on` selector for coding-idiom cases; per-language
// storage types live in CODING_IDIOMS_LINO, not in this record.
function numericListValueType(items, isFloat) {
if (items.some((item) => item.kind === "string")) {
return { label: "string" };
}
return { label: isFloat ? "float" : "integer" };
}
function numericListBuildProgram(slug, items, canonical, isFloat) {
const family = numericListFamily(canonical);
const valueType = numericListValueType(items, isFloat);
const list = codingDefaultName("list");
const statements = [
{
kind: "literal_list",
name: list,
mutable: codingMutatesListInPlace(slug),
},
];
if (family === "list_transformation") {
const target = codingDefaultName("transformed");
statements.push({
kind: canonical === "reverse" ? "reverse_list" : "sort_list",
source: list,
target,
canonical,
direction: numericListDirection(canonical),
});
statements.push({
kind: "print_joined",
source: target,
separator: ", ",
});
} else {
const target = codingDefaultName("reduced");
statements.push({
kind: "reduce_list",
source: list,
target,
reducer: canonical,
});
statements.push({ kind: "print_scalar", source: target });
}
return {
languageSlug: slug,
canonical,
valueType,
literals: numericListLiterals(items, valueType),
displayValues: items.map((item) => item.text),
statements,
};
}
// Transformation direction token, read from the numeric-list ontology instead
// of a hardcoded match. Mirrors direction_for in
// src/solver_handlers/numeric_list/mod.rs.
function numericListDirection(canonical) {
const entry = numericListOntology()[canonical];
return entry ? entry.direction : "";
}
function numericListProgramLinks(program) {
const lines = [
"program_syntax_tree",
` language ${program.languageSlug}`,
` value_type ${program.valueType.label}`,
` operation ${program.canonical}`,
` literal_values ${(program.displayValues || program.literals).join("|")}`,
];
for (const statement of program.statements) {
if (statement.kind === "literal_list") {
lines.push(
` semantic_node literal_list name=${statement.name} mutable=${statement.mutable}`,
);
} else if (statement.kind === "sort_list" || statement.kind === "reverse_list") {
lines.push(
` semantic_node ${statement.kind} source=${statement.source} target=${statement.target} direction=${statement.direction}`,
);
} else if (statement.kind === "reduce_list") {
lines.push(
` semantic_node reduce_list source=${statement.source} target=${statement.target} reducer=${statement.reducer}`,
);
} else if (statement.kind === "print_joined") {
lines.push(
` semantic_node print_joined source=${statement.source} separator="${statement.separator}"`,
);
} else if (statement.kind === "print_scalar") {
lines.push(` semantic_node print_scalar source=${statement.source}`);
}
}
return lines.join("\n");
}
// Issue #395: code-idioms knowledge base for the universal list coding
// algorithm. Byte mirror of data/seed/coding-idioms.lino (regenerate with
// experiments/generate-coding-idioms-embed.mjs) — each language declares
// scaffolds (one per operation family) and idioms (named code fragments with
// cases selected by operation and value class), inheriting through extends.
// The composer below discovers the code composition from this data at
// execution time; there are no per-language renderer functions. Mirrors
// src/solver_handlers/numeric_list/codegen.rs.
const CODING_IDIOMS_LINO = [
"coding_idioms",
" description \"Per-language code composition knowledge for the universal list coding algorithm (issue #395). Each language defines scaffolds (one per operation family) and idioms (named fragments with cases selected by operation and value class). The composer discovers the coding algorithm at execution time: it picks the scaffold for the operation family, then recursively expands {slot} placeholders by matching idiom cases against the requested operation and the value class of the list items. Computed slots (literal, count, tab, list, transformed, reduced, type) are resolved from the program data; every other slot names an idiom defined here. Languages inherit scaffolds, types, names, and idioms through extends, nearest definition wins.\"",
" defaults",
" list \"numbers\"",
" transformed \"sorted\"",
" reduced \"result\"",
" language \"javascript\"",
" scaffold \"list_transformation\"",
" code 'const {list} = [{literal}];\\nconst {transformed} = {action};\\nconsole.log({transformed}.join(\", \"));'",
" scaffold \"list_reduction\"",
" code \"const {list} = [{literal}];\\nconst {reduced} = {expression};\\nconsole.log({reduced});\"",
" idiom \"action\"",
" case",
" for \"sort\"",
" on \"string\"",
" code \"[...{list}].sort()\"",
" case",
" for \"reverse_sort\"",
" on \"string\"",
" code \"[...{list}].sort().reverse()\"",
" case",
" for \"sort\"",
" code \"[...{list}].sort((a, b) => a - b)\"",
" case",
" for \"reverse_sort\"",
" code \"[...{list}].sort((a, b) => b - a)\"",
" case",
" for \"reverse\"",
" code \"[...{list}].reverse()\"",
" idiom \"expression\"",
" case",
" for \"sum\"",
" code \"{list}.reduce((a, b) => a + b, 0)\"",
" case",
" for \"product\"",
" code \"{list}.reduce((a, b) => a * b, 1)\"",
" case",
" for \"minimum\"",
" code \"Math.min(...{list})\"",
" case",
" for \"maximum\"",
" code \"Math.max(...{list})\"",
" language \"typescript\"",
" extends \"javascript\"",
" types",
" integer \"number\"",
" float \"number\"",
" string \"string\"",
" scaffold \"list_transformation\"",
" code 'const {list}: {type}[] = [{literal}];\\nconst {transformed} = {action};\\nconsole.log({transformed}.join(\", \"));'",
" scaffold \"list_reduction\"",
" code \"const {list}: {type}[] = [{literal}];\\nconst {reduced} = {expression};\\nconsole.log({reduced});\"",
" language \"python\"",
" names",
" transformed \"sorted_numbers\"",
" scaffold \"list_transformation\"",
" code '{list} = [{literal}]\\n{transformed} = {action}\\nprint(\", \".join(str(n) for n in {transformed}))'",
" scaffold \"list_reduction\"",
" code \"{imports}{list} = [{literal}]\\n{reduced} = {expression}\\nprint({reduced})\"",
" idiom \"imports\"",
" case",
" for \"product\"",
" code \"import math\\n\\n\"",
" case",
" for \"any\"",
" code \"\"",
" idiom \"action\"",
" case",
" for \"sort\"",
" code \"sorted({list})\"",
" case",
" for \"reverse_sort\"",
" code \"sorted({list}, reverse=True)\"",
" case",
" for \"reverse\"",
" code \"list(reversed({list}))\"",
" idiom \"expression\"",
" case",
" for \"sum\"",
" code \"sum({list})\"",
" case",
" for \"product\"",
" code \"math.prod({list})\"",
" case",
" for \"minimum\"",
" code \"min({list})\"",
" case",
" for \"maximum\"",
" code \"max({list})\"",
" language \"rust\"",
" mutable_list \"true\"",
" types",
" integer \"i64\"",
" float \"f64\"",
" string \"&str\"",
" scaffold \"list_transformation\"",
" code 'fn main() {\\n let mut {list}: Vec<{type}> = vec![{literal}];\\n {action}\\n let rendered: Vec<String> = {list}.iter().map(|n| n.to_string()).collect();\\n println!(\"{}\", rendered.join(\", \"));\\n}'",
" scaffold \"list_reduction\"",
" code 'fn main() {\\n let {list}: Vec<{type}> = vec![{literal}];\\n let {reduced} = {expression};\\n println!(\"{}\", {reduced});\\n}'",
" idiom \"action\"",
" case",
" for \"sort\"",
" on \"integer string\"",
" code \"{list}.sort();\"",
" case",
" for \"reverse_sort\"",
" on \"integer string\"",
" code \"{list}.sort_by(|a, b| b.cmp(a));\"",
" case",
" for \"sort\"",
" on \"float\"",
" code \"{list}.sort_by(|a, b| a.partial_cmp(b).unwrap());\"",
" case",
" for \"reverse_sort\"",
" on \"float\"",
" code \"{list}.sort_by(|a, b| b.partial_cmp(a).unwrap());\"",
" case",
" for \"reverse\"",
" code \"{list}.reverse();\"",
" idiom \"expression\"",
" case",
" for \"sum\"",
" code \"{list}.iter().copied().sum::<{type}>()\"",
" case",
" for \"product\"",
" code \"{list}.iter().copied().product::<{type}>()\"",
" case",
" for \"minimum\"",
" on \"integer\"",
" code \"*{list}.iter().min().unwrap()\"",
" case",
" for \"maximum\"",
" on \"integer\"",
" code \"*{list}.iter().max().unwrap()\"",
" case",
" for \"minimum\"",
" on \"float\"",
" code \"{list}.iter().copied().fold(f64::INFINITY, f64::min)\"",
" case",
" for \"maximum\"",
" on \"float\"",
" code \"{list}.iter().copied().fold(f64::NEG_INFINITY, f64::max)\"",
" language \"go\"",
" mutable_list \"true\"",
" types",
" integer \"int\"",
" float \"float64\"",
" string \"string\"",
" scaffold \"list_transformation\"",
" code 'package main\\n\\nimport (\\n{imports}\\n)\\n\\nfunc main() {\\n{tab}{list} := []{type}{{literal}}\\n{tab}{action}\\n{tab}parts := make([]string, len({list}))\\n{tab}for i, n := range {list} {\\n{tab}{tab}parts[i] = {format_item}\\n{tab}}\\n{tab}fmt.Println(strings.Join(parts, \", \"))\\n}'",
" scaffold \"list_reduction\"",
" code 'package main\\n\\nimport \"fmt\"\\n\\nfunc main() {\\n{tab}{list} := []{type}{{literal}}\\n{tab}{body}\\n{tab}fmt.Println({reduced})\\n}'",
" idiom \"imports\"",
" case",
" for \"sort reverse_sort\"",
" on \"string\"",
" code '{tab}\"fmt\"\\n{tab}\"sort\"\\n{tab}\"strings\"'",
" case",
" for \"sort reverse_sort\"",
" code '{tab}\"fmt\"\\n{tab}\"sort\"\\n{tab}\"strconv\"\\n{tab}\"strings\"'",
" case",
" for \"reverse\"",
" on \"string\"",
" code '{tab}\"fmt\"\\n{tab}\"strings\"'",
" case",
" for \"reverse\"",
" code '{tab}\"fmt\"\\n{tab}\"strconv\"\\n{tab}\"strings\"'",
" idiom \"action\"",
" case",
" for \"sort\"",
" on \"string\"",
" code \"sort.Strings({list})\"",
" case",
" for \"reverse_sort\"",
" on \"string\"",
" code \"sort.Sort(sort.Reverse(sort.StringSlice({list})))\"",
" case",
" for \"sort\"",
" code \"sort.Slice({list}, func(i, j int) bool { return {list}[i] < {list}[j] })\"",
" case",
" for \"reverse_sort\"",
" code \"sort.Slice({list}, func(i, j int) bool { return {list}[i] > {list}[j] })\"",
" case",
" for \"reverse\"",
" code \"for i, j := 0, len({list})-1; i < j; i, j = i+1, j-1 {\\n{tab}{tab}{list}[i], {list}[j] = {list}[j], {list}[i]\\n{tab}}\"",
" idiom \"format_item\"",
" case",
" for \"any\"",
" on \"integer\"",
" code \"strconv.Itoa(n)\"",
" case",
" for \"any\"",
" on \"float\"",
" code \"strconv.FormatFloat(n, 'g', -1, 64)\"",
" case",
" for \"any\"",
" on \"string\"",
" code \"n\"",
" idiom \"body\"",
" case",
" for \"sum\"",
" code \"var {reduced} {type} = 0\\n{tab}for _, n := range {list} {\\n{tab}{tab}{reduced} += n\\n{tab}}\"",
" case",
" for \"product\"",
" code \"var {reduced} {type} = 1\\n{tab}for _, n := range {list} {\\n{tab}{tab}{reduced} *= n\\n{tab}}\"",
" case",
" for \"minimum\"",
" code \"{reduced} := {list}[0]\\n{tab}for _, n := range {list}[1:] {\\n{tab}{tab}if n < {reduced} {\\n{tab}{tab}{tab}{reduced} = n\\n{tab}{tab}}\\n{tab}}\"",
" case",
" for \"maximum\"",
" code \"{reduced} := {list}[0]\\n{tab}for _, n := range {list}[1:] {\\n{tab}{tab}if n > {reduced} {\\n{tab}{tab}{tab}{reduced} = n\\n{tab}{tab}}\\n{tab}}\"",
" language \"ruby\"",
" scaffold \"list_transformation\"",
" code '{list} = [{literal}]\\n{transformed} = {action}\\nputs {transformed}.join(\", \")'",
" scaffold \"list_reduction\"",
" code \"{list} = [{literal}]\\n{reduced} = {expression}\\nputs {reduced}\"",
" idiom \"action\"",
" case",
" for \"sort\"",
" code \"{list}.sort\"",
" case",
" for \"reverse_sort\"",
" code \"{list}.sort.reverse\"",
" case",
" for \"reverse\"",
" code \"{list}.reverse\"",
" idiom \"expression\"",
" case",
" for \"sum\"",
" code \"{list}.sum\"",
" case",
" for \"product\"",
" code \"{list}.inject(1, :*)\"",
" case",
" for \"minimum\"",
" code \"{list}.min\"",
" case",
" for \"maximum\"",
" code \"{list}.max\"",
" language \"java\"",
" mutable_list \"true\"",
" types",
" integer \"int\"",
" float \"double\"",
" string \"String\"",
" scaffold \"list_transformation\"",
" code 'import java.util.Arrays;\\nimport java.util.Collections;\\nimport java.util.StringJoiner;\\n\\npublic class Main {\\n public static void main(String[] args) {\\n {type}[] {list} = {{literal}};\\n {action}\\n StringJoiner joiner = new StringJoiner(\", \");\\n for ({type} n : {list}) joiner.add(String.valueOf(n));\\n System.out.println(joiner.toString());\\n }\\n}'",
" scaffold \"list_reduction\"",
" code 'public class Main {\\n public static void main(String[] args) {\\n {type}[] {list} = {{literal}};\\n {body}\\n System.out.println({reduced});\\n }\\n}'",
" idiom \"boxed\"",
" case",
" for \"any\"",
" on \"integer\"",
" code \"Integer\"",
" case",
" for \"any\"",
" on \"float\"",
" code \"Double\"",
" case",
" for \"any\"",
" on \"string\"",
" code \"String\"",
" idiom \"action\"",
" case",
" for \"sort\"",
" code \"Arrays.sort({list});\"",
" case",
" for \"reverse_sort\"",
" on \"string\"",
" code \"Arrays.sort({list}, Collections.reverseOrder());\"",
" case",
" for \"reverse_sort\"",
" code '{boxed}[] boxed = Arrays.stream({list}).boxed().toArray({boxed}[]::new);\\n Arrays.sort(boxed, Collections.reverseOrder());\\n for (int i = 0; i < {list}.length; i++) {list}[i] = boxed[i];'",
" case",
" for \"reverse\"",
" code 'for (int i = 0, j = {list}.length - 1; i < j; i++, j--) {\\n {type} tmp = {list}[i];\\n {list}[i] = {list}[j];\\n {list}[j] = tmp;\\n }'",
" idiom \"body\"",
" case",
" for \"sum\"",
" code \"{type} {reduced} = 0;\\n for ({type} n : {list}) {reduced} += n;\"",
" case",
" for \"product\"",
" code \"{type} {reduced} = 1;\\n for ({type} n : {list}) {reduced} *= n;\"",
" case",
" for \"minimum\"",
" code \"{type} {reduced} = {list}[0];\\n for ({type} n : {list}) {reduced} = Math.min({reduced}, n);\"",
" case",
" for \"maximum\"",
" code \"{type} {reduced} = {list}[0];\\n for ({type} n : {list}) {reduced} = Math.max({reduced}, n);\"",
" language \"csharp\"",
" types",
" integer \"int\"",
" float \"double\"",
" string \"string\"",
" scaffold \"list_transformation\"",
" code 'using System;\\nusing System.Linq;\\n\\nclass Program {\\n static void Main() {\\n {type}[] {list} = {{literal}};\\n var {transformed} = {action};\\n Console.WriteLine(string.Join(\", \", {transformed}));\\n }\\n}'",
" scaffold \"list_reduction\"",
" code 'using System;\\nusing System.Linq;\\n\\nclass Program {\\n static void Main() {\\n {type}[] {list} = {{literal}};\\n var {reduced} = {expression};\\n Console.WriteLine({reduced});\\n }\\n}'",
" idiom \"action\"",
" case",
" for \"sort\"",
" code \"{list}.OrderBy(n => n)\"",
" case",
" for \"reverse_sort\"",
" code \"{list}.OrderByDescending(n => n)\"",
" case",
" for \"reverse\"",
" code \"{list}.Reverse()\"",
" idiom \"expression\"",
" case",
" for \"sum\"",
" code \"{list}.Sum()\"",
" case",
" for \"product\"",
" code \"{list}.Aggregate(({type})1, (a, b) => a * b)\"",
" case",
" for \"minimum\"",
" code \"{list}.Min()\"",
" case",
" for \"maximum\"",
" code \"{list}.Max()\"",
" language \"cpp\"",
" mutable_list \"true\"",
" types",
" integer \"int\"",
" float \"double\"",
" string \"std::string\"",
" scaffold \"list_transformation\"",
" code '#include <algorithm>\\n#include <iostream>\\n{string_include}#include <vector>\\n\\nint main() {\\n std::vector<{type}> {list} = {{literal}};\\n {action}\\n for (size_t i = 0; i < {list}.size(); ++i) {\\n if (i) std::cout << \", \";\\n std::cout << {list}[i];\\n }\\n std::cout << std::endl;\\n return 0;\\n}'",
" scaffold \"list_reduction\"",
" code '#include <algorithm>\\n#include <iostream>\\n#include <numeric>\\n{string_include}#include <vector>\\n\\nint main() {\\n std::vector<{type}> {list} = {{literal}};\\n {type} {reduced} = {expression};\\n std::cout << {reduced} << std::endl;\\n return 0;\\n}'",
" idiom \"string_include\"",
" case",
" for \"any\"",
" on \"string\"",
" code \"#include <string>\\n\"",
" case",
" for \"any\"",
" code \"\"",
" idiom \"action\"",
" case",
" for \"sort\"",
" code \"std::sort({list}.begin(), {list}.end());\"",
" case",
" for \"reverse_sort\"",
" code \"std::sort({list}.begin(), {list}.end(), std::greater<>());\"",
" case",
" for \"reverse\"",
" code \"std::reverse({list}.begin(), {list}.end());\"",
" idiom \"expression\"",
" case",
" for \"sum\"",
" code \"std::accumulate({list}.begin(), {list}.end(), ({type})0)\"",
" case",
" for \"product\"",
" code \"std::accumulate({list}.begin(), {list}.end(), ({type})1, std::multiplies<{type}>())\"",
" case",
" for \"minimum\"",
" code \"*std::min_element({list}.begin(), {list}.end())\"",
" case",
" for \"maximum\"",
" code \"*std::max_element({list}.begin(), {list}.end())\"",
" language \"c\"",
" mutable_list \"true\"",
" types",
" integer \"int\"",
" float \"double\"",
" string \"char *\"",
" scaffold \"list_transformation\"",
" code '#include <stdio.h>\\n#include <stdlib.h>\\n{string_include}\\n{comparator}int main(void) {\\n{body}\\n for (size_t i = 0; i < count; ++i) {\\n if (i) printf(\", \");\\n printf(\"{printf_format}\", {list}[i]);\\n }\\n printf(\"\\\\n\");\\n return 0;\\n}'",
" scaffold \"list_reduction\"",
" code '#include <stdio.h>\\n\\nint main(void) {\\n {type} {list}[] = {{literal}};\\n size_t count = {count};\\n {type} {reduced} = {init};\\n for (size_t i = 0; i < count; ++i) {\\n{step}\\n }\\n printf(\"{printf_format}\\\\n\", {reduced});\\n return 0;\\n}'",
" idiom \"string_include\"",
" case",
" for \"any\"",
" on \"string\"",
" code \"#include <string.h>\\n\"",
" case",
" for \"any\"",
" code \"\"",
" idiom \"printf_format\"",
" case",
" for \"any\"",
" on \"integer\"",
" code \"%d\"",
" case",
" for \"any\"",
" on \"float\"",
" code \"%g\"",
" case",
" for \"any\"",
" on \"string\"",
" code \"%s\"",
" idiom \"body\"",
" case",
" for \"reverse\"",
" code ' {type} {list}[] = {{literal}};\\n size_t count = {count};\\n for (size_t i = 0, j = count - 1; i < j; ++i, --j) {\\n {type} tmp = {list}[i];\\n {list}[i] = {list}[j];\\n {list}[j] = tmp;\\n }'",
" case",
" for \"sort reverse_sort\"",
" code ' {type} {list}[] = {{literal}};\\n size_t count = {count};\\n qsort({list}, count, sizeof({type}), compare);'",
" idiom \"comparator\"",
" case",
" for \"reverse\"",
" code \"\"",
" case",
" for \"sort\"",
" on \"integer\"",
" code 'static int compare(const void *a, const void *b) {\\n return (*(const int *)a - *(const int *)b);\\n}\\n\\n'",
" case",
" for \"reverse_sort\"",
" on \"integer\"",
" code 'static int compare(const void *a, const void *b) {\\n return (*(const int *)b - *(const int *)a);\\n}\\n\\n'",
" case",
" for \"sort\"",
" on \"float\"",
" code 'static int compare(const void *a, const void *b) {\\n double diff = *(const double *)a - *(const double *)b;\\n return (diff > 0) - (diff < 0);\\n}\\n\\n'",
" case",
" for \"reverse_sort\"",
" on \"float\"",
" code 'static int compare(const void *a, const void *b) {\\n double diff = *(const double *)b - *(const double *)a;\\n return (diff > 0) - (diff < 0);\\n}\\n\\n'",
" case",
" for \"sort\"",
" on \"string\"",
" code 'static int compare(const void *a, const void *b) {\\n const char *left = *(const char * const *)a;\\n const char *right = *(const char * const *)b;\\n return strcmp(left, right);\\n}\\n\\n'",
" case",
" for \"reverse_sort\"",
" on \"string\"",
" code 'static int compare(const void *a, const void *b) {\\n const char *left = *(const char * const *)a;\\n const char *right = *(const char * const *)b;\\n return strcmp(right, left);\\n}\\n\\n'",
" idiom \"init\"",
" case",
" for \"sum\"",
" on \"integer\"",
" code \"0\"",
" case",
" for \"sum\"",
" on \"float\"",
" code \"0.0\"",
" case",
" for \"product\"",
" on \"integer\"",
" code \"1\"",
" case",
" for \"product\"",
" on \"float\"",
" code \"1.0\"",
" case",
" for \"minimum maximum\"",
" code \"{list}[0]\"",
" idiom \"step\"",
" case",
" for \"sum\"",
" code \" {reduced} += {list}[i];\"",
" case",
" for \"product\"",
" code \" {reduced} *= {list}[i];\"",
" case",
" for \"minimum\"",
" code \" if ({list}[i] < {reduced}) {reduced} = {list}[i];\"",
" case",
" for \"maximum\"",
" code \" if ({list}[i] > {reduced}) {reduced} = {list}[i];\"",
].join("\n");
// Safety caps mirroring MAX_EXPANSION_DEPTH / MAX_INHERITANCE_DEPTH in
// src/solver_handlers/numeric_list/codegen.rs: anything deeper is a
// definition cycle in the seed data and must fail composition.
const CODING_IDIOMS_MAX_EXPANSION_DEPTH = 8;
const CODING_IDIOMS_MAX_INHERITANCE_DEPTH = 4;
let cachedCodingIdiomCatalog;
// Parsed root of the coding-idioms knowledge base, loaded once per worker.
// Mirrors idiom_catalog in src/solver_handlers/numeric_list/codegen.rs.
function codingIdiomCatalog() {
if (cachedCodingIdiomCatalog === undefined) {
const root = parseLinoTree(CODING_IDIOMS_LINO);
cachedCodingIdiomCatalog =
root.children.find((child) => child.name === "coding_idioms") || null;
}
return cachedCodingIdiomCatalog;
}
function codingFindChild(node, name) {
return node.children.find((child) => child.name === name) || null;
}
function codingChildValue(node, name) {
const child = codingFindChild(node, name);
return child ? child.value : "";
}
// The `language "<slug>"` node followed by its transitive `extends` parents,
// nearest first. Empty when the catalog does not know the slug. Mirrors
// language_chain.
function codingLanguageChain(catalog, slug) {
const chain = [];
let current = slug;
while (chain.length < CODING_IDIOMS_MAX_INHERITANCE_DEPTH) {
const node = catalog.children.find(
(child) => child.name === "language" && child.value === current,
);
if (!node) break;
chain.push(node);
const parent = codingChildValue(node, "extends");
if (!parent) break;
current = parent;
}
return chain;
}
// The canonical semantic-tree variable name for `key` (list / transformed /
// reduced), read from the catalog's `defaults` node. Mirrors default_name.
function codingDefaultName(key) {
const catalog = codingIdiomCatalog();
if (!catalog) return "";
const defaults = codingFindChild(catalog, "defaults");
if (!defaults) return "";
return codingChildValue(defaults, key);
}
// Whether the language declares (in the knowledge base) that its list
// transformations mutate the literal list in place rather than building a new
// collection. Mirrors mutates_list_in_place.
function codingMutatesListInPlace(slug) {
const catalog = codingIdiomCatalog();
if (!catalog) return false;
for (const language of codingLanguageChain(catalog, slug)) {
const node = codingFindChild(language, "mutable_list");
if (node) return node.value === "true";
}
return false;
}
// One rendering pass: the resolved language chain plus the computed slot
// bindings for the program being rendered. Mirrors Composer::new.
function codingComposer(program, chain) {
const bindings = new Map();
// Per-language variable names: the nearest `names` override in the
// inheritance chain, else the catalog-wide `defaults` entry.
for (const key of ["list", "transformed", "reduced"]) {
let name = "";
for (const language of chain) {
const names = codingFindChild(language, "names");
if (names) {
const entry = codingFindChild(names, key);
if (entry) {
name = entry.value;
break;
}
}
}
if (!name) name = codingDefaultName(key);
if (name) bindings.set(key, name);
}
// The language's storage type for the program's value class, from the
// nearest `types` table in the inheritance chain that declares it.
for (const language of chain) {
const types = codingFindChild(language, "types");
if (types) {
const entry = codingFindChild(types, program.valueType.label);
if (entry) {
bindings.set("type", entry.value);
break;
}
}
}
bindings.set("literal", program.literals.join(", "));
bindings.set("count", String(program.literals.length));
// Links Notation values cannot encode a raw tab, so templates spell it as a
// computed slot.
bindings.set("tab", "\t");
return { program, chain, bindings };
}
// The scaffold template for the operation family, from the nearest language
// in the chain that declares one. Mirrors Composer::scaffold.
function codingScaffold(chain, family) {
for (const language of chain) {
const scaffold = language.children.find(
(child) => child.name === "scaffold" && child.value === family,
);
if (scaffold) return codingChildValue(scaffold, "code");
}
return null;
}
// The idiom definition for `slot`, from the nearest language in the chain
// that declares it. Idioms are not merged across the chain: the nearest
// definition fully shadows inherited ones. Mirrors Composer::idiom.
function codingIdiom(chain, slot) {
for (const language of chain) {
const idiom = language.children.find(
(child) => child.name === "idiom" && child.value === slot,
);
if (idiom) return idiom;
}
return null;
}
// Pick the idiom case that best matches the requested operation and value
// class. A case applies when its `for` tokens contain the operation (or
// `any`) and its `on` tokens, when present, contain the value class. Specific
// matches outrank generic ones: an exact operation token scores over `any`,
// and a value-class constraint scores over none. The first case with the
// highest score wins, so declaration order breaks ties. Mirrors
// Composer::select_case.
function codingSelectCase(composer, idiom) {
const operation = composer.program.canonical;
const valueClass = composer.program.valueType.label;
let best = null;
for (const candidate of idiom.children) {
if (candidate.name !== "case") continue;
const forTokens = codingChildValue(candidate, "for")
.split(/\s+/)
.filter(Boolean);
const operationExact = forTokens.includes(operation);
if (!operationExact && !forTokens.includes("any")) continue;
const on = codingFindChild(candidate, "on");
if (on && !on.value.split(/\s+/).filter(Boolean).includes(valueClass)) {
continue;
}
const score = (operationExact ? 2 : 0) + (on ? 1 : 0);
if (!best || score > best.score) {
best = { code: codingChildValue(candidate, "code"), score };
}
}
return best ? best.code : null;
}
// Recursively expand `{slot}` placeholders. Computed bindings are inserted
// verbatim (never rescanned, so user-provided literals cannot inject further
// slots); idiom slots expand their selected case recursively; any other brace
// sequence — `{}`, `{ return`, `{{literal}}` — is ordinary target-language
// syntax and passes through unchanged. Mirrors Composer::expand.
function codingExpand(composer, template, depth) {
if (depth > CODING_IDIOMS_MAX_EXPANSION_DEPTH) return null;
const chars = Array.from(template);
let out = "";
let index = 0;
while (index < chars.length) {
if (chars[index] !== "{") {
out += chars[index];
index += 1;
continue;
}
let end = index + 1;
while (end < chars.length && /[a-z0-9_]/.test(chars[end])) end += 1;
if (end >= chars.length || chars[end] !== "}" || end === index + 1) {
out += "{";
index += 1;
continue;
}
const name = chars.slice(index + 1, end).join("");
if (composer.bindings.has(name)) {
out += composer.bindings.get(name);
} else {
const idiom = codingIdiom(composer.chain, name);
if (idiom) {
const code = codingSelectCase(composer, idiom);
if (code === null) return null;
const expanded = codingExpand(composer, code, depth + 1);
if (expanded === null) return null;
out += expanded;
} else {
out += `{${name}}`;
}
}
index = end + 1;
}
return out;
}
// Render the program tree into the requested target language by composing the
// scaffold and idioms discovered in the coding-idioms knowledge base. Returns
// null when the knowledge base has no language section, no scaffold for the
// operation's family, or no idiom case matching the operation and value class
// — composition failures are explicit, never silent fallbacks. Mirrors
// NumericProgram::render.
function numericListProgramSource(program) {
const catalog = codingIdiomCatalog();
if (!catalog) return null;
const chain = codingLanguageChain(catalog, program.languageSlug);
if (!chain.length) return null;
const composer = codingComposer(program, chain);
const scaffold = codingScaffold(chain, numericListFamily(program.canonical));
if (scaffold === null) return null;
return codingExpand(composer, scaffold, 0);
}
// Issue #395: universal numeric-list coding algorithm. "<operation> these
// numbers in <language>, give me the code and the result" produces generated
// code plus the deterministically-computed result. Mirrors try_numeric_list /
// solve_numeric_list in src/solver_handlers/numeric_list/mod.rs. The result is
// computed in-solver (every operation is a pure, total function over the parsed
// values) — no runtime is embedded.
function tryNumericList(prompt) {
const normalized = normalizePrompt(prompt);
// Defer genuine function-synthesis prompts ("write a function that returns the
// sum of 3 and 5") to the dedicated program-synthesis handler.
if (operationMatchesSlug("function", normalized) || prompt.includes("def ")) {
return null;
}
const canonical = detectNumericListOperation(normalized);
if (!canonical) return null;
// Reductions phrase-overlap with ordinary prose far more than the imperative
// transform verbs do, so they only fire when the prompt explicitly asks for
// code (the `code_request` operation) — exactly the issue #395 contract.
if (
numericListFamily(canonical) === "list_reduction" &&
!operationMatchesSlug("code_request", normalized)
) {
return null;
}
const slug = programLanguageFromPrompt(normalized);
if (!slug) return null;
const languageInfo = WRITE_PROGRAM_LANGUAGES[slug];
if (!languageInfo) return null;
const items = parseNumericListItems(prompt, canonical);
if (items.length < 2) return null;
if (
numericListFamily(canonical) === "list_reduction" &&
items.some((item) => item.kind === "string")
) {
return null;
}
const isFloat = items.some(
(item) => item.kind === "number" && !Number.isInteger(item.value),
);
const given = items.map((n) => n.text);
const result = computeNumericList(canonical, items, isFloat);
const program = numericListBuildProgram(slug, items, canonical, isFloat);
const syntaxTree = numericListProgramLinks(program);
const code = numericListProgramSource(program);
if (code === null) return null;
const resultKind = numericListResultKind(canonical);
const family = numericListFamily(canonical);
const responseLanguage = detectLanguage(prompt);
const parts =
NUMERIC_LIST_LOCALIZATION[responseLanguage] || NUMERIC_LIST_LOCALIZATION.en;
const givenText = given.join(", ");
const shown = resultKind === "scalar" ? result[0] : result.join(", ");
const resultText = result.join(", ");
const body = `${parts.intro(canonical, languageInfo.name, givenText, program.valueType.label)}\n\n\`\`\`${languageInfo.fence}\n${code}\n\`\`\`\n\n${parts.resultLabel} ${shown}`;
return {
intent: "write_program",
content: body,
confidence: 1.0,
evidence: [
`response:write_program:numeric_list:${canonical}:${slug}`,
`formalize:(@USER OP:${canonical} values:[${given.join(" ")}] value_type:${program.valueType.label} language:${slug} result_kind:${resultKind} request:[code result])`,
`synthesis:spec:language=${slug} task=numeric_list operation=${canonical} family=${family} result_kind=${resultKind} value_type=${program.valueType.label}`,
`synthesis:given:${givenText}`,
`synthesis:syntax_tree:${syntaxTree}`,
`composition:code_fragment:${code}`,
"execution_status:computed deterministically",
"execution_environment:pure in-solver evaluation of a decidable numeric-list operation",
`execution_result:${resultText}`,
],
trace: [
`synthesis:spec:language=${slug} task=numeric_list operation=${canonical} family=${family} result_kind=${resultKind} value_type=${program.valueType.label}`,
`synthesis:syntax_tree:${syntaxTree}`,
`execution_result:${resultText}`,
],
};
}
function tryProgramSynthesis(prompt, normalized) {
// Issue #386: canonicalize first so native operation verbs (write / implement /
// return …) in any supported language are recognised, exactly like
// try_program_synthesis in src/solver_handlers/program_synthesis.rs.
const canonical = canonicalizedPrompt(normalized);
if (!looksLikePythonFunctionSynthesis(prompt, canonical)) return null;
const functionName = extractPythonFunctionName(prompt, canonical);
if (!functionName) return null;
const candidate = synthesizePythonCandidate(prompt, canonical, functionName);
if (!candidate) return null;
const assertionCount = candidate.tests.length;
const syntaxTree = pythonFunctionLinks(candidate.functionTree);
const code = renderPythonFunction(candidate.functionTree);
const evidence = [
`response:write_program:synthesized:python:${candidate.id}`,
`synthesis:spec:language=python function=${candidate.functionName}`,
`synthesis:syntax_tree:${syntaxTree}`,
...candidate.fragments.map((fragment) => `composition:code_fragment:${fragment}`),
`synthesis:candidate:${candidate.id}`,
"synthesis:workspace:browser-worker-deterministic-verifier",
"action_log:create_file:solution.py",
"action_log:run_command:python3 solution.py",
`synthesis:candidate_execution:command=python3 solution.py exit=Some(0) timed_out=false assertion_count=${assertionCount}`,
`synthesis:verification:tests_passed assertion_count=${assertionCount}`,
"execution_status:tests passed",
"execution_environment:browser worker deterministic mirror; no filesystem side effects",
];
const body = [
"Here is a derived Python function synthesized from the specification and verified in an isolated workspace:",
"",
"```python",
code + "```",
"",
"Execution status: tests passed in isolated bounded agent workspace.",
"Check command: `python3 solution.py`",
`Test outcome: ${assertionCount}/${assertionCount} assertions passed.`,
"Workspace isolation: browser worker deterministic verifier with no filesystem side effects.",
];
return {
intent: "write_program",
content: body.join("\n"),
confidence: 1.0,
evidence,
trace: [
`synthesis:candidate:${candidate.id}`,
`synthesis:syntax_tree:${syntaxTree}`,
`synthesis:verification:tests_passed assertion_count=${assertionCount}`,
],
};
}
function quoteCloseFor(open) {
if (open === "'") return "'";
if (open === '"') return '"';
if (open === "`") return "`";
return "";
}
function quotedTextSegments(text) {
const segments = [];
let cursor = 0;
const source = String(text || "");
while (cursor < source.length) {
let found = -1;
let close = "";
for (let index = cursor; index < source.length; index += 1) {
close = quoteCloseFor(source[index]);
if (close) {
found = index;
break;
}
}
if (found < 0) break;
const contentStart = found + 1;
const contentEnd = source.indexOf(close, contentStart);
if (contentEnd < 0) break;
segments.push(source.slice(contentStart, contentEnd));
cursor = contentEnd + 1;
}
return segments;
}
function textAfterColon(prompt) {
const text = String(prompt || "");
const index = text.lastIndexOf(":");
if (index < 0) return "";
return text
.slice(index + 1)
.trim()
.replace(/^["'`]+|["'`]+$/g, "")
.trim();
}
function appendSimpleTextOperations(normalized, operations) {
if (normalized.includes("lowercase") || normalized.includes("lower case")) {
operations.push({ slug: "lowercase" });
} else if (normalized.includes("uppercase") || normalized.includes("upper case")) {
operations.push({ slug: "uppercase" });
}
if (normalized.includes("reverse words") || normalized.includes("reverse the words")) {
operations.push({ slug: "reverse_words" });
}
if (
normalized.includes("extract") &&
(normalized.includes("email") || normalized.includes("e mail"))
) {
operations.push({ slug: "extract_email" });
}
if (
normalized.includes("deduplicate lines") ||
normalized.includes("dedupe lines") ||
(normalized.includes("deduplicate") && normalized.includes("line"))
) {
operations.push({ slug: "deduplicate_lines" });
}
if (
normalized.includes("sort lines") ||
normalized.includes("sort the lines") ||
(normalized.includes("sort") && normalized.includes("line"))
) {
operations.push({ slug: "sort_lines" });
}
if (normalized.includes("count unique words") || normalized.includes("count distinct words")) {
operations.push({ slug: "count_unique_words" });
}
}
function parseTextManipulationRequest(prompt, normalized) {
const quoted = quotedTextSegments(prompt);
const operations = [];
let input = "";
if (normalized.includes("replace")) {
if (quoted.length < 2) return null;
operations.push({ slug: "replace_text", from: quoted[0], to: quoted[1] });
input = quoted[2] || textAfterColon(prompt);
} else if (normalized.includes("count occurrences")) {
if (quoted.length < 1) return null;
operations.push({ slug: "count_occurrences", needle: quoted[0] });
input = quoted[1] || textAfterColon(prompt);
} else {
input = quoted[0] || textAfterColon(prompt);
appendSimpleTextOperations(normalized, operations);
}
if (!input || operations.length === 0) return null;
return { input, operations };
}
function cleanEmailCandidate(candidate) {
return String(candidate || "")
.replace(/^[^A-Za-z0-9@._+-]+|[^A-Za-z0-9@._+-]+$/g, "")
.replace(/[.]+$/g, "");
}
function looksLikeEmail(candidate) {
const text = String(candidate || "");
if ((text.match(/@/g) || []).length !== 1) return false;
const [local, domain] = text.split("@");
return Boolean(
local &&
domain &&
domain.includes(".") &&
domain.split(".").every((part) => part && /^[A-Za-z0-9-]+$/.test(part)),
);
}
function countUniqueWords(input) {
return new Set(
String(input || "")
.split(/\s+/)
.map((word) => word.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, ""))
.filter(Boolean),
).size;
}
function deduplicateLines(input) {
const seen = new Set();
const lines = [];
for (const line of String(input || "").split(/\r?\n/)) {
if (!seen.has(line)) {
seen.add(line);
lines.push(line);
}
}
return lines;
}
function applyTextOperation(operation, input) {
switch (operation.slug) {
case "uppercase":
return String(input).toUpperCase();
case "lowercase":
return String(input).toLowerCase();
case "replace_text":
return String(input).split(operation.from).join(operation.to);
case "reverse_words":
return String(input).split(/\s+/).filter(Boolean).reverse().join(" ");
case "extract_email":
return String(input)
.split(/\s+/)
.map(cleanEmailCandidate)
.filter(looksLikeEmail)
.join("\n");
case "count_occurrences":
return operation.needle ? String(input).split(operation.needle).length - 1 : "0";
case "count_unique_words":
return String(countUniqueWords(input));
case "deduplicate_lines":
return deduplicateLines(input).join("\n");
case "sort_lines":
return String(input).split(/\r?\n/).sort().join("\n");
default:
return String(input);
}
}
function buildTextManipulationChain(input, operations) {
const steps = [];
const usedRules = new Set();
let current = String(input);
for (const operation of operations) {
let ruleId = `rule_${operation.slug}`;
for (let suffix = 2; usedRules.has(ruleId); suffix += 1) {
ruleId = `rule_${operation.slug}_${suffix}`;
}
usedRules.add(ruleId);
const before = current;
const after = applyTextOperation(operation, before);
steps.push({ operation, ruleId, before, after });
current = after;
}
return { result: current, steps };
}
function tryTextManipulation(prompt, normalized) {
const request = parseTextManipulationRequest(prompt, normalized);
if (!request) return null;
const chain = buildTextManipulationChain(request.input, request.operations);
const ruleChain = chain.steps.map((step) => step.ruleId).join(">");
const rulesId = synthesisStableId(
"text_substitution_rules",
`${request.input}:${ruleChain}`,
);
const traceId = synthesisStableId(
"text_substitution_trace",
chain.steps.map((step) => `${step.before}->${step.after}`).join("|"),
);
const graphId = synthesisStableId("text_substitution_graph", `${request.input}:${chain.result}`);
const evidence = [
`text_input:bytes=${request.input.length} chars=${Array.from(request.input).length}`,
...chain.steps.flatMap((step) => [
`text_operation:${step.operation.slug}`,
`text_rule:${step.ruleId}`,
]),
`text_rule_chain:${ruleChain}`,
`text_substitution_rules:${rulesId}`,
`text_substitution_trace:${traceId}`,
`text_substitution_graph:${graphId}`,
`text_result:${chain.result}`,
];
return {
intent: "text_manipulation",
content: chain.result,
confidence: 1.0,
evidence,
trace: [
`text_substitution_trace:${traceId}`,
`text_result:${truncateSynthesisTrace(chain.result, 80)}`,
],
};
}
function isProofIntroBoundary(ch) {
return /\s|[.,:;!?…。,:;!?]/u.test(ch);
}
function stripProofClaimNoise(value) {
return String(value || "")
.replace(/^[\s,.:;!?…。,:;!?]+/u, "")
.trim();
}
function startsWithProofVerb(normalized, verb) {
if (!normalized.startsWith(verb)) return false;
const tail = normalized.slice(verb.length);
if (!tail) return true;
return !/[\p{L}\p{N}]/u.test(tail.charAt(0));
}
function hasProofRequestShape(normalized) {
const text = String(normalized || "").trim();
if (!text) return false;
// A proof request is recognised structurally from the meaning lexicon, not
// from words baked into this file: a clause-initial bare directive verb
// (proof_directive, with the verb-boundary check), a request-frame lead in any
// language that needs no `that` clause (proof_request_lead), or a mid-prompt
// proof assertion marker in any language (proof_marker).
return (
bareLiterals(ROLE_PROOF_DIRECTIVE).some((verb) => startsWithProofVerb(text, verb)) ||
prefixLiterals(ROLE_PROOF_REQUEST_LEAD).some((lead) => text.startsWith(lead)) ||
lexiconMentionsRoleSubstring(ROLE_PROOF_MARKER, text)
);
}
function extractProofClaim(normalized) {
const trimmed = String(normalized || "").trim();
// The claim scaffolds (each ending in the … slot) come from the
// proof_claim_scaffold role in declaration order, so the first matching
// prefix wins exactly as before — every that/что/कि variant is listed ahead
// of its shorter sibling in the lexicon. Comma variants are absent: the
// normaliser rewrites the comma to a space, making them unreachable here.
const prefixes = prefixLiterals(ROLE_PROOF_CLAIM_SCAFFOLD);
for (const prefix of prefixes) {
if (trimmed.startsWith(prefix)) {
return stripProofClaimNoise(trimmed.slice(prefix.length));
}
}
for (const prefix of prefixes) {
const index = trimmed.indexOf(prefix);
if (index <= 0) continue;
const before = trimmed.charAt(index - 1);
if (isProofIntroBoundary(before)) {
return stripProofClaimNoise(trimmed.slice(index + prefix.length));
}
}
return trimmed;
}
function matchesEuclidPrimeClaim(claim) {
const lower = String(claim || "").toLowerCase();
return (
lower.includes("infinitely many primes") ||
lower.includes("infinitely many prime numbers") ||
lower.includes("infinitude of primes") ||
lower.includes("prime numbers are infinite") ||
lower.includes("euclid") ||
lower.includes("евклид") ||
(lower.includes("прост") && lower.includes("бесконеч")) ||
lower.includes("अनंत अभाज्य") ||
lower.includes("अनन्त अभाज्य") ||
lower.includes("अभाज्य संख्याएँ अनंत") ||
lower.includes("अभाज्य संख्याएं अनंत") ||
lower.includes("अभाज्य संख्याएँ अनन्त") ||
lower.includes("अभाज्य संख्याएं अनन्त") ||
lower.includes("无穷多素数") ||
lower.includes("无穷多个素数") ||
lower.includes("素数有无穷") ||
lower.includes("素数无穷") ||
lower.includes("無窮多素數") ||
lower.includes("素數有無窮") ||
lower.includes("素數無窮") ||
lower.includes("欧几里得")
);
}
function euclidPrimeProofBody(language) {
if (language === "ru") {
return [
"Как я понял запрос: трактуем запрос как формальное утверждение «Простых чисел бесконечно много.» и доказываем методом «от противного» в relative-meta-logic.",
"",
"Доказательство (метод: от противного).",
"",
"Утверждение: Простых чисел бесконечно много.",
"1. Работаем в элементарной теории чисел, формализуемой в арифметике Пеано (PA): простое число — это целое число больше 1, положительные делители которого только 1 и оно само. В доказательстве используется теорема PA: у каждого целого числа больше 1 есть простой делитель; это формальный контекст для тактики от противного в relative-meta-logic.",
"2. Предположим противное: простых чисел конечное число; обозначим их p₁, p₂, …, pₙ.",
"3. Рассмотрим число N = p₁·p₂·…·pₙ + 1. Это целое число, большее единицы.",
"4. По основной теореме арифметики у N есть простой делитель q. Если q = pᵢ для некоторого i, то pᵢ делит и p₁·p₂·…·pₙ, и N, а значит делит их разность, равную 1 — противоречие.",
"5. Значит, q — простое число, не входящее в список p₁, …, pₙ, что противоречит предположению о полноте списка.",
"",
"Предположение несостоятельно, следовательно простых чисел бесконечно много. ∎",
].join("\n");
}
if (language === "hi") {
return [
"मैंने प्रश्न को कैसे समझा: प्रश्न को औपचारिक कथन \"अभाज्य संख्याएँ अनंत हैं।\" मानकर relative-meta-logic में \"विरोधाभास\" विधि से प्रमाणित कर रहे हैं।",
"",
"प्रमाण (विधि: विरोधाभास)।",
"",
"कथन: अभाज्य संख्याएँ अनंत हैं।",
"1. हम प्राथमिक संख्या-सिद्धांत में काम करते हैं, जिसे Peano arithmetic (PA) में औपचारिक किया जा सकता है: अभाज्य वह पूर्णांक है जो 1 से बड़ा है और जिसके धनात्मक भाजक केवल 1 और वही संख्या हैं। प्रमाण PA के इस प्रमेय का उपयोग करता है कि 1 से बड़े हर पूर्णांक का कोई अभाज्य भाजक होता है; यही relative-meta-logic की contradiction युक्ति का औपचारिक संदर्भ है।",
"2. विरोधाभास हेतु मान लीजिए कि अभाज्य संख्याएँ केवल सीमित संख्या में हैं: p₁, p₂, …, pₙ।",
"3. संख्या N = p₁·p₂·…·pₙ + 1 लीजिए। N एक से बड़ा पूर्णांक है।",
"4. अंकगणित की मूल प्रमेय से N का कोई अभाज्य भाजक q है। यदि किसी i के लिए q = pᵢ हो, तो pᵢ संख्या p₁·p₂·…·pₙ और N दोनों को विभाजित करेगा, अर्थात उनका अंतर 1 भी विभाजित करेगा — असंभव।",
"5. अतः q एक ऐसा अभाज्य है जो सूची p₁, …, pₙ में नहीं है, जो सूची के पूर्ण होने की परिकल्पना का खंडन करता है।",
"",
"अतः परिकल्पना असत्य है और अभाज्य संख्याएँ अनंत हैं। ∎",
].join("\n");
}
if (language === "zh") {
return [
"对问题的理解: 把问题视为形式命题“素数有无穷多个。”, 在 relative-meta-logic 中用“反证法”方法证明。",
"",
"证明 (方法: 反证法)。",
"",
"命题: 素数有无穷多个。",
"1. 在可由 Peano arithmetic (PA) 形式化的初等数论中工作: 素数是大于 1 的整数, 其正因数只有 1 和自身。证明使用 PA 中的定理: 每个大于 1 的整数都有素因数; 这就是 relative-meta-logic 反证策略的形式上下文。",
"2. 假设素数仅有有限多个, 记为 p₁、p₂、…、pₙ。",
"3. 构造数 N = p₁·p₂·…·pₙ + 1。N 是大于 1 的整数。",
"4. 由算术基本定理, N 有一个素因数 q。若 q = pᵢ, 则 pᵢ 同时整除 p₁·p₂·…·pₙ 与 N, 因而整除二者之差 1, 矛盾。",
"5. 因此 q 是不在 p₁, …, pₙ 中的素数, 与假设矛盾。",
"",
"假设不成立, 故素数有无穷多个。∎",
].join("\n");
}
return [
"How I interpreted the request: treating the request as the formal claim \"There are infinitely many prime numbers.\" and discharging it by contradiction inside relative-meta-logic.",
"",
"Proof (method: contradiction).",
"",
"Statement: There are infinitely many prime numbers.",
"1. Work in elementary number theory, formalizable in Peano arithmetic (PA): a prime is an integer greater than 1 whose only positive divisors are 1 and itself. The proof uses the PA theorem that every integer greater than 1 has a prime divisor; this is the formal context for the relative-meta-logic contradiction tactic.",
"2. Assume for contradiction that only finitely many primes exist; call them p₁, p₂, …, pₙ.",
"3. Form the number N = p₁·p₂·…·pₙ + 1. Then N is an integer greater than 1.",
"4. By the fundamental theorem of arithmetic, N has a prime divisor q. If q = pᵢ for some i, then pᵢ divides both p₁·p₂·…·pₙ and N, so pᵢ divides their difference, which is 1 — impossible.",
"5. Hence q is a prime not in the list p₁, …, pₙ, contradicting the assumption that the list was complete.",
"",
"The assumption fails, so there are infinitely many primes. ∎",
].join("\n");
}
function genericProofPlanBody(prompt, language) {
if (language === "ru") {
return [
"Как я понял запрос: утверждение нужно доказать, но для финального исполнения не хватает выбранной формальной системы.",
"",
"План доказательства (метод: формализация и проверка).",
"",
`Утверждение: ${String(prompt || "").trim()}`,
"1. Зафиксировать систему аксиом, например PA для арифметики, ZFC для теории множеств или конкретную теорию предметной области.",
"2. Перевести утверждение в закрытую формулу этой системы.",
"3. Выбрать тактику relative-meta-logic: rewrite, induction, contradiction или поиск контрпримера.",
"4. Проверить каждый шаг и вернуть либо сертификат доказательства, либо контрпример.",
"",
"Чтобы выполнить доказательство полностью, нужен явный набор аксиом и точная формулировка утверждения.",
].join("\n");
}
if (language === "hi") {
return [
"मैंने प्रश्न को कैसे समझा: यह प्रमाण का अनुरोध है, पर अंतिम निष्पादन के लिए चुनी हुई औपचारिक प्रणाली चाहिए।",
"",
"प्रमाण योजना (विधि: औपचारिकरण और सत्यापन)।",
"",
`कथन: ${String(prompt || "").trim()}`,
"1. कोई अभिगृहीत प्रणाली चुनें, जैसे arithmetic के लिए PA, set theory के लिए ZFC, या किसी क्षेत्र-विशेष की theory।",
"2. कथन को उस प्रणाली के बंद सूत्र में अनुवादित करें।",
"3. relative-meta-logic की tactic चुनें: rewrite, induction, contradiction, या counterexample search।",
"4. प्रत्येक चरण जाँचें और proof certificate या counterexample लौटाएँ।",
"",
"प्रमाण पूरा करने के लिए सटीक axiom set और closed statement चाहिए।",
].join("\n");
}
if (language === "zh") {
return [
"对问题的理解: 该提示要求证明, 但最终执行需要选定的形式系统。",
"",
"证明计划 (方法: 形式化与验证)。",
"",
`命题: ${String(prompt || "").trim()}`,
"1. 固定一个公理系统, 例如 arithmetic 用 PA, set theory 用 ZFC, 或某个领域专用理论。",
"2. 将命题翻译成该系统中的闭公式。",
"3. 选择 relative-meta-logic 策略: rewrite、induction、contradiction 或 counterexample search。",
"4. 检查每一步, 并返回证明证书或反例。",
"",
"要完成证明, 需要精确的 axiom set 和 closed statement。",
].join("\n");
}
return [
"How I interpreted the request: the prompt asks for a proof, but final execution needs a chosen formal system.",
"",
"Proof plan (method: formalization and verification).",
"",
`Statement: ${String(prompt || "").trim()}`,
"1. Fix an axiom system, for example PA for arithmetic, ZFC for set theory, or a domain-specific theory.",
"2. Translate the claim into a closed formula in that system.",
"3. Choose a relative-meta-logic tactic: rewrite, induction, contradiction, or counterexample search.",
"4. Check each step and return either a proof certificate or a counterexample.",
"",
"To finish the proof, provide the exact axiom set and a closed statement.",
].join("\n");
}
function tryProofRequest(prompt, normalized, language) {
if (!hasProofRequestShape(normalized)) return null;
const claim = extractProofClaim(normalized);
if (matchesEuclidPrimeClaim(claim)) {
return {
intent: "proof_request",
content: euclidPrimeProofBody(language),
confidence: 0.85,
evidence: [
"policy:proof_request",
"pipeline:planned:relative-meta-logic",
"proof_outcome:proven",
"proof_method:contradiction",
`language:${language}`,
],
};
}
return {
intent: "proof_request",
content: genericProofPlanBody(prompt, language),
confidence: 0.6,
evidence: [
"policy:proof_request",
"pipeline:planned:relative-meta-logic",
"proof_outcome:partial_plan",
`language:${language}`,
],
};
}
// WEEKDAY_CYCLE keeps only the rendering surfaces (display names + Russian
// case forms) used to phrase an answer. Issue #386: the *recognition* words
// (the former `aliases`, plus the next/previous/today/day/question markers)
// are no longer hardcoded here — they live as self-describing meanings in
// data/seed/meanings-calendar.lino under the `calendar_*` roles, embedded
// below in MEANINGS_LINO. The detection functions query the lexicon by role
// and map a matched weekday slug back to its cycle entry; mirrors
// src/solver_handlers/calendar.rs.
const WEEKDAY_CYCLE = [
{
slug: "monday",
en: "Monday",
ru: "понедельник",
hi: "सोमवार",
zh: "星期一",
ruGenitive: "понедельника",
ruInstrumental: "понедельником",
},
{
slug: "tuesday",
en: "Tuesday",
ru: "вторник",
hi: "मंगलवार",
zh: "星期二",
ruGenitive: "вторника",
ruInstrumental: "вторником",
},
{
slug: "wednesday",
en: "Wednesday",
ru: "среда",
hi: "बुधवार",
zh: "星期三",
ruGenitive: "среды",
ruInstrumental: "средой",
},
{
slug: "thursday",
en: "Thursday",
ru: "четверг",
hi: "गुरुवार",
zh: "星期四",
ruGenitive: "четверга",
ruInstrumental: "четвергом",
},
{
slug: "friday",
en: "Friday",
ru: "пятница",
hi: "शुक्रवार",
zh: "星期五",
ruGenitive: "пятницы",
ruInstrumental: "пятницей",
},
{
slug: "saturday",
en: "Saturday",
ru: "суббота",
hi: "शनिवार",
zh: "星期六",
ruGenitive: "субботы",
ruInstrumental: "субботой",
},
{
slug: "sunday",
en: "Sunday",
ru: "воскресенье",
hi: "रविवार",
zh: "星期日",
ruGenitive: "воскресенья",
ruInstrumental: "воскресеньем",
},
];
function hasCalendarCjkCharacter(term) {
return /[\u4e00-\u9fff]/u.test(term);
}
function isCalendarWordCharacter(character) {
return /[\p{L}\p{N}_]/u.test(character);
}
function containsCalendarTerm(text, term) {
if (hasCalendarCjkCharacter(term)) {
return String(text || "").includes(term);
}
let index = String(text || "").indexOf(term);
while (index !== -1) {
const before = index > 0 ? Array.from(text.slice(0, index)).pop() : "";
const after = Array.from(text.slice(index + term.length))[0] || "";
if (
(!before || !isCalendarWordCharacter(before)) &&
(!after || !isCalendarWordCharacter(after))
) {
return true;
}
index = text.indexOf(term, index + term.length);
}
return false;
}
// Issue #386: calendar recognition is driven entirely by the self-describing
// `calendar_*` meanings (see data/seed/meanings-calendar.lino, embedded in
// MEANINGS_LINO). day-reference / today / weekday words match with the
// boundary-aware containsCalendarTerm; direction and question words match as
// loose substrings (parity with raw `str::contains` in calendar.rs). The
// boundary vs. substring split per role mirrors the Rust handler exactly.
function mentionsWeekdayContext(normalized) {
return wordsForRole(ROLE_CALENDAR_DAY_REFERENCE).some((word) =>
containsCalendarTerm(normalized, word),
);
}
function mentionsCurrentDayQuestion(normalized) {
const mentionsToday = wordsForRole(ROLE_CALENDAR_TODAY).some((word) =>
containsCalendarTerm(normalized, word),
);
if (!mentionsToday) return false;
const asksForDay = wordsForRole(ROLE_CALENDAR_DAY_REFERENCE).some((word) =>
containsCalendarTerm(normalized, word),
);
const questionLike = wordsForRole(ROLE_CALENDAR_QUESTION).some((word) =>
normalized.includes(word),
);
return asksForDay && questionLike;
}
function detectWeekdayOperation(normalized) {
const hasNext = wordsForRole(ROLE_CALENDAR_DIRECTION_NEXT).some((marker) =>
normalized.includes(marker),
);
const hasPrevious = wordsForRole(ROLE_CALENDAR_DIRECTION_PREVIOUS).some(
(marker) => normalized.includes(marker),
);
if (hasNext && !hasPrevious) return "next";
if (hasPrevious && !hasNext) return "previous";
return null;
}
function detectWeekday(normalized) {
for (const meaning of meaningsWithRole(ROLE_CALENDAR_WEEKDAY)) {
if (meaning.words.some((word) => containsCalendarTerm(normalized, word))) {
const entry = WEEKDAY_CYCLE.find((weekday) => weekday.slug === meaning.slug);
if (entry) return entry;
}
}
return null;
}
function shiftWeekday(weekday, operation) {
const index = WEEKDAY_CYCLE.indexOf(weekday);
const offset = operation === "next" ? 1 : -1;
return WEEKDAY_CYCLE[(index + offset + WEEKDAY_CYCLE.length) % WEEKDAY_CYCLE.length];
}
function validCalendarTimeZone(candidate) {
const timeZone = cleanContextValue(candidate);
if (!timeZone) return "";
try {
new Intl.DateTimeFormat("en-US", { timeZone }).format(new Date(0));
return timeZone;
} catch (_error) {
return "";
}
}
function resolvedCalendarTimeZone(userContext) {
const fromContext = validCalendarTimeZone(userContext && userContext.timeZone);
if (fromContext) return fromContext;
try {
return Intl.DateTimeFormat().resolvedOptions().timeZone || "";
} catch (_error) {
return "";
}
}
function calendarDateInTimeZone(date, timeZone) {
const options = {
year: "numeric",
month: "2-digit",
day: "2-digit",
};
if (timeZone) options.timeZone = timeZone;
const parts = new Intl.DateTimeFormat("en-CA", options).formatToParts(date);
const value = (type) => parts.find((part) => part.type === type)?.value || "";
const year = Number(value("year"));
const month = Number(value("month"));
const day = Number(value("day"));
if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) {
return null;
}
const iso = `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
const dayIndex = new Date(Date.UTC(year, month - 1, day)).getUTCDay();
const weekday = WEEKDAY_CYCLE[(dayIndex + 6) % 7];
return { iso, weekday };
}
function currentCalendarDate(userContext) {
const reference = new Date();
const timeZone = resolvedCalendarTimeZone(userContext);
return {
timeZone: timeZone || "local",
date: calendarDateInTimeZone(reference, timeZone),
};
}
function renderCurrentDay(language, weekday, isoDate, timeZone) {
if (language === "ru") {
return `Сегодня ${weekday.ru}, ${isoDate} (${timeZone}).`;
}
if (language === "hi") {
return `आज ${weekday.hi} है, ${isoDate} (${timeZone}).`;
}
if (language === "zh") {
return `今天是${weekday.zh},${isoDate}(${timeZone})。`;
}
return `Today is ${weekday.en}, ${isoDate} (${timeZone}).`;
}
function renderWeekdayRelation(language, operation, source, result) {
const delta = operation === "next" ? "+1" : "-1";
if (language === "ru") {
if (operation === "next") {
return `После ${source.ruGenitive} наступает ${result.ru}. Я сдвинул ${source.ru} на ${delta} в семидневном календарном цикле.`;
}
return `Перед ${source.ruInstrumental} идёт ${result.ru}. Я сдвинул ${source.ru} на ${delta} в семидневном календарном цикле.`;
}
if (language === "hi") {
if (operation === "next") {
return `${source.hi} के बाद ${result.hi} आता है। मैं सात दिनों के कैलेंडर चक्र में ${source.hi} को ${delta} दिन सरकाता हूँ।`;
}
return `${source.hi} से पहले ${result.hi} आता है। मैं सात दिनों के कैलेंडर चक्र में ${source.hi} को ${delta} दिन सरकाता हूँ।`;
}
if (language === "zh") {
if (operation === "next") {
return `${source.zh}之后是${result.zh}。我在七天的日历循环中将${source.zh}移动${delta}天。`;
}
return `${source.zh}之前是${result.zh}。我在七天的日历循环中将${source.zh}移动${delta}天。`;
}
if (operation === "next") {
return `The day after ${source.en} is ${result.en}. I move ${source.en} by ${delta} in the seven-day calendar cycle.`;
}
return `The day before ${source.en} is ${result.en}. I move ${source.en} by ${delta} in the seven-day calendar cycle.`;
}
function tryCalendarReasoning(prompt, normalized, userContext = {}) {
if (mentionsCurrentDayQuestion(normalized)) {
const language = detectLanguage(prompt);
const resolved = currentCalendarDate(userContext);
if (!resolved.date) return null;
return {
intent: "calendar_current_day",
content: renderCurrentDay(
language,
resolved.date.weekday,
resolved.date.iso,
resolved.timeZone,
),
confidence: 1.0,
evidence: [
"calendar:clock:browser",
`calendar:today:${resolved.date.iso}`,
`calendar:weekday:${resolved.date.weekday.slug}`,
`calendar:time_zone:${resolved.timeZone}`,
`language:${language}`,
],
};
}
if (!mentionsWeekdayContext(normalized)) return null;
const operation = detectWeekdayOperation(normalized);
if (!operation) return null;
const source = detectWeekday(normalized);
if (!source) return null;
const result = shiftWeekday(source, operation);
const language = detectLanguage(prompt);
return {
intent: "calendar_weekday_relation",
content: renderWeekdayRelation(language, operation, source, result),
confidence: 1.0,
evidence: [
"calendar:cycle:monday,tuesday,wednesday,thursday,friday,saturday,sunday",
`calendar:subject_weekday:${source.slug}`,
`calendar:operation:${operation}:${source.slug}`,
`calendar:result_weekday:${result.slug}`,
`language:${language}`,
],
};
}
function renderConceptInContext(language, context, record) {
const contextNormalized = normalizeConceptTerm(context);
const contextRecord = resolveContextRecord(contextNormalized);
const contextLabel =
(contextRecord && contextLabelFor(contextRecord, language)) || context;
const sameAsLabel =
String(contextLabel).trim().toLowerCase() ===
String(context).trim().toLowerCase();
const intentVariant = sameAsLabel
? "concept_lookup_in_context_no_alias"
: "concept_lookup_in_context";
const variantTable = MULTILINGUAL_ANSWERS[intentVariant] || {};
const baseTable = MULTILINGUAL_ANSWERS.concept_lookup_in_context || {};
const templateEntry =
variantTable[language] ||
variantTable.en ||
baseTable[language] ||
baseTable.en ||
null;
const template = templateEntry
? (typeof templateEntry === "string" ? templateEntry : templateEntry.text)
: "In the context of {context} ({context_label}), {term} ({category}) means: {summary}\n\nSource: {source} ({source_kind}).";
const localized = localizedConceptFor(record, language);
const term = (localized && localized.term) || record.term;
const summary = (localized && localized.summary) || record.summary;
const source = (localized && localized.source) || record.source;
const sourceKind =
(localized && localized.sourceKind) || record.sourceKind;
const sourceMarkup = renderSourceLink(source);
return template
.replace(/\{context_label\}/g, contextLabel)
.replace(/\{context\}/g, context)
.replace(/\{term\}/g, term)
.replace(/\{category\}/g, record.category)
.replace(/\{summary\}/g, summary)
.replace(/\{source\}/g, sourceMarkup)
.replace(/\{source_kind\}/g, sourceKind);
}
function renderConceptPlain(language, record) {
const localized = localizedConceptFor(record, language);
const term = (localized && localized.term) || record.term;
const summary = (localized && localized.summary) || record.summary;
const source = (localized && localized.source) || record.source;
const sourceKind =
(localized && localized.sourceKind) || record.sourceKind;
const sourceMarkup = renderSourceLink(source);
return `${term} (${record.category}): ${summary}\n\nSource: ${sourceMarkup} (${sourceKind}).`;
}
function tryConceptLookup(prompt) {
const query = extractConceptQuery(prompt);
if (!query) return null;
const evidence = [`concept_lookup:request:${query.term}`];
if (query.context) {
evidence.push(`concept_lookup:context:${query.context}`);
}
const lookup = lookupConceptQuery(query);
if (!lookup) {
// Surface the miss in evidence so the demo's trace panel can show why
// the handler declined the prompt. Returning null lets later handlers
// (Wikipedia lookup, fallback) still get a chance.
return null;
}
const record = lookup.record;
const language = detectLanguage(prompt);
const localized = localizedConceptFor(record, language);
const effectiveSource = (localized && localized.source) || record.source;
// Issue #21: emit the percent-decoded IRI form for the trace panel.
const humanSource = humanizeUrl(effectiveSource);
evidence.push(`concept_lookup:hit:${record.slug}`);
evidence.push(`source:${humanSource}`);
if (record.wikidata) {
evidence.push(`wikidata:${record.wikidata}`);
}
if (lookup.contextMatch && lookup.context) {
evidence.push(`concept_lookup:context-match:${lookup.context}`);
const body = renderConceptInContext(language, lookup.context, record);
return {
intent: "concept_lookup_in_context",
content: body,
confidence: 0.9,
evidence,
};
}
if (lookup.context) {
evidence.push(`concept_lookup:context-mismatch:${lookup.context}`);
}
const body = renderConceptPlain(language, record);
return {
intent: "concept_lookup",
content: body,
confidence: 0.9,
evidence,
};
}
function extractDefinitionMergeTerm(prompt, allowPlainConcept) {
// The intent is two meanings together: a definition_merge_action ("merge",
// "combine", "fuse", …) applied to a definition_artifact_request
// ("definition", "translation", "wikipedia", …). Both are matched as raw
// substrings of the normalized prompt, so inflected forms in every supported
// language are caught with no per-word list in code. Mirrors
// extract_definition_merge_term in src/solver_handlers/definition_merge.rs.
const text = String(prompt || "");
const normalized = normalizePrompt(text);
const asksMerge = lexiconMentionsRoleSubstring(ROLE_DEFINITION_MERGE_ACTION, normalized);
const asksDefinition = lexiconMentionsRoleSubstring(
ROLE_DEFINITION_ARTIFACT_REQUEST,
normalized,
);
if (!asksMerge || !asksDefinition) {
if (allowPlainConcept) {
const query = extractConceptQuery(text);
if (query && !query.context) return query.term;
}
return null;
}
// The introducing phrases ("definitions of", "translation for", …) are
// definition_merge_marker prefix word forms; the literal before each slot
// marker is the phrase to locate. They are declared in the lexicon in the
// original priority order, so the first prefix that appears in the prompt
// wins and the text after it becomes the term.
const lower = text.toLowerCase();
for (const form of roleWordForms(ROLE_DEFINITION_MERGE_MARKER)) {
if (form.slot !== "prefix") continue;
const marker = form.before;
const index = lower.indexOf(marker);
if (index < 0) continue;
const candidate = trimDefinitionMergeTail(text.slice(index + marker.length));
if (candidate) return candidate.toLowerCase();
}
const query = extractConceptQuery(text);
return query ? query.term : null;
}
function trimDefinitionMergeTail(value) {
// The boundary words that end the term ("from", "using", "with", …) are
// definition_merge_tail_boundary meanings; we reconstruct each as a
// space-padded token and cut at the earliest one we find. Only the English
// surface forms are consulted here: this is an English-frame heuristic, and
// the term itself may be in any language (e.g. the Russian preposition "в" is
// part of the term "реклама в Telegram", not a boundary). The other languages
// remain in the seed so the meaning stays fully self-describing. The quote and
// punctuation trim sets are typographic and stay in code. Mirrors
// trim_definition_merge_tail in src/solver_handlers/definition_merge.rs.
const text = String(value || "");
const lower = text.toLowerCase();
let end = text.length;
for (const word of wordsForRoleInLanguages(ROLE_DEFINITION_MERGE_TAIL_BOUNDARY, ["en"])) {
const index = lower.indexOf(` ${word} `);
if (index >= 0) end = Math.min(end, index);
}
return text
.slice(0, end)
.trim()
.replace(/^['"`“”«»]+|['"`“”«»]+$/g, "")
.replace(/[?。.!,;:]+$/g, "")
.trim();
}
function inferredSourceLanguage(source) {
const value = String(source || "");
if (value.includes("://ru.wikipedia.org/")) return "ru";
if (value.includes("://hi.wikipedia.org/")) return "hi";
if (value.includes("://zh.wikipedia.org/")) return "zh";
return "en";
}
function normalizeDefinitionFact(value) {
return String(value || "")
.toLocaleLowerCase()
.replace(/[^\p{L}\p{N}]+/gu, "");
}
function pushDefinitionFragment(fragments, language, summary, source, sourceKind) {
const cleanSummary = String(summary || "").trim();
if (!cleanSummary) return;
const duplicate = fragments.some(
(fragment) =>
fragment.language === language &&
normalizeDefinitionFact(fragment.summary) === normalizeDefinitionFact(cleanSummary),
);
if (duplicate) return;
fragments.push({
language: String(language || "en"),
summary: cleanSummary,
source: String(source || "").trim(),
sourceKind: String(sourceKind || "").trim(),
});
}
function definitionFragments(record) {
const fragments = [];
pushDefinitionFragment(
fragments,
inferredSourceLanguage(record && record.source),
record && record.summary,
record && record.source,
record && record.sourceKind,
);
for (const localized of Array.isArray(record && record.localized) ? record.localized : []) {
pushDefinitionFragment(
fragments,
localized && localized.language,
localized && localized.summary,
localized && localized.source,
localized && localized.sourceKind,
);
}
return fragments;
}
function sourceLanguages(fragments) {
const languages = [];
for (const fragment of fragments) {
if (!languages.includes(fragment.language)) languages.push(fragment.language);
}
return languages;
}
function sourceUrls(fragments) {
const sources = [];
for (const fragment of fragments) {
if (!fragment.source || sources.includes(fragment.source)) continue;
sources.push(fragment.source);
}
return sources;
}
function splitDefinitionSentences(summary) {
const sentences = [];
let current = "";
for (const character of String(summary || "")) {
current += character;
if ([".", "!", "?", "।", "。"].includes(character)) {
const sentence = current.trim();
if (sentence) sentences.push(sentence);
current = "";
}
}
const tail = current.trim();
if (tail) sentences.push(tail);
return sentences;
}
function mergedDefinitionFacts(fragments) {
const facts = [];
const seen = new Set();
for (const fragment of fragments) {
for (const sentence of splitDefinitionSentences(fragment.summary)) {
const key = normalizeDefinitionFact(sentence);
if (!key || seen.has(key)) continue;
seen.add(key);
facts.push({ language: fragment.language, text: sentence });
}
}
return facts;
}
function uniqueSourceFragments(fragments) {
const unique = [];
const seen = new Set();
for (const fragment of fragments) {
if (!fragment.source) continue;
const key = `${fragment.language}\n${fragment.source}`;
if (seen.has(key)) continue;
seen.add(key);
unique.push(fragment);
}
return unique;
}
function renderDefinitionMerge(record, fragments, facts) {
const english = localizedConceptFor(record, "en");
const displayTerm = (english && english.term) || record.term;
const anchor = record.wikidata ? ` [${record.wikidata}]` : "";
const lines = [
`Merged definition of ${displayTerm}${anchor}`,
`Source languages: ${sourceLanguages(fragments).join(", ")}`,
"",
"Facts:",
];
for (const fact of facts) {
lines.push(`- [${fact.language}] ${fact.text}`);
}
lines.push("Sources:");
for (const fragment of uniqueSourceFragments(fragments)) {
lines.push(
`- [${fragment.language}] ${renderSourceLink(fragment.source)} (${fragment.sourceKind})`,
);
}
return lines.join("\n");
}
function tryDefinitionMerge(prompt, options) {
const opts = options || {};
const term = extractDefinitionMergeTerm(prompt, Boolean(opts.allowPlainConcept));
if (!term) return null;
const evidence = [`definition_merge:request:${term}`];
if (opts.allowPlainConcept) evidence.push("definition_merge:mode:auto");
const lookup = lookupConceptQuery({ term, context: null });
if (!lookup) return null;
const record = lookup.record;
const fragments = definitionFragments(record);
if (fragments.length === 0) return null;
evidence.push(`definition_merge:hit:${record.slug}`);
if (record.wikidata) evidence.push(`wikidata:${record.wikidata}`);
for (const language of sourceLanguages(fragments)) {
evidence.push(`definition_merge:language:${language}`);
}
for (const source of sourceUrls(fragments)) {
evidence.push(`source:${humanizeUrl(source)}`);
}
const facts = mergedDefinitionFacts(fragments);
evidence.push(`definition_merge:facts:${facts.length}`);
return {
intent: "definition_merge",
content: renderDefinitionMerge(record, fragments, facts),
confidence: 0.9,
evidence,
};
}
// Known person name corrections for typo suggestions. Each entry maps a
// canonical name to a list of common misspellings (all lowercase).
const KNOWN_PERSON_VARIANTS = [
{ canonical: "Elon Musk", variants: ["elon musk", "elon mask", "elon muск"] },
{ canonical: "Donald Trump", variants: ["donald trump", "donald tramp", "donald tromp"] },
{ canonical: "Joe Biden", variants: ["joe biden", "joe bidan", "joe bidon"] },
{ canonical: "Barack Obama", variants: ["barack obama", "barak obama", "barrack obama"] },
{ canonical: "Vladimir Putin", variants: ["vladimir putin", "vladimir puting", "vladmir putin"] },
{ canonical: "Albert Einstein", variants: ["albert einstein", "albert einstien", "albert enstien"] },
{ canonical: "Isaac Newton", variants: ["isaac newton", "isaak newton", "issac newton"] },
{ canonical: "Nikola Tesla", variants: ["nikola tesla", "nicolas tesla", "nikolai tesla"] },
];
function editDistance(a, b) {
const left = Array.from(String(a || ""));
const right = Array.from(String(b || ""));
const m = left.length, n = right.length;
const dp = Array.from({ length: m + 1 }, (_, i) =>
Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0))
);
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
dp[i][j] = left[i - 1] === right[j - 1]
? dp[i - 1][j - 1]
: 1 + Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]);
if (
i > 1 &&
j > 1 &&
left[i - 1] === right[j - 2] &&
left[i - 2] === right[j - 1]
) {
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + 1);
}
}
}
return dp[m][n];
}
function isCloseTokenTypo(actual, expected) {
const left = String(actual || "").toLowerCase();
const right = String(expected || "").toLowerCase();
const leftLength = Array.from(left).length;
const rightLength = Array.from(right).length;
return Math.min(leftLength, rightLength) >= 4 && editDistance(left, right) === 1;
}
function leadingTokenSpans(value, limit) {
const text = String(value || "");
const spans = [];
const pattern = /\S+/gu;
let match;
while ((match = pattern.exec(text)) !== null && spans.length < limit) {
spans.push({
start: match.index,
end: match.index + match[0].length,
text: match[0],
});
}
return spans;
}
function fuzzyPrefixMatch(value, prefix) {
const words = String(prefix || "").trim().split(/\s+/u).filter(Boolean);
if (words.length === 0) return null;
const spans = leadingTokenSpans(value, words.length);
if (spans.length !== words.length) return null;
let typoCount = 0;
for (let i = 0; i < words.length; i += 1) {
const actual = spans[i].text;
const expected = words[i];
if (actual.toLowerCase() === expected.toLowerCase()) continue;
if (!isCloseTokenTypo(actual, expected)) return null;
typoCount += 1;
}
if (typoCount !== 1) return null;
const end = spans[spans.length - 1].end;
return {
typoCount,
end,
interpretation: {
original: String(value || "").slice(0, end),
corrected: String(prefix || "").trim(),
},
};
}
function stripKnownPrefix(value, prefixes) {
const text = String(value || "");
const lower = text.toLowerCase();
for (const prefix of prefixes) {
if (lower.startsWith(prefix)) {
return { value: text.slice(prefix.length).trimStart(), interpretation: null };
}
}
const matches = prefixes
.map((prefix) => fuzzyPrefixMatch(text, prefix))
.filter(Boolean)
.sort((left, right) =>
left.typoCount - right.typoCount || right.end - left.end,
);
const best = matches[0];
if (!best) return null;
const next = matches[1];
if (next && next.typoCount === best.typoCount && next.end === best.end) {
return null;
}
return {
value: text.slice(best.end).trimStart(),
interpretation: best.interpretation,
};
}
function suggestNameCorrection(term) {
const lower = term.toLowerCase();
for (const { canonical, variants } of KNOWN_PERSON_VARIANTS) {
if (variants.includes(lower)) return canonical;
}
for (const { canonical, variants } of KNOWN_PERSON_VARIANTS) {
const canonicalLower = canonical.toLowerCase();
if (
variants.some((v) => editDistance(lower, v) === 1) ||
editDistance(lower, canonicalLower) === 1
) {
return canonical;
}
}
return null;
}
function isWhoIsPrompt(normalized) {
// "who is …" detection reasons over the who_question meaning: a language
// whose marker leads the name occupies the who_question_lead prefix slot
// (English who is …, Russian кто такой …), while one whose marker trails it
// occupies the who_question_tail suffix slot (Hindi … कौन है, Chinese …是谁).
return (
prefixLiterals(ROLE_WHO_QUESTION_LEAD).some((lead) => normalized.startsWith(lead)) ||
suffixLiterals(ROLE_WHO_QUESTION_TAIL).some((tail) => normalized.endsWith(tail))
);
}
function tryWhoIsQuestion(prompt) {
const normalized = prompt.toLowerCase().trim();
if (!isWhoIsPrompt(normalized)) return null;
const query = extractConceptQuery(prompt);
if (!query) return null;
const term = query.term;
const suggestion = suggestNameCorrection(term);
const content = suggestion
? `I don't have a Links Notation fact for "${term}" yet. Did you mean "${suggestion}"? Add a fact or rule in Links Notation and run the request again.`
: `I don't have a Links Notation fact for "${term}" yet. Add a fact or rule in Links Notation and run the request again.`;
return {
intent: "who_is_question",
content,
confidence: 0.5,
evidence: [`concept_lookup:miss:${term}`, "response:who_is_question"],
};
}
// Wikipedia REST summary endpoint per language. Browser-friendly: CORS is
// enabled by Wikimedia for these summary endpoints, so the worker can fetch
// without a proxy from GitHub Pages.
const WIKIPEDIA_HOSTS = {
en: "https://en.wikipedia.org/api/rest_v1/page/summary",
ru: "https://ru.wikipedia.org/api/rest_v1/page/summary",
hi: "https://hi.wikipedia.org/api/rest_v1/page/summary",
zh: "https://zh.wikipedia.org/api/rest_v1/page/summary",
};
// Wikipedia full-text page search endpoint per language (CORS-enabled). Returns
// ranked page results matching a free-text query — more effective than the
// title-only search for context-aware disambiguation because the ranker scores
// body content, not just the title.
const WIKIPEDIA_SEARCH_HOSTS = {
en: "https://en.wikipedia.org/w/rest.php/v1/search/page",
ru: "https://ru.wikipedia.org/w/rest.php/v1/search/page",
hi: "https://hi.wikipedia.org/w/rest.php/v1/search/page",
zh: "https://zh.wikipedia.org/w/rest.php/v1/search/page",
};
const WIKIPEDIA_ACTION_API_HOSTS = {
en: "https://en.wikipedia.org/w/api.php",
ru: "https://ru.wikipedia.org/w/api.php",
hi: "https://hi.wikipedia.org/w/api.php",
zh: "https://zh.wikipedia.org/w/api.php",
};
const WIKTIONARY_SEARCH_HOSTS = {
en: "https://en.wiktionary.org/w/api.php",
ru: "https://ru.wiktionary.org/w/api.php",
hi: "https://hi.wiktionary.org/w/api.php",
zh: "https://zh.wiktionary.org/w/api.php",
};
const WIKINEWS_SEARCH_HOSTS = {
en: "https://en.wikinews.org/w/api.php",
ru: "https://ru.wikinews.org/w/api.php",
zh: "https://zh.wikinews.org/w/api.php",
};
function wikipediaHostsFor(language) {
// Try the detected language first, then fall back to English so a Russian
// query for an English-only article still returns a definition.
const ordered = [language, "en"].filter(
(value, index, array) => value && array.indexOf(value) === index,
);
return ordered.map((lang) => ({
language: lang,
url: WIKIPEDIA_HOSTS[lang] || WIKIPEDIA_HOSTS.en,
}));
}
function capitalizeWords(term) {
return term
.split(/(\s+)/)
.map((part) =>
/\S/.test(part) ? part.charAt(0).toUpperCase() + part.slice(1) : part,
)
.join("");
}
function wikipediaTermVariants(term) {
const seen = new Set();
const variants = [];
const push = (value) => {
if (!value) return;
const slug = String(value)
.trim()
.replace(/\s+/g, "_")
.replace(/_+/g, "_");
if (!slug || seen.has(slug)) return;
seen.add(slug);
variants.push(slug);
};
const trimmed = String(term || "").trim();
push(trimmed);
push(capitalizeWords(trimmed));
push(capitalizeWords(trimmed.toLowerCase()));
push(trimmed.toLowerCase());
// Biography titles on Wikipedia (notably ru.wikipedia.org) use the
// "Surname, Given names" form: querying "Илон Маск" 404s, but "Маск, Илон"
// resolves. For two-word terms try the swap in both original and
// capitalized casing so other language hosts can match too.
const words = trimmed.split(/\s+/).filter(Boolean);
if (words.length === 2) {
const swapped = `${words[1]}, ${words[0]}`;
push(swapped);
push(capitalizeWords(swapped.toLowerCase()));
}
return variants;
}
function normalizeLookupText(value) {
return String(value || "")
.normalize("NFKD")
.toLowerCase()
.replace(/\p{M}/gu, "")
.replace(/[^\p{L}\p{N}]+/gu, " ")
.trim();
}
function compactLookupText(value) {
return normalizeLookupText(value).replace(/\s+/g, "");
}
function boundedEditDistance(left, right, limit) {
if (Math.abs(left.length - right.length) > limit) return limit + 1;
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
for (let i = 1; i <= left.length; i += 1) {
const current = [i];
let rowMin = current[0];
for (let j = 1; j <= right.length; j += 1) {
const cost = left[i - 1] === right[j - 1] ? 0 : 1;
const next = Math.min(
previous[j] + 1,
current[j - 1] + 1,
previous[j - 1] + cost,
);
current[j] = next;
rowMin = Math.min(rowMin, next);
}
if (rowMin > limit) return limit + 1;
previous = current;
}
return previous[right.length];
}
function isNearLookupText(left, right) {
const a = compactLookupText(left);
const b = compactLookupText(right);
if (!a || !b) return false;
const maxLength = Math.max(a.length, b.length);
const limit = maxLength <= 8 ? 1 : 2;
return boundedEditDistance(a, b, limit) <= limit;
}
function isPlausibleWikipediaSearchMatch(summary, term) {
if (
!summary ||
(summary.matchKind !== "search" && summary.matchKind !== "context_search")
) {
return true;
}
const termNormalized = normalizeLookupText(term);
if (!termNormalized) return true;
const termTokens = termNormalized.split(/\s+/).filter(Boolean);
const candidates = [
summary.title,
summary.matchedTitle,
String(summary.matchedSlug || "").replace(/_/g, " "),
summary.extract,
];
for (const candidate of candidates) {
const normalized = normalizeLookupText(candidate);
if (!normalized) continue;
if (normalized === termNormalized) return true;
const candidateTokens = new Set(normalized.split(/\s+/).filter(Boolean));
if (
termTokens.length > 0 &&
termTokens.every((token) => candidateTokens.has(token))
) {
return true;
}
if (isNearLookupText(termNormalized, normalized)) return true;
}
return false;
}
const LOOKUP_STEM_STOPWORDS = new Set([
"a",
"an",
"and",
"for",
"in",
"of",
"on",
"the",
"to",
"about",
"sentence",
"sentences",
"в",
"во",
"и",
"или",
"на",
"о",
"об",
"про",
]);
function hasSharedLookupStem(summary, term) {
const normalizedTerm = normalizeLookupText(term);
if (!normalizedTerm) return false;
const content = normalizeLookupText(
[
summary && summary.title,
summary && summary.matchedTitle,
summary && String(summary.matchedSlug || "").replace(/_/g, " "),
summary && summary.extract,
]
.filter(Boolean)
.join(" "),
);
if (!content) return false;
const contentTokens = content.split(/\s+/).filter(Boolean);
for (const token of normalizedTerm.split(/\s+/).filter(Boolean)) {
if (LOOKUP_STEM_STOPWORDS.has(token) || token.length < 7) continue;
const stemLength = Math.min(8, token.length - 2);
const stem = token.slice(0, stemLength);
if (stem.length >= 5 && contentTokens.some((candidate) => candidate.startsWith(stem))) {
return true;
}
}
return false;
}
function isArticleQuestionWikipediaMatch(summary, query) {
if (!summary) return false;
if (summary.matchKind === "direct") return true;
if (isPlausibleWikipediaSearchMatch(summary, query.exactTerm)) return true;
if (query.lookupTerm !== query.exactTerm && isPlausibleWikipediaSearchMatch(summary, query.lookupTerm)) {
return true;
}
if (!hasSharedLookupStem(summary, query.lookupTerm || query.exactTerm)) {
return false;
}
return !query.contextOriginal || hasSharedLookupStem(summary, query.contextOriginal);
}
// Resolve a context-qualified term to a Wikipedia page slug via full-text page
// search. Tries multiple query formulations (uppercase term, mixed case) on the
// detected language host then on English, returning the first match found.
// This helps disambiguate short acronyms like "KISS" or "DRY" when the user
// provides a programming/domain context.
async function searchWikipediaSlug(term, context, language) {
if (typeof fetch !== "function") return null;
const apiHeaders = {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
};
const upper = term.toUpperCase();
// Build candidate queries in preference order: uppercase term with context is
// most discriminating; plain term with context is the fallback.
const queries = [];
if (upper !== term) queries.push(`${upper} ${context}`.trim());
queries.push(`${term} ${context}`.trim());
const ordered = [language, "en"].filter(
(value, index, array) => value && array.indexOf(value) === index,
);
for (const lang of ordered) {
const base = WIKIPEDIA_SEARCH_HOSTS[lang] || WIKIPEDIA_SEARCH_HOSTS.en;
for (const query of queries) {
const url = `${base}?q=${encodeURIComponent(query)}&limit=5`;
try {
const response = await fetch(url, { headers: apiHeaders });
if (!response || !response.ok) continue;
const data = await response.json();
if (!data || !Array.isArray(data.pages) || data.pages.length === 0)
continue;
// Return the key of the top result; callers will fetch the full summary.
const page = data.pages[0];
return {
slug: page.key,
title: page.title || page.key,
language: lang,
query,
};
} catch (_error) {
// Ignore and try next query / language host.
}
}
}
return null;
}
function decodeHtmlEntities(value) {
const named = {
amp: "&",
apos: "'",
mdash: "—",
ndash: "–",
gt: ">",
lt: "<",
nbsp: " ",
quot: '"',
};
return String(value || "")
.replace(/&#x([0-9a-f]+);/giu, (_match, code) => {
const parsed = Number.parseInt(code, 16);
return Number.isFinite(parsed) ? String.fromCodePoint(parsed) : "";
})
.replace(/&#(\d+);/gu, (_match, code) => {
const parsed = Number.parseInt(code, 10);
return Number.isFinite(parsed) ? String.fromCodePoint(parsed) : "";
})
.replace(/&([a-z]+);/giu, (match, name) => named[name.toLowerCase()] || match);
}
function stripHtmlToText(html) {
return decodeHtmlEntities(
String(html || "")
.replace(/<style\b[\s\S]*?<\/style>/giu, " ")
.replace(/<script\b[\s\S]*?<\/script>/giu, " ")
.replace(/<sup\b[\s\S]*?<\/sup>/giu, " ")
.replace(/<[^>]+>/gu, " "),
)
.replace(/\s+([,.;:!?])/gu, "$1")
.replace(/\s+/gu, " ")
.trim();
}
function truncateDisambiguationHtml(html) {
const text = String(html || "");
let end = text.length;
for (const marker of [
/<h[1-6]\b[^>]*id=["'](?:См\._также|See_also|Примечания|References|Notes)["']/iu,
/<div\b[^>]*id=["']disambig["']/iu,
]) {
const match = marker.exec(text);
if (match && match.index > 0) end = Math.min(end, match.index);
}
return text.slice(0, end);
}
function deduplicateTextList(values) {
const out = [];
const seen = new Set();
for (const value of values) {
const text = String(value || "").trim();
if (!text) continue;
const key = normalizeLookupText(text);
if (!key || seen.has(key)) continue;
seen.add(key);
out.push(text);
}
return out;
}
function extractDisambiguationEntriesFromHtml(html) {
const scoped = truncateDisambiguationHtml(html);
const entries = [];
const itemPattern = /<li\b[^>]*>([\s\S]*?)<\/li>/giu;
let match;
while ((match = itemPattern.exec(scoped)) !== null) {
const text = stripHtmlToText(match[1]);
if (!text || text.startsWith("↑")) continue;
entries.push(text);
}
return deduplicateTextList(entries).slice(0, 12);
}
function extractDisambiguationEntriesFromSummary(summary) {
const title = normalizeLookupText(summary && summary.title);
const raw = String((summary && summary.extract) || "");
const extract = raw.replace(
/^([^:\n]{1,80}):\s*([«»"'“”„]?[^\n]{1,80}[»"'“”„]?\s[—–-]\s)/u,
"$1:\n$2",
);
const lines = extract
.split(/\n+/u)
.map((line) => line.trim())
.filter(Boolean)
.filter((line) => {
const normalized = normalizeLookupText(line.replace(/:$/u, ""));
return normalized && normalized !== title;
});
return deduplicateTextList(lines);
}
function definitionPrefixForDisambiguationEntry(entry) {
const text = String(entry || "").trim();
const dash = text.search(/\s[—–-]\s/u);
if (dash <= 0) return "";
return normalizeLookupText(
text
.slice(0, dash)
.trim()
.replace(/^[«»"'“”„]+|[«»"'“”„]+$/gu, ""),
);
}
function isDefinitionStyleDisambiguation(summary, requestedTerm, entries) {
const targets = [requestedTerm, summary && summary.title]
.map((value) => normalizeLookupText(value))
.filter(Boolean);
if (targets.length === 0) return false;
return entries.some((entry) => {
const prefix = definitionPrefixForDisambiguationEntry(entry);
return prefix && targets.includes(prefix);
});
}
async function fetchWikipediaDisambiguationEntries(summary) {
if (typeof fetch !== "function" || !summary) return [];
const base =
WIKIPEDIA_ACTION_API_HOSTS[summary.language] || WIKIPEDIA_ACTION_API_HOSTS.en;
const page = summary.matchedSlug || summary.title;
if (!page) return [];
const url = `${base}?action=parse&page=${encodeURIComponent(
page,
)}&prop=text&format=json&formatversion=2&redirects=1&origin=*`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
},
});
if (!response || !response.ok) return [];
const data = await response.json();
const text = data && data.parse ? data.parse.text : "";
let html = "";
if (typeof text === "string") {
html = text;
} else if (text && typeof text === "object" && text["*"]) {
html = text["*"];
}
return extractDisambiguationEntriesFromHtml(html);
} catch (_error) {
return [];
}
}
async function buildDefinitionStyleDisambiguationSummary(
data,
term,
language,
matchedSlug,
requestUrl,
) {
const title = String(data.title || term);
const pageUrl =
(data.content_urls &&
data.content_urls.desktop &&
data.content_urls.desktop.page) ||
requestUrl;
const summary = {
title,
extract: String(data.extract || "").trim(),
url: pageUrl,
language,
matchKind: "disambiguation",
matchedSlug,
};
const summaryEntries = extractDisambiguationEntriesFromSummary(summary);
if (!isDefinitionStyleDisambiguation(summary, term, summaryEntries)) {
return null;
}
const parsedEntries = await fetchWikipediaDisambiguationEntries(summary);
const entries = parsedEntries.length > 0 ? parsedEntries : summaryEntries;
return {
...summary,
extract: entries.join("\n"),
disambiguationEntries: entries,
};
}
async function fetchWikipediaSummary(term, language, context, options) {
if (typeof fetch !== "function") return null;
const includeDefinitionDisambiguation = Boolean(
options && options.includeDefinitionDisambiguation,
);
const apiHeaders = {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
};
// When context is provided, first try a title-search to find the most
// relevant article slug (e.g. "Kiss" + "рамках програмирования" → "KISS
// principle"). This prevents ambiguous short terms from matching the wrong
// article (e.g. the rock band instead of the software design principle).
if (context) {
const found = await searchWikipediaSlug(term, context, language);
if (found) {
const summaryBase =
WIKIPEDIA_HOSTS[found.language] || WIKIPEDIA_HOSTS.en;
const url = `${summaryBase}/${encodeURIComponent(found.slug)}`;
try {
const response = await fetch(url, { headers: apiHeaders });
if (response && response.ok) {
const data = await response.json();
if (
data &&
typeof data === "object" &&
data.type !== "disambiguation"
) {
const extract = String(data.extract || "").trim();
if (extract) {
const title = String(data.title || term);
const pageUrl =
(data.content_urls &&
data.content_urls.desktop &&
data.content_urls.desktop.page) ||
url;
return {
title,
extract,
url: pageUrl,
language: found.language,
matchKind: "context_search",
matchedSlug: found.slug,
matchedTitle: found.title || title,
searchQuery: found.query || "",
};
}
}
}
} catch (_error) {
// Fall through to bare-term lookup below.
}
}
}
// Bare-term fallback: try direct slug variants without context.
const hosts = wikipediaHostsFor(language);
const variants = wikipediaTermVariants(term);
for (const host of hosts) {
for (const slug of variants) {
const url = `${host.url}/${encodeURIComponent(slug)}`;
try {
const response = await fetch(url, { headers: apiHeaders });
if (!response || !response.ok) continue;
const data = await response.json();
if (!data || typeof data !== "object") continue;
if (data.type === "disambiguation") {
if (includeDefinitionDisambiguation && !context) {
const disambiguation = await buildDefinitionStyleDisambiguationSummary(
data,
term,
host.language,
slug,
url,
);
if (disambiguation) return disambiguation;
}
continue;
}
const extract = String(data.extract || "").trim();
if (!extract) continue;
const title = String(data.title || term);
const pageUrl =
(data.content_urls &&
data.content_urls.desktop &&
data.content_urls.desktop.page) ||
url;
return {
title,
extract,
url: pageUrl,
language: host.language,
matchKind: "direct",
matchedSlug: slug,
};
} catch (_error) {
// Swallow network/parse errors and continue to the next variant.
}
}
}
// All direct slug variants were disambiguation pages or not found. Use the
// full-text search endpoint to find the top-ranked article for the term
// (e.g. "tesla" → "Tesla, Inc." instead of the disambiguation page).
const found = await searchWikipediaSlug(term, "", language);
if (found) {
const summaryBase = WIKIPEDIA_HOSTS[found.language] || WIKIPEDIA_HOSTS.en;
const url = `${summaryBase}/${encodeURIComponent(found.slug)}`;
try {
const response = await fetch(url, { headers: apiHeaders });
if (response && response.ok) {
const data = await response.json();
if (
data &&
typeof data === "object" &&
data.type !== "disambiguation"
) {
const extract = String(data.extract || "").trim();
if (extract) {
const title = String(data.title || term);
const pageUrl =
(data.content_urls &&
data.content_urls.desktop &&
data.content_urls.desktop.page) ||
url;
return {
title,
extract,
url: pageUrl,
language: found.language,
matchKind: "search",
matchedSlug: found.slug,
matchedTitle: found.title || title,
searchQuery: found.query || "",
};
}
}
}
} catch (_error) {
// Search-based fallback failed; return null below.
}
}
return null;
}
function isClosestWikipediaMatch(summary) {
return summary && summary.matchKind === "search";
}
function closestMatchNote(summary, language) {
const title = summary && summary.title ? summary.title : "the top result";
if (language === "ru") {
return `Ближайшее совпадение по поиску Wikipedia: «${title}». Если это не то, уточните запрос.`;
}
if (language === "zh") {
return `Wikipedia 搜索的最接近匹配是“${title}”。如果这不是你的意思,请进一步说明。`;
}
if (language === "hi") {
return `Wikipedia खोज में सबसे नज़दीकी मिलान "${title}" है। अगर आपका मतलब यह नहीं था, तो कृपया स्पष्ट करें।`;
}
return `Closest match from Wikipedia search: "${title}". If that is not what you meant, clarify the prompt.`;
}
function wikipediaClarificationMessage(summary, language) {
const title = summary && summary.title ? summary.title : "the top result";
if (language === "ru") {
return `Похоже, вы имели в виду «${title}». Уточните, отвечать по этой статье Wikipedia?`;
}
if (language === "zh") {
return `你是指“${title}”吗?请确认后我再根据这篇 Wikipedia 文章回答。`;
}
if (language === "hi") {
return `क्या आपका मतलब "${title}" था? Wikipedia के इस लेख से उत्तर देने से पहले कृपया स्पष्ट करें।`;
}
return `Did you mean "${title}"? Please clarify before I answer from that Wikipedia article.`;
}
function wikipediaDisambiguationMessage(summary, language) {
const humanUrl = humanizeUrl(summary.url);
const entries = Array.isArray(summary.disambiguationEntries)
? summary.disambiguationEntries
: String(summary.extract || "")
.split(/\n+/u)
.map((line) => line.trim())
.filter(Boolean);
const list = entries.map((entry) => `- ${entry}`).join("\n");
if (language === "ru") {
return `На странице Wikipedia «${summary.title}» перечислены значения:\n\n${list}\n\nИсточник: [${humanUrl}](${summary.url}) (wikipedia).`;
}
if (language === "zh") {
return `Wikipedia “${summary.title}”页面列出以下含义:\n\n${list}\n\n来源:[${humanUrl}](${summary.url}) (wikipedia).`;
}
if (language === "hi") {
return `Wikipedia पृष्ठ "${summary.title}" ये अर्थ सूचीबद्ध करता है:\n\n${list}\n\nस्रोत: [${humanUrl}](${summary.url}) (wikipedia).`;
}
return `Wikipedia's "${summary.title}" page lists these meanings:\n\n${list}\n\nSource: [${humanUrl}](${summary.url}) (wikipedia).`;
}
function wikipediaArticleQuestionMessage(summary, query, language, exactMatch) {
const humanUrl = humanizeUrl(summary.url);
const source = `Source: [${humanUrl}](${summary.url}) (wikipedia).`;
if (language === "ru") {
const wikipediaName =
summary.language === "ru" ? "русскоязычной Википедии" : "Wikipedia";
if (exactMatch) {
return `В Wikipedia есть статья «${summary.title}»: ${summary.extract}\n\nИсточник: [${humanUrl}](${summary.url}) (wikipedia).`;
}
return [
`В ${wikipediaName} я не нашёл отдельной статьи с названием «${query.exactTerm}», но ближайшая подходящая страница — «${summary.title}»: ${summary.extract}`,
`Источник: [${humanUrl}](${summary.url}) (wikipedia).`,
].join("\n\n");
}
if (language === "zh") {
const zhSource = `来源:[${humanUrl}](${summary.url}) (wikipedia).`;
if (exactMatch) {
return `Wikipedia 有一篇“${summary.title}”条目:${summary.extract}\n\n${zhSource}`;
}
return `我没有找到标题为“${query.exactTerm}”的 Wikipedia 条目,但最接近的有用页面是“${summary.title}”:${summary.extract}\n\n${zhSource}`;
}
if (language === "hi") {
const hiSource = `स्रोत: [${humanUrl}](${summary.url}) (wikipedia).`;
if (exactMatch) {
return `Wikipedia पर "${summary.title}" लेख है: ${summary.extract}\n\n${hiSource}`;
}
return `मुझे Wikipedia पर "${query.exactTerm}" शीर्षक वाला अलग लेख नहीं मिला, लेकिन सबसे नज़दीकी उपयोगी पृष्ठ "${summary.title}" है: ${summary.extract}\n\n${hiSource}`;
}
if (exactMatch) {
return `Wikipedia has an article titled "${summary.title}": ${summary.extract}\n\n${source}`;
}
return `I did not find an exact Wikipedia article titled "${query.exactTerm}", but the closest useful page is "${summary.title}": ${summary.extract}\n\n${source}`;
}
// ---------------------------------------------------------------------------
// Wikidata-backed fact reasoning pipeline (issue #127).
//
// Rather than matching against hardcoded summaries in `data/seed/facts.lino`,
// fact questions ("what is the capital of X?", "столица X", "X की राजधानी",
// "X的首都") are parsed into a structured query
// `{ relation, subjectTerm, language, forceFresh }`. The query is then
// resolved against:
//
// 1. An in-memory cache (1-week TTL) keyed by `relation:subject:language`.
// The cache is pre-warmed from the seed `FACTS` entries so the test
// matrix stays deterministic offline.
// 2. Wikidata `wbsearchentities` to resolve the subject term to a Q-ID.
// 3. Wikidata `wbgetentities` to fetch the property claim (P36 = capital,
// P1082 = population, P38 = currency, P37 = official language, P30 =
// continent, P2046 = area, P35 = head of state, P6 = head of government).
// 4. Wikidata `wbgetentities` again to resolve the target Q-ID to a label
// in the user's prevailing language (and to a Wikipedia sitelink).
//
// Every step is recorded as a `fact_query:*` event so the reasoning trace
// shows the structured query, the cache decision, the Wikidata round-trips,
// and the final resolved answer. A user can force a fresh resolution by
// adding markers like "fresh", "no cache", "не из кэша", "без кеша",
// "ताज़ा", or "刷新" to the prompt.
// ---------------------------------------------------------------------------
const WIKIDATA_API = "https://www.wikidata.org/w/api.php";
const FACT_RELATIONS = [
{
relation: "capital",
property: "P36",
valueType: "entity",
},
{
relation: "population",
property: "P1082",
valueType: "quantity",
},
{
relation: "currency",
property: "P38",
valueType: "entity",
},
{
relation: "official_language",
property: "P37",
valueType: "entity",
},
{
relation: "continent",
property: "P30",
valueType: "entity",
},
{
relation: "area",
property: "P2046",
valueType: "quantity",
},
{
relation: "head_of_state",
property: "P35",
valueType: "entity",
},
{
relation: "head_of_government",
property: "P6",
valueType: "entity",
},
];
function relationConfig(relation) {
return FACT_RELATIONS.find((entry) => entry.relation === relation) || null;
}
// Markers that flag the user wants a fresh (uncached) result. Detected in all
// four supported languages plus a couple of common English phrasings.
const FORCE_FRESH_MARKERS = [
"fresh",
"no cache",
"no-cache",
"without cache",
"skip cache",
"ignore cache",
"refresh",
"не из кэша",
"не из кеша",
"без кэша",
"без кеша",
"обнови",
"свежий ответ",
"свежие данные",
"ताज़ा",
"ताज़े",
"बिना कैश",
"नया जवाब",
"刷新",
"新鲜",
"不要缓存",
"不用缓存",
];
function shouldForceFresh(normalized, prompt) {
const lowerPrompt = String(prompt || "").toLowerCase();
return FORCE_FRESH_MARKERS.some(
(marker) => normalized.includes(marker) || lowerPrompt.includes(marker),
);
}
// Multilingual relation patterns. Each entry has a list of triggers that, when
// present in the normalized prompt, identify the relation. Subject extraction
// uses the `extract` regexes which capture the subject term verbatim from the
// original (un-normalized) prompt — that preserves Cyrillic/Devanagari/CJK
// scripts that the normalizer otherwise strips.
const FACT_QUESTION_PATTERNS = [
{
relation: "capital",
// English
extract: [
/\bcapital\s+(?:city\s+)?of\s+(?:the\s+)?([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/\b([^?.!,;:]+?)['’]s\s+capital\b/i,
/\bwhich\s+city\s+is\s+(?:the\s+)?capital\s+of\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/\bwhich\s+city\s+is\s+([^?.!,;:]+?)['’]s\s+capital\b/i,
// Russian: "столица России", "какова столица России",
// "столицей какой страны является Москва" — only the first form is
// resolved; the inverted form falls through to other handlers.
/столица\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/какова\s+столица\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/какая\s+столица\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
// Hindi: "X की राजधानी क्या है"
/([^?.!,;:]+?)\s+की\s+राजधानी(?:\s+क्या\s+है)?(?:[?.!,;:]|$)/i,
// Chinese: "X的首都" / "X的首都是什么"
/([^?。.!!,,;:、]+?)的首都(?:是什么|是哪里|是哪个城市)?(?:[?。.!!,,;:、]|$)/i,
],
},
{
relation: "population",
extract: [
/\bpopulation\s+of\s+(?:the\s+)?([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/\bhow\s+many\s+people\s+(?:live|are\s+there)\s+in\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/\b([^?.!,;:]+?)['’]s\s+population\b/i,
/население\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/какое\s+население\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/([^?.!,;:]+?)\s+की\s+(?:जनसंख्या|आबादी)(?:[?.!,;:]|$)/i,
/([^?。.!!,,;:、]+?)的人口(?:是多少|有多少)?(?:[?。.!!,,;:、]|$)/i,
],
},
{
relation: "currency",
extract: [
/\bcurrency\s+of\s+(?:the\s+)?([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/\b([^?.!,;:]+?)['’]s\s+currency\b/i,
/валюта\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/какая\s+валюта\s+в\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/([^?.!,;:]+?)\s+की\s+मुद्रा(?:[?.!,;:]|$)/i,
/([^?。.!!,,;:、]+?)的(?:货币|貨幣)(?:是什么|是哪种)?(?:[?。.!!,,;:、]|$)/i,
],
},
{
relation: "official_language",
extract: [
/\bofficial\s+language\s+of\s+(?:the\s+)?([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/\bwhat\s+language\s+(?:do\s+they\s+speak|is\s+spoken)\s+in\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/государственный\s+язык\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/официальный\s+язык\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/([^?.!,;:]+?)\s+की\s+(?:राजभाषा|आधिकारिक\s+भाषा)(?:[?.!,;:]|$)/i,
/([^?。.!!,,;:、]+?)的(?:官方语言|官方語言)(?:[?。.!!,,;:、]|$)/i,
],
},
{
relation: "continent",
extract: [
/\bcontinent\s+(?:is\s+)?([^?.!,;:]+?)\s+(?:on|in)\b/i,
/\bwhich\s+continent\s+is\s+([^?.!,;:]+?)\s+(?:on|in)\b/i,
/на\s+каком\s+континенте\s+(?:находится|расположена|расположен)\s+([^?.!,;:]+?)(?:[?.!,;:]|$)/i,
/([^?.!,;:]+?)\s+किस\s+महाद्वीप\s+में\s+है(?:[?.!,;:]|$)/i,
/([^?。.!!,,;:、]+?)在哪个(?:大洲|洲)(?:[?。.!!,,;:、]|$)/i,
],
},
];
// Words/phrases that should be stripped from a captured subject before we
// hand it off to Wikidata. These are not part of the entity name — they leak
// from question prefixes the regex didn't consume (e.g. "the country called
// France" → "France"). Order matters: longer prefixes first.
const SUBJECT_TRIM_PREFIXES = [
"the country called ",
"the country ",
"country ",
"the city of ",
"the city ",
"city of ",
"country called ",
"republic of ",
"kingdom of ",
"is ",
"in ",
"of the ",
"of ",
"страна ",
"страны ",
"стране ",
"страну ",
];
function trimSubjectTerm(raw) {
let value = String(raw || "")
.replace(/[«»"'`“”„‟‹›]+/g, "")
.replace(/\s+/g, " ")
.trim();
let changed = true;
while (changed) {
changed = false;
const lower = value.toLowerCase();
for (const prefix of SUBJECT_TRIM_PREFIXES) {
if (lower.startsWith(prefix)) {
value = value.slice(prefix.length).trim();
changed = true;
break;
}
}
}
return value;
}
function parseFactQuestion(prompt, normalized) {
const text = String(prompt || "");
if (!text.trim()) return null;
for (const pattern of FACT_QUESTION_PATTERNS) {
for (const regex of pattern.extract) {
const match = regex.exec(text);
if (!match) continue;
const subjectTerm = trimSubjectTerm(match[1]);
if (!subjectTerm) continue;
// Reject single-letter or pure-punctuation captures so we don't fire
// on noise like "x." or "?".
if (subjectTerm.length < 2 && !/[Ѐ-鿿]/.test(subjectTerm)) {
continue;
}
return {
relation: pattern.relation,
subjectTerm,
language: detectLanguage(prompt),
forceFresh: shouldForceFresh(normalized, prompt),
};
}
}
return null;
}
// In-memory cache. Keyed by `relation:subject_normalized:language`. The TTL
// matches the user-requested 1 week. Pre-warmed from FACTS at init() so the
// offline test matrix sees the same starting cache the Rust solver does.
const FACT_QUERY_CACHE = new Map();
const FACT_QUERY_TTL_MS = 7 * 24 * 60 * 60 * 1000;
function factCacheKey(relation, subjectTerm, language) {
return [
String(relation || "").toLowerCase(),
String(subjectTerm || "")
.toLowerCase()
.replace(/\s+/g, " ")
.trim(),
String(language || "en").toLowerCase(),
].join(":");
}
function factCacheGet(relation, subjectTerm, language) {
const key = factCacheKey(relation, subjectTerm, language);
const entry = FACT_QUERY_CACHE.get(key);
if (!entry) return null;
if (
entry.expiresAt &&
typeof entry.expiresAt === "number" &&
entry.expiresAt < Date.now()
) {
FACT_QUERY_CACHE.delete(key);
return null;
}
return entry;
}
function factCachePut(relation, subjectTerm, language, value) {
const key = factCacheKey(relation, subjectTerm, language);
const ttl = typeof value.ttlMs === "number" ? value.ttlMs : FACT_QUERY_TTL_MS;
const entry = Object.assign({}, value, {
expiresAt: Date.now() + ttl,
});
FACT_QUERY_CACHE.set(key, entry);
return entry;
}
// Pre-warm the runtime cache from the seed `facts.lino`. Each seed record can
// optionally declare `relation`, `subjectQid`, `valueQid`, plus per-language
// `subjectLabel`/`valueLabel`/`valueText` overrides — those are the structured
// cache anchors. The legacy fields (`summary`, `subjectAliases`,
// `questionKeywords`) remain in place for the `tryFactLookup` substring path.
function warmFactCacheFromSeed() {
if (!Array.isArray(FACTS)) return;
const languages = ["en", "ru", "hi", "zh"];
for (const record of FACTS) {
if (!record || !record.relation || !record.subjectAliases) continue;
const localizedMap = new Map();
if (Array.isArray(record.localized)) {
for (const loc of record.localized) {
if (loc && loc.language) localizedMap.set(loc.language, loc);
}
}
for (const lang of languages) {
const loc = localizedMap.get(lang) || localizedMap.get("en") || {};
const summary =
(loc && loc.summary) || record.summary || "";
const source = (loc && loc.source) || record.source || "";
const sourceKind =
(loc && loc.sourceKind) || record.sourceKind || "wikipedia";
const valueLabel = (loc && loc.valueLabel) || record.valueLabel || "";
const subjectLabel =
(loc && loc.subjectLabel) || record.subjectLabel || "";
// The aliases for the subject language drive cache key lookup. For each
// alias (already lowercased by seed_loader.js), pre-seed a cache entry.
const aliases = Array.isArray(record.subjectAliases)
? record.subjectAliases
: [];
for (const alias of aliases) {
if (!alias) continue;
factCachePut(record.relation, alias, lang, {
relation: record.relation,
subjectTerm: alias,
subjectLabel: subjectLabel || alias,
subjectQid: record.subjectQid || "",
valueLabel,
valueQid: record.valueQid || "",
summary,
source,
sourceKind,
language: lang,
fromSeed: true,
ttlMs: FACT_QUERY_TTL_MS,
});
}
}
}
}
async function wikidataSearchEntity(term, language) {
if (typeof fetch !== "function") return null;
// Wikidata supports per-language search; English fallback ensures broad
// recall even for non-Latin scripts.
const ordered = [language, "en"].filter(
(value, index, array) => value && array.indexOf(value) === index,
);
for (const lang of ordered) {
const url = `${WIKIDATA_API}?action=wbsearchentities&format=json&origin=*&type=item&limit=5&language=${encodeURIComponent(
lang,
)}&search=${encodeURIComponent(term)}`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
},
});
if (!response || !response.ok) continue;
const data = await response.json();
if (data && Array.isArray(data.search) && data.search.length > 0) {
const hit = data.search[0];
return {
qid: hit.id,
label: hit.label || term,
description: hit.description || "",
language: lang,
};
}
} catch (_error) {
// Try the next language.
}
}
return null;
}
function wikidataConceptUrl(hit) {
const id = hit && hit.id ? String(hit.id) : "";
if (id) return `https://www.wikidata.org/wiki/${encodeURIComponent(id)}`;
const conceptUri = hit && hit.concepturi ? String(hit.concepturi) : "";
const qid = conceptUri.match(/Q\d+/);
if (qid) return `https://www.wikidata.org/wiki/${qid[0]}`;
return "https://www.wikidata.org/wiki/Wikidata:Main_Page";
}
function wikidataHitMatchesTerm(hit, term) {
const target = normalizeLookupText(term);
if (!target || !hit) return false;
const candidates = [
hit.label,
hit.title,
hit.match && hit.match.text,
hit.display && hit.display.label && hit.display.label.value,
];
if (Array.isArray(hit.aliases)) {
candidates.push(...hit.aliases);
}
return candidates.some((candidate) => normalizeLookupText(candidate) === target);
}
async function fetchWikidataConceptSummary(term, language) {
if (typeof fetch !== "function") return null;
const ordered = [language, "en"].filter(
(value, index, array) => value && array.indexOf(value) === index,
);
for (const lang of ordered) {
const url = `${WIKIDATA_API}?action=wbsearchentities&format=json&origin=*&type=item&limit=5&language=${encodeURIComponent(
lang,
)}&search=${encodeURIComponent(term)}`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
},
});
if (!response || !response.ok) continue;
const data = await response.json();
const hits = data && Array.isArray(data.search) ? data.search : [];
const hit = hits.find((candidate) =>
wikidataHitMatchesTerm(candidate, term),
);
if (!hit) continue;
const display = hit.display || {};
return {
sourceKind: "wikidata",
qid: hit.id || "",
title:
(display.label && display.label.value) ||
hit.label ||
(hit.match && hit.match.text) ||
term,
description:
(display.description && display.description.value) ||
hit.description ||
"",
url: wikidataConceptUrl(hit),
language: lang,
};
} catch (_error) {
// Try the next Wikidata language.
}
}
return null;
}
function wiktionaryFallbackDescription(title, language) {
if (language === "ru") {
return `В Wiktionary есть словарная статья «${title}».`;
}
if (language === "zh") {
return `Wiktionary 有“${title}”这个词条。`;
}
if (language === "hi") {
return `Wiktionary में "${title}" के लिए शब्दकोश प्रविष्टि है।`;
}
return `Wiktionary has a dictionary entry for "${title}".`;
}
async function fetchWiktionaryEntry(term, language) {
if (typeof fetch !== "function") return null;
const ordered = [language, "en"].filter(
(value, index, array) => value && array.indexOf(value) === index,
);
const target = normalizeLookupText(term);
for (const lang of ordered) {
const base = WIKTIONARY_SEARCH_HOSTS[lang] || WIKTIONARY_SEARCH_HOSTS.en;
const url = `${base}?action=opensearch&search=${encodeURIComponent(
term,
)}&limit=5&format=json&origin=*`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
},
});
if (!response || !response.ok) continue;
const data = await response.json();
if (!Array.isArray(data) || !Array.isArray(data[1])) continue;
const titles = data[1];
const descriptions = Array.isArray(data[2]) ? data[2] : [];
const urls = Array.isArray(data[3]) ? data[3] : [];
const index = titles.findIndex(
(title) => normalizeLookupText(title) === target,
);
if (index < 0) continue;
const title = titles[index] || term;
return {
sourceKind: "wiktionary",
title,
description:
descriptions[index] || wiktionaryFallbackDescription(title, lang),
url:
urls[index] ||
`https://${lang}.wiktionary.org/wiki/${encodeURIComponent(title)}`,
language: lang,
};
} catch (_error) {
// Try the next Wiktionary language.
}
}
return null;
}
function renderExternalLookupContent(result, requestedTerm) {
const humanUrl = humanizeUrl(result.url);
const title = result.title || requestedTerm;
const heading =
requestedTerm && normalizeLookupText(requestedTerm) !== normalizeLookupText(title)
? `${requestedTerm}: ${title}`
: title;
const description = String(result.description || "").trim();
const body = description ? `${heading}: ${description}` : `${heading}.`;
return `${body}\n\nSource: [${humanUrl}](${result.url}) (${result.sourceKind}).`;
}
function externalLookupResponse(result, requestedTerm, rejectedSummary) {
const humanUrl = humanizeUrl(result.url);
const evidence = [
`${result.sourceKind}_lookup:${result.qid || result.title}`,
`source:${humanUrl}`,
`language:${result.language}`,
];
if (result.qid) evidence.push(`wikidata:${result.qid}`);
if (rejectedSummary && rejectedSummary.title) {
evidence.push(`wikipedia_lookup:rejected:${rejectedSummary.title}`);
}
return {
intent: `${result.sourceKind}_lookup`,
content: renderExternalLookupContent(result, requestedTerm),
confidence: result.sourceKind === "wikidata" ? 0.82 : 0.75,
evidence,
};
}
async function tryTermKnowledgeFallback(term, language, rejectedSummary) {
const wikidata = await fetchWikidataConceptSummary(term, language);
if (wikidata) {
return externalLookupResponse(wikidata, term, rejectedSummary);
}
const wiktionary = await fetchWiktionaryEntry(term, language);
if (wiktionary) {
return externalLookupResponse(wiktionary, term, rejectedSummary);
}
return null;
}
async function wikidataFetchEntityClaim(qid, property, language) {
if (typeof fetch !== "function") return null;
const url = `${WIKIDATA_API}?action=wbgetentities&format=json&origin=*&ids=${encodeURIComponent(
qid,
)}&props=claims%7Clabels%7Csitelinks&languages=${encodeURIComponent(
language,
)}%7Cen`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
},
});
if (!response || !response.ok) return null;
const data = await response.json();
if (!data || !data.entities) return null;
const entity = data.entities[qid];
if (!entity) return null;
const claims = (entity.claims || {})[property] || [];
const subjectLabel =
(entity.labels && (entity.labels[language] || entity.labels.en) || {})
.value || "";
const sitelinks = entity.sitelinks || {};
return { claims, subjectLabel, sitelinks };
} catch (_error) {
return null;
}
}
async function wikidataResolveLabel(qid, language) {
if (typeof fetch !== "function") return null;
const url = `${WIKIDATA_API}?action=wbgetentities&format=json&origin=*&ids=${encodeURIComponent(
qid,
)}&props=labels%7Csitelinks&languages=${encodeURIComponent(language)}%7Cen`;
try {
const response = await fetch(url, {
headers: {
accept: "application/json",
"api-user-agent":
"formal-ai-demo (https://github.com/link-assistant/formal-ai)",
},
});
if (!response || !response.ok) return null;
const data = await response.json();
if (!data || !data.entities) return null;
const entity = data.entities[qid];
if (!entity) return null;
const label =
(entity.labels && (entity.labels[language] || entity.labels.en) || {})
.value || "";
const sitelinks = entity.sitelinks || {};
return { label, sitelinks };
} catch (_error) {
return null;
}
}
function wikipediaSitelinkUrl(sitelinks, language) {
if (!sitelinks || typeof sitelinks !== "object") return "";
const key = `${language}wiki`;
const fallback = "enwiki";
const entry = sitelinks[key] || sitelinks[fallback];
if (!entry) return "";
if (entry.url) return entry.url;
if (entry.title) {
const lang = sitelinks[key] ? language : "en";
return `https://${lang}.wikipedia.org/wiki/${encodeURIComponent(
String(entry.title).replace(/\s+/g, "_"),
).replace(/%2F/gi, "/")}`;
}
return "";
}
// Localized templates for rendering the final answer. The seed value is
// inserted via `{value}`; the subject is inserted via `{subject}`.
const FACT_RESPONSE_TEMPLATES = {
capital: {
en: "The capital of {subject} is {value}.",
ru: "Столица {subject} — {value}.",
hi: "{subject} की राजधानी {value} है।",
zh: "{subject}的首都是{value}。",
},
population: {
en: "The population of {subject} is approximately {value}.",
ru: "Население {subject} составляет примерно {value}.",
hi: "{subject} की जनसंख्या लगभग {value} है।",
zh: "{subject}的人口约为 {value}。",
},
currency: {
en: "The currency of {subject} is the {value}.",
ru: "Валюта {subject} — {value}.",
hi: "{subject} की मुद्रा {value} है।",
zh: "{subject}的货币是{value}。",
},
official_language: {
en: "The official language of {subject} is {value}.",
ru: "Государственный язык {subject} — {value}.",
hi: "{subject} की राजभाषा {value} है।",
zh: "{subject}的官方语言是{value}。",
},
continent: {
en: "{subject} is located on the continent of {value}.",
ru: "{subject} расположена на континенте {value}.",
hi: "{subject} {value} महाद्वीप पर स्थित है।",
zh: "{subject}位于{value}。",
},
area: {
en: "The area of {subject} is approximately {value}.",
ru: "Площадь {subject} составляет примерно {value}.",
hi: "{subject} का क्षेत्रफल लगभग {value} है।",
zh: "{subject}的面积约为 {value}。",
},
head_of_state: {
en: "The head of state of {subject} is {value}.",
ru: "Глава государства {subject} — {value}.",
hi: "{subject} के राष्ट्राध्यक्ष {value} हैं।",
zh: "{subject}的国家元首是{value}。",
},
head_of_government: {
en: "The head of government of {subject} is {value}.",
ru: "Глава правительства {subject} — {value}.",
hi: "{subject} के सरकार के प्रमुख {value} हैं।",
zh: "{subject}的政府首脑是{value}。",
},
};
function renderFactSummary(relation, subjectLabel, valueLabel, language) {
const templates =
FACT_RESPONSE_TEMPLATES[relation] || FACT_RESPONSE_TEMPLATES.capital;
const template = templates[language] || templates.en;
return template
.replace("{subject}", subjectLabel || "")
.replace("{value}", valueLabel || "");
}
function factQueryEvidence(record, language) {
const evidence = [
`fact_query:relation:${record.relation}`,
`fact_query:subject:${record.subjectLabel || record.subjectTerm}`,
`language:${language}`,
];
if (record.subjectQid) evidence.push(`wikidata:${record.subjectQid}`);
if (record.valueQid) evidence.push(`wikidata:${record.valueQid}`);
if (record.source) evidence.push(`source:${humanizeUrl(record.source)}`);
if (record.fromSeed) evidence.push("fact_query:cache:seed");
else if (record.fromCache) evidence.push("fact_query:cache:hit");
else evidence.push("fact_query:cache:miss");
return evidence;
}
async function resolveFactQueryViaWikidata(query, log) {
// Stage 1: subject resolution via wbsearchentities.
if (log) log.push(`fact_query:wbsearchentities:request:${query.subjectTerm}`);
const subject = await wikidataSearchEntity(query.subjectTerm, query.language);
if (!subject) {
if (log) log.push("fact_query:wbsearchentities:miss");
return null;
}
if (log) log.push(`fact_query:wbsearchentities:resolved:${subject.qid}`);
const config = relationConfig(query.relation);
if (!config) return null;
// Stage 2: claim fetch via wbgetentities.
if (log) log.push(`fact_query:wbgetentities:request:${config.property}`);
const claimData = await wikidataFetchEntityClaim(
subject.qid,
config.property,
query.language,
);
if (!claimData || !claimData.claims || claimData.claims.length === 0) {
if (log) log.push("fact_query:wbgetentities:no_claim");
return null;
}
const claim = claimData.claims[0];
const mainsnak = claim && claim.mainsnak;
if (!mainsnak || !mainsnak.datavalue) {
if (log) log.push("fact_query:wbgetentities:no_datavalue");
return null;
}
// Stage 3: value resolution.
let valueLabel = "";
let valueQid = "";
if (config.valueType === "entity") {
const value = mainsnak.datavalue.value || {};
valueQid = value.id || "";
if (!valueQid) {
if (log) log.push("fact_query:wbgetentities:value_not_entity");
return null;
}
if (log) log.push(`fact_query:label_resolve:request:${valueQid}`);
const labelResult = await wikidataResolveLabel(valueQid, query.language);
if (!labelResult || !labelResult.label) {
if (log) log.push("fact_query:label_resolve:miss");
return null;
}
valueLabel = labelResult.label;
if (log) log.push(`fact_query:label_resolve:${valueLabel}`);
// Capture the Wikipedia sitelink for the value entity as the canonical
// evidence source — that's the human-readable artifact users can verify.
const url =
wikipediaSitelinkUrl(labelResult.sitelinks, query.language) ||
wikipediaSitelinkUrl(claimData.sitelinks, query.language);
return {
relation: query.relation,
subjectTerm: query.subjectTerm,
subjectLabel: claimData.subjectLabel || subject.label,
subjectQid: subject.qid,
valueLabel,
valueQid,
summary: renderFactSummary(
query.relation,
claimData.subjectLabel || subject.label,
valueLabel,
query.language,
),
source: url,
sourceKind: "wikidata",
language: query.language,
fromCache: false,
fromSeed: false,
};
}
// Quantity values (population, area) are not Q-IDs.
const value = mainsnak.datavalue.value || {};
const rawAmount = String(value.amount || "").replace(/^\+/, "");
if (!rawAmount) {
if (log) log.push("fact_query:wbgetentities:value_empty");
return null;
}
valueLabel = rawAmount;
if (log) log.push(`fact_query:quantity:${valueLabel}`);
const url = wikipediaSitelinkUrl(claimData.sitelinks, query.language);
return {
relation: query.relation,
subjectTerm: query.subjectTerm,
subjectLabel: claimData.subjectLabel || subject.label,
subjectQid: subject.qid,
valueLabel,
valueQid: "",
summary: renderFactSummary(
query.relation,
claimData.subjectLabel || subject.label,
valueLabel,
query.language,
),
source: url,
sourceKind: "wikidata",
language: query.language,
fromCache: false,
fromSeed: false,
};
}
async function tryFactQuery(prompt, normalized, preferences) {
const query = parseFactQuestion(prompt, normalized);
if (!query) return null;
// Trace events: every step of the reasoning pipeline is recorded so the
// browser memory log shows the structured query, the cache decision, and
// any Wikidata calls.
const trace = [];
trace.push(`fact_query:request:${prompt}`);
trace.push(`fact_query:relation:${query.relation}`);
trace.push(`fact_query:subject:${query.subjectTerm}`);
trace.push(`fact_query:language:${query.language}`);
if (query.forceFresh) trace.push("fact_query:force_fresh");
// Stage 1: cache check (skipped when the user asked for fresh data).
if (!query.forceFresh) {
trace.push("fact_query:cache:check");
const cached = factCacheGet(
query.relation,
query.subjectTerm,
query.language,
);
if (cached) {
trace.push(`fact_query:cache:hit:${cached.fromSeed ? "seed" : "runtime"}`);
const evidence = factQueryEvidence(
Object.assign({}, cached, { fromCache: true }),
query.language,
);
return {
intent: "fact_query",
content: cached.summary,
confidence: 0.92,
evidence,
trace,
formalizedObject: cached.subjectQid || "",
};
}
trace.push("fact_query:cache:miss");
} else {
trace.push("fact_query:cache:bypass");
}
// Stage 2: Wikidata resolution.
const resolved = await resolveFactQueryViaWikidata(query, trace);
if (!resolved) {
trace.push("fact_query:wikidata:no_match");
return null;
}
// Stage 3: cache the resolution.
factCachePut(query.relation, query.subjectTerm, query.language, resolved);
trace.push(`fact_query:cache:store:${factCacheKey(
query.relation,
query.subjectTerm,
query.language,
)}`);
trace.push(`fact_query:response:${resolved.summary}`);
return {
intent: "fact_query",
content: resolved.summary,
confidence: 0.92,
evidence: factQueryEvidence(resolved, query.language),
trace,
formalizedObject: resolved.subjectQid || "",
};
}
async function tryWikipediaLookup(prompt, language, preferences) {
const query = extractConceptQuery(prompt);
if (!query) return null;
// Avoid hitting the network for terms that already resolved in CONCEPTS;
// that path is handled by `tryConceptLookup`. We try the full
// `(term, context)` query first so that "what is iir in ml" doesn't waste
// a network call when a context-aware record exists.
if (lookupConceptQuery(query)) return null;
// Pass the original-case term to Wikipedia: non-Latin scripts (e.g. Cyrillic
// for "Илон Маск") require correct capitalization in the REST URL because
// ru.wikipedia.org does not redirect the all-lowercase slug.
const wikiTerm = query.termOriginal || query.term;
const wikiContext = query.contextOriginal || query.context;
const summary = await fetchWikipediaSummary(wikiTerm, language, wikiContext, {
includeDefinitionDisambiguation: !wikiContext,
});
if (!summary) {
return tryTermKnowledgeFallback(wikiTerm, language, null);
}
const isClosestMatch = isClosestWikipediaMatch(summary);
const requiresPlausibleSearchMatch =
isClosestMatch || summary.matchKind === "context_search";
if (
requiresPlausibleSearchMatch &&
!isPlausibleWikipediaSearchMatch(summary, wikiTerm)
) {
const fallback = await tryTermKnowledgeFallback(wikiTerm, language, summary);
if (fallback) return fallback;
return null;
}
const guessProbability = numericPreference(
preferences && preferences.guessProbability,
0.8,
0,
1,
);
const humanUrl = humanizeUrl(summary.url);
const evidence = [
`wikipedia_lookup:${summary.title}`,
`source:${humanUrl}`,
`language:${summary.language}`,
];
if (wikiContext) evidence.push(`wikipedia_lookup:context:${wikiContext}`);
if (isClosestMatch) {
evidence.push(`wikipedia_lookup:closest_match:${summary.title}`);
}
if (summary.matchKind === "disambiguation") {
const entryCount = Array.isArray(summary.disambiguationEntries)
? summary.disambiguationEntries.length
: 0;
evidence.push(`wikipedia_lookup:disambiguation:${summary.title}`);
evidence.push(`wikipedia_lookup:disambiguation_entries:${entryCount}`);
return {
intent: "wikipedia_lookup",
content: wikipediaDisambiguationMessage(summary, language),
confidence: 0.84,
evidence,
};
}
if (isClosestMatch && guessProbability < 0.5) {
evidence.push("ambiguity:ask");
return {
intent: "clarification",
content: wikipediaClarificationMessage(summary, language),
confidence: 0.65,
evidence,
};
}
const bodyLines = [
`${summary.title}: ${summary.extract}\n\n` +
`Source: [${humanUrl}](${summary.url}) (wikipedia).`,
];
if (isClosestMatch) {
bodyLines.push(closestMatchNote(summary, language));
evidence.push("ambiguity:guess");
}
return {
intent: "wikipedia_lookup",
content: bodyLines.join("\n\n"),
confidence: 0.85,
evidence,
};
}
async function tryWikipediaArticleQuestion(prompt, language, preferences) {
const term = extractWikipediaArticleQuestionTerm(prompt);
if (!term) return null;
const query = refineWikipediaArticleQuestionLookup(term, language);
if (!query.exactTerm) return null;
const exactSummary = await fetchWikipediaSummary(query.exactTerm, language, null);
let summary = exactSummary;
const exactMatch = exactSummary && exactSummary.matchKind === "direct";
if (!exactMatch && (query.lookupTerm !== query.exactTerm || query.contextOriginal)) {
const refinedSummary = await fetchWikipediaSummary(
query.lookupTerm,
language,
query.contextOriginal,
);
if (refinedSummary) summary = refinedSummary;
}
if (!summary) {
return tryTermKnowledgeFallback(query.exactTerm, language, null);
}
if (!exactMatch && !isArticleQuestionWikipediaMatch(summary, query)) {
const fallback = await tryTermKnowledgeFallback(
query.exactTerm,
language,
summary,
);
if (fallback) return fallback;
return null;
}
const guessProbability = numericPreference(
preferences && preferences.guessProbability,
0.8,
0,
1,
);
const humanUrl = humanizeUrl(summary.url);
const evidence = [
`wikipedia_article_question:${query.exactTerm}`,
`source:${humanUrl}`,
`language:${summary.language}`,
];
if (query.lookupTerm !== query.exactTerm) {
evidence.push(`wikipedia_article_question:lookup:${query.lookupTerm}`);
}
if (query.contextOriginal) {
evidence.push(`wikipedia_article_question:context:${query.contextOriginal}`);
}
if (exactMatch) {
evidence.push("wikipedia_article_question:exact");
} else {
evidence.push(`wikipedia_article_question:closest_match:${summary.title}`);
}
if (!exactMatch && guessProbability < 0.5) {
evidence.push("ambiguity:ask");
return {
intent: "wikipedia_article_question",
content: wikipediaClarificationMessage(summary, language),
confidence: 0.65,
evidence,
};
}
if (!exactMatch) evidence.push("ambiguity:guess");
return {
intent: "wikipedia_article_question",
content: wikipediaArticleQuestionMessage(summary, query, language, exactMatch),
confidence: exactMatch ? 0.88 : 0.82,
evidence,
query: query.exactTerm,
formalizedObject: summary.title,
};
}
// Issue #386 software-authoring roles — mirror ROLE_SOFTWARE_AUTHORING_ACTION
// and ROLE_SOFTWARE_ARTIFACT_KIND in src/seed/meanings.rs. The surface words (in
// every supported language) live in data/seed/meanings.lino and
// data/seed/meanings-software-project.lino (embedded in MEANINGS_LINO below);
// this module names no word in any single language.
const ROLE_SOFTWARE_AUTHORING_ACTION = "software_authoring_action";
const ROLE_SOFTWARE_ARTIFACT_KIND = "software_artifact_kind";
const ROLE_SOFTWARE_REQUIREMENT_CATEGORY = "software_requirement_category";
// Issue #386 software-delivery / language / game-tracker / approval roles —
// mirror the matching ROLE_* consts in src/seed/meanings.rs. Their surface
// words (every supported language) live in
// data/seed/meanings-software-project.lino (embedded in MEANINGS_LINO below);
// this module names no word in any single language.
const ROLE_SOFTWARE_FEATURE = "software_feature";
const ROLE_SOFTWARE_DELIVERY_MODE = "software_delivery_mode";
const ROLE_SOFTWARE_IMPLEMENTATION_LANGUAGE = "software_implementation_language";
const ROLE_GAME_TRACKER_DOMAIN = "game_tracker_domain";
const ROLE_GAME_TRACKER_MECHANIC = "game_tracker_mechanic";
const ROLE_SOFTWARE_STEP_GRANULARITY = "software_step_granularity";
const ROLE_SOFTWARE_BASH_COMMAND = "software_bash_command";
const ROLE_SOFTWARE_APPROVAL_TRIGGER = "software_approval_trigger";
// Map a software-requirement-category meaning slug to its canonical category
// label. Mirrors requirement_category_label in
// src/solver_handlers/software_project.rs: recognition words live in the
// lexicon; the canonical category label lives here. A slug absent here is
// skipped rather than mislabelled.
const SOFTWARE_REQUIREMENT_CATEGORY_LABELS = {
requirement_state_tracking: "state_tracking",
requirement_data_exchange: "data_exchange",
requirement_automation: "automation",
requirement_validation: "validation",
requirement_integration: "integration",
requirement_user_interface: "user_interface",
requirement_project_behavior: "project_behavior",
};
// Map a software_delivery_mode meaning slug to its canonical delivery-mode
// label. Mirrors DeliveryMode::from_slug in
// src/solver_handlers/software_project.rs: the lexicon owns the surface words;
// this resolver owns the slug→label mapping. The default (code_generation) has
// no slug — it is the fallback when no mode meaning is evidenced.
const SOFTWARE_DELIVERY_MODE_LABELS = {
delivery_manual_instructions: "manual_instructions",
delivery_immediate_execution: "immediate_execution",
delivery_script_generation: "script_generation",
};
// Map a software_implementation_language meaning slug to its canonical target
// label. Mirrors implementation_language_from_slug in
// src/solver_handlers/software_project.rs. The default (typescript) has no slug.
const SOFTWARE_IMPLEMENTATION_LANGUAGE_LABELS = {
language_python: "python",
language_rust: "rust",
language_javascript: "javascript",
};
// Map a software-artifact-kind meaning slug to its canonical English label.
// Mirrors artifact_label in src/solver_handlers/software_project.rs: the lexicon
// owns the surface words a prompt is matched against (every language); this
// resolver owns only the stable slug→label mapping. A slug absent here is
// skipped rather than mislabelled.
const SOFTWARE_ARTIFACT_LABELS = {
artifact_browser_extension: "browser extension",
artifact_command_line_tool: "command-line tool",
artifact_github_action: "action",
artifact_mobile_app: "mobile app",
artifact_web_app: "web app",
artifact_application: "application",
artifact_extension: "extension",
artifact_dashboard: "dashboard",
artifact_scraper: "scraper",
artifact_library: "library",
artifact_website: "website",
artifact_plugin: "plugin",
artifact_service: "service",
artifact_bot: "bot",
artifact_app: "app",
artifact_api: "API",
artifact_sdk: "SDK",
artifact_tool: "tool",
artifact_mod: "mod",
};
// [surface, label] recognition table, sourced from the lexicon in declaration
// order. Mirrors artifact_surface_table in
// src/solver_handlers/software_project.rs.
function softwareArtifactTable() {
const table = [];
for (const meaning of meaningsWithRole(ROLE_SOFTWARE_ARTIFACT_KIND)) {
const label = SOFTWARE_ARTIFACT_LABELS[meaning.slug];
if (!label) continue;
for (const surface of meaning.words) table.push([surface, label]);
}
return table;
}
// [surface, action-slug] recognition table for software-authoring verbs. The
// matched slug is stored verbatim as the request's `action`, so the verb is
// recognised in every language it is lexicalised in. Mirrors
// action_surface_table in src/solver_handlers/software_project.rs.
function softwareActionTable() {
const table = [];
for (const meaning of meaningsWithRole(ROLE_SOFTWARE_AUTHORING_ACTION)) {
for (const surface of meaning.words) table.push([surface, meaning.slug]);
}
return table;
}
// Whether `character` is a "word character" for the recognition scan:
// alphanumeric but not CJK. Mirrors is_word_character in
// src/solver_handlers/software_project.rs — CJK scripts have no inter-word
// spaces so they match as substrings, while Latin/Cyrillic/Devanagari keep
// whole-token boundaries so a short surface like `апи` never matches inside
// `напиши`.
function isSoftwareWordCharacter(character) {
return /[\p{Alphabetic}\p{Number}]/u.test(character) && !containsCjk(character);
}
function isSoftwareStartBoundary(value, index) {
if (index === 0) return true;
return !isSoftwareWordCharacter(value[index - 1]);
}
function isSoftwareEndBoundary(value, index) {
if (index >= value.length) return true;
return !isSoftwareWordCharacter(value[index]);
}
// Position-major scan: the surface appearing earliest in `normalized` wins,
// ties at one position broken by table order (prefix collisions like app vs
// application are resolved by the end-boundary check, not order). Returns the
// matched { surface, payload } or null. Mirrors scan_match in
// src/solver_handlers/software_project.rs.
function scanSoftwareSurface(normalized, table) {
const text = String(normalized || "");
for (let index = 0; index < text.length; index += 1) {
if (!isSoftwareStartBoundary(text, index)) continue;
for (const [surface, payload] of table) {
if (surface && text.startsWith(surface, index)) {
if (isSoftwareEndBoundary(text, index + surface.length)) {
return { surface, payload };
}
}
}
}
return null;
}
// Lowercased union of every surface word (all languages) of the meanings that
// carry ROLE_SOFTWARE_REQUIREMENT_CATEGORY. A clause containing any of them
// states a feature requirement. Mirrors requirement_marker_words in
// src/solver_handlers/software_project.rs — no hardcoded marker list; the
// vocabulary lives in data/seed/meanings-software-project.lino.
function requirementMarkerWords() {
return wordsForRole(ROLE_SOFTWARE_REQUIREMENT_CATEGORY)
.filter((word) => word.length > 0)
.map((word) => word.toLowerCase());
}
const GAME_TRACKER_TYPESCRIPT = `type Cooldown = {
name: string;
remainingRounds: number;
};
type UnitState = {
id: string;
name: string;
hp: number;
maxHp: number;
protection: number;
resistance: number;
cooldowns: Cooldown[];
};
type DamageResult = {
damageTaken: number;
prevented: number;
unit: UnitState;
};
export function mitigateDamage(unit: UnitState, rawDamage: number): DamageResult {
const prevented = Math.max(0, unit.protection) + Math.max(0, unit.resistance);
const damageTaken = Math.max(0, rawDamage - prevented);
return {
damageTaken,
prevented,
unit: { ...unit, hp: Math.max(0, unit.hp - damageTaken) },
};
}
export function setStacks(
unit: UnitState,
protection: number,
resistance: number,
): UnitState {
return {
...unit,
protection: Math.max(0, protection),
resistance: Math.max(0, resistance),
};
}
export function tickCooldowns(unit: UnitState): UnitState {
return {
...unit,
cooldowns: unit.cooldowns
.map((cooldown) => ({
...cooldown,
remainingRounds: Math.max(0, cooldown.remainingRounds - 1),
}))
.filter((cooldown) => cooldown.remainingRounds > 0),
};
}`;
const GENERIC_PROJECT_TYPESCRIPT = `type ProjectRecord = {
id: string;
title: string;
status: "open" | "done";
notes: string[];
};
type ProjectCommand =
| { type: "add"; id: string; title: string }
| { type: "note"; id: string; note: string }
| { type: "complete"; id: string };
export function applyCommand(
records: ProjectRecord[],
command: ProjectCommand,
): ProjectRecord[] {
switch (command.type) {
case "add":
return [
...records,
{ id: command.id, title: command.title, status: "open", notes: [] },
];
case "note":
return records.map((record) =>
record.id === command.id
? { ...record, notes: [...record.notes, command.note] }
: record,
);
case "complete":
return records.map((record) =>
record.id === command.id ? { ...record, status: "done" } : record,
);
}
}`;
const GENERIC_PROJECT_JAVASCRIPT = `export function applyCommand(records, command) {
switch (command.type) {
case "add":
return [...records, { id: command.id, title: command.title, status: "open", notes: [] }];
case "note":
return records.map((record) =>
record.id === command.id
? { ...record, notes: [...record.notes, command.note] }
: record,
);
case "complete":
return records.map((record) =>
record.id === command.id ? { ...record, status: "done" } : record,
);
default:
throw new Error("Unknown command: " + command.type);
}
}`;
const GENERIC_PROJECT_PYTHON = `from dataclasses import dataclass, field
@dataclass(frozen=True)
class ProjectRecord:
id: str
title: str
status: str = "open"
notes: tuple[str, ...] = field(default_factory=tuple)
def apply_command(records: tuple[ProjectRecord, ...], command: dict) -> tuple[ProjectRecord, ...]:
if command["type"] == "add":
return (*records, ProjectRecord(id=command["id"], title=command["title"]))
if command["type"] == "note":
return tuple(
ProjectRecord(r.id, r.title, r.status, (*r.notes, command["note"]))
if r.id == command["id"] else r
for r in records
)
if command["type"] == "complete":
return tuple(
ProjectRecord(r.id, r.title, "done", r.notes)
if r.id == command["id"] else r
for r in records
)
raise ValueError(f"Unknown command: {command['type']}")
`;
const GENERIC_PROJECT_RUST = `#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProjectRecord {
pub id: String,
pub title: String,
pub status: ProjectStatus,
pub notes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProjectStatus {
Open,
Done,
}
pub enum ProjectCommand {
Add { id: String, title: String },
Note { id: String, note: String },
Complete { id: String },
}
pub fn apply_command(mut records: Vec<ProjectRecord>, command: ProjectCommand) -> Vec<ProjectRecord> {
match command {
ProjectCommand::Add { id, title } => records.push(ProjectRecord {
id,
title,
status: ProjectStatus::Open,
notes: Vec::new(),
}),
ProjectCommand::Note { id, note } => {
for record in &mut records {
if record.id == id {
record.notes.push(note.clone());
}
}
}
ProjectCommand::Complete { id } => {
for record in &mut records {
if record.id == id {
record.status = ProjectStatus::Done;
}
}
}
}
records
}`;
const PLAYWRIGHT_DOCS_URL = "https://playwright.dev/docs/writing-tests";
const PLAYWRIGHT_STARTER_TYPESCRIPT = `import { test, expect } from '@playwright/test';
test('opens the Playwright docs', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveTitle(/Playwright/);
await page.getByRole('link', { name: 'Docs' }).click();
await expect(page.getByRole('heading', { name: /Playwright/ })).toBeVisible();
});`;
function containsAnySubstring(value, needles) {
return needles.some((needle) => value.includes(needle));
}
function containsToken(normalized, token) {
return String(normalized || "").split(/\s+/).includes(token);
}
// Issue #386 Playwright roles — mirror ROLE_PLAYWRIGHT_TOOL_NAME and
// ROLE_PLAYWRIGHT_SCRIPT_CUE in src/seed/roles.rs. The tool name (with its
// 'playright' misspelling, whose `action` names the canonical spelling) and the
// script-authoring cues live in data/seed/meanings-playwright.lino (embedded in
// MEANINGS_LINO above). isPlaywrightScriptRequest matches both roles as raw
// substrings across every language, exactly like the Rust recogniser.
const ROLE_PLAYWRIGHT_TOOL_NAME = "playwright_tool_name";
const ROLE_PLAYWRIGHT_SCRIPT_CUE = "playwright_script_cue";
function isPlaywrightScriptRequest(normalized) {
return (
lexiconMentionsRoleSubstring(ROLE_PLAYWRIGHT_TOOL_NAME, normalized) &&
lexiconMentionsRoleSubstring(ROLE_PLAYWRIGHT_SCRIPT_CUE, normalized)
);
}
// True when the prompt contains a misspelled form of the Playwright name — any
// playwright_tool_name form whose `action` names the canonical spelling. The
// misspelling and its correction live in the seed data, so the handler reports
// the fix without naming either form here. Mirrors
// mentions_playwright_misspelling in src/solver_handlers/playwright_script.rs.
function mentionsPlaywrightMisspelling(normalized) {
return roleWordForms(ROLE_PLAYWRIGHT_TOOL_NAME)
.filter((form) => form.action)
.some((form) => normalized.includes(form.text));
}
function renderPlaywrightClarification(language) {
if (language === "ru") {
return [
"Я могу написать Playwright-скрипт. Уточните URL страницы, действия и ожидаемую проверку.",
"Если нужен пример по умолчанию, я могу взять стартовый сценарий из документации Playwright.",
].join(" ");
}
return [
"I can write a Playwright script. Please provide the page URL, the actions to perform, and the expected assertion.",
"If you want a default example, I can use the starter scenario from the Playwright docs.",
].join(" ");
}
function renderPlaywrightStarter(language, correctedSpelling) {
const lines = [];
if (language === "ru" && correctedSpelling) {
lines.push(
"Я трактую `Playright` как `Playwright` и даю стартовый TypeScript-пример по документации Playwright.",
);
} else if (language === "ru") {
lines.push("Даю стартовый TypeScript-пример по документации Playwright.");
} else if (correctedSpelling) {
lines.push(
"I interpret `Playright` as `Playwright` and will use a starter TypeScript example based on the Playwright docs.",
);
} else {
lines.push(
"I will use a starter TypeScript example based on the Playwright docs.",
);
}
lines.push("");
lines.push(`Source: ${PLAYWRIGHT_DOCS_URL}`);
lines.push("");
lines.push("```typescript");
lines.push(PLAYWRIGHT_STARTER_TYPESCRIPT);
lines.push("```");
lines.push("");
if (language === "ru") {
lines.push("Проверка:");
lines.push("1. `npm init playwright@latest`");
lines.push("2. `npx playwright test`");
lines.push("");
lines.push("Уточните URL, действия и ожидаемый результат, если нужен сценарий под конкретный сайт.");
} else {
lines.push("Check it with:");
lines.push("1. `npm init playwright@latest`");
lines.push("2. `npx playwright test`");
lines.push("");
lines.push("Provide the URL, actions, and expected result if you want a site-specific script.");
}
return lines.join("\n");
}
function tryPlaywrightScript(prompt, preferences = {}, language = "en") {
const normalized = normalizePrompt(prompt);
if (!isPlaywrightScriptRequest(normalized)) return null;
const guessProbability = numericPreference(
preferences && preferences.guessProbability,
0.8,
0,
1,
);
const evidence = [
"script_framework:playwright",
`source:${PLAYWRIGHT_DOCS_URL}`,
`guess_probability:${guessProbability.toFixed(2)}`,
];
const correctedSpelling = mentionsPlaywrightMisspelling(normalized);
if (correctedSpelling) {
evidence.push("spelling_correction:Playright->Playwright");
}
if (guessProbability < 0.5) {
return {
intent: "playwright_script_clarification",
content: renderPlaywrightClarification(language),
confidence: 0.64,
evidence,
};
}
return {
intent: "playwright_script",
content: renderPlaywrightStarter(language, correctedSpelling),
confidence: 0.82,
evidence,
};
}
function detectSoftwareAction(normalized) {
const match = scanSoftwareSurface(normalized, softwareActionTable());
return match ? match.payload : null;
}
function detectSoftwareArtifact(normalized) {
const match = scanSoftwareSurface(normalized, softwareArtifactTable());
return match ? { surface: match.surface, label: match.payload } : null;
}
function extractSoftwareTarget(prompt, artifact) {
const markers = [
`${artifact.surface} for `,
`${artifact.surface} to `,
`${artifact.label} for `,
`${artifact.label} to `,
" for ",
" to ",
];
for (const marker of markers) {
const target = extractAfterMarker(prompt, marker);
if (target) return target;
}
return "the requested environment";
}
function extractAfterMarker(prompt, marker) {
const source = String(prompt || "");
const lower = source.toLowerCase();
const lowerMarker = marker.toLowerCase();
const start = lower.indexOf(lowerMarker);
if (start < 0) return null;
const tail = source.slice(start + lowerMarker.length);
const stopMatch = /[?.,;\n]/.exec(tail);
const stop = stopMatch ? stopMatch.index : tail.length;
const raw = tail
.slice(0, stop)
.split(" with ")[0]
.split(" that ")[0]
.split(" and ")[0]
.trim();
if (!raw) return null;
return capitalizeShortTarget(raw);
}
function capitalizeShortTarget(raw) {
const compact = String(raw || "").trim().split(/\s+/).slice(0, 5).join(" ");
if (!compact) return compact;
if (/[A-ZА-Я]/.test(compact)) return compact;
return compact.charAt(0).toUpperCase() + compact.slice(1);
}
function sentenceCase(raw) {
const trimmed = String(raw || "").trim().replace(/^[-* ]+|[-* ]+$/g, "");
if (!trimmed) return "";
return trimmed.charAt(0).toUpperCase() + trimmed.slice(1);
}
function extractSoftwareFeatures(prompt) {
const markers = requirementMarkerWords();
const features = [];
const segments = String(prompt || "").split(/[.;\n]/);
for (const segment of segments) {
for (const clause of segment.split(",")) {
const cleaned = clause.trim();
if (!cleaned) continue;
const lower = cleaned.toLowerCase();
if (!containsAnySubstring(lower, markers)) continue;
const feature = sentenceCase(cleaned);
if (feature && !features.includes(feature)) features.push(feature);
}
}
if (features.length === 0) {
features.push("Capture state, user commands, persistence, validation, and tests.");
}
return features;
}
// A request is a game-unit tracker only when it pairs a game domain with a
// combat mechanic — both the game_tracker_domain and game_tracker_mechanic
// roles must be evidenced. Mirrors is_game_unit_tracker in
// src/solver_handlers/software_project.rs; the decomposition lives in the
// lexicon, so the code knows only "a tracker needs both a domain and a mechanic".
function isGameUnitTracker(normalized) {
return (
lexiconMentionsRole(ROLE_GAME_TRACKER_DOMAIN, normalized) &&
lexiconMentionsRole(ROLE_GAME_TRACKER_MECHANIC, normalized)
);
}
function classifySoftwareRequirement(requirement, gameTracker) {
const lower = String(requirement || "").toLowerCase();
// A game unit tracker is state by construction, regardless of wording.
if (gameTracker) {
return "state_tracking";
}
// Walk the requirement-category meanings in declaration order; the first
// whose surface word appears classifies the clause. Mirrors
// classify_requirement in src/solver_handlers/software_project.rs. The
// catch-all project_behavior comes last, so it acts as the default.
for (const meaning of meaningsWithRole(ROLE_SOFTWARE_REQUIREMENT_CATEGORY)) {
const label = SOFTWARE_REQUIREMENT_CATEGORY_LABELS[meaning.slug];
if (!label) continue;
if (meaning.words.some((word) => lower.includes(word.toLowerCase()))) {
return label;
}
}
return "project_behavior";
}
function softwareSubtaskTitle(category, requirement) {
switch (category) {
case "state_tracking":
return `Model state fields and pure transitions for ${requirement}`;
case "data_exchange":
return `Define parsers, serializers, and backup flow for ${requirement}`;
case "automation":
return `Schedule deterministic jobs and delivery checks for ${requirement}`;
case "validation":
return `Encode validation rules and failure messages for ${requirement}`;
case "integration":
return `Isolate host API boundaries and mocks for ${requirement}`;
case "user_interface":
return `Design focused views and state updates for ${requirement}`;
default:
return `Implement and test the smallest behavior for ${requirement}`;
}
}
function deriveSoftwareSubtasks(requirements, gameTracker) {
return requirements.map((requirement, index) => {
const category = classifySoftwareRequirement(requirement, gameTracker);
return {
requirementId: `R${index + 1}`,
category,
title: softwareSubtaskTitle(category, requirement),
};
});
}
// Pick the delivery mode by walking the software_delivery_mode meanings in
// declaration order (manual instructions → immediate execution → script
// generation — the order encodes priority) and taking the first one evidenced
// in the request; the default is generated code. Mirrors detect_delivery_mode
// in src/solver_handlers/software_project.rs — the surface words live in the
// lexicon, so the code knows only "a request can ask for a delivery mode".
function detectSoftwareDeliveryMode(normalized) {
const meaning = firstRoleMatch(ROLE_SOFTWARE_DELIVERY_MODE, normalized);
return (meaning && SOFTWARE_DELIVERY_MODE_LABELS[meaning.slug]) || "code_generation";
}
// Resolve the target language by walking the software_implementation_language
// meanings in declaration order (python → rust → javascript) and taking the
// first one named in the request; the default is TypeScript. Mirrors
// detect_implementation_language in src/solver_handlers/software_project.rs.
function detectSoftwareImplementationLanguage(normalized) {
const meaning = firstRoleMatch(ROLE_SOFTWARE_IMPLEMENTATION_LANGUAGE, normalized);
return (meaning && SOFTWARE_IMPLEMENTATION_LANGUAGE_LABELS[meaning.slug]) || "typescript";
}
// Mirrors approval_gates in src/solver_handlers/software_project.rs: the feature,
// step-granularity, and bash-command gates are added when the lexicon evidences
// the matching role, so the gate vocabulary lives once in data.
function softwareApprovalGates(normalized, deliveryMode) {
const gates = ["task_formalization", "implementation_plan"];
if (lexiconMentionsRole(ROLE_SOFTWARE_FEATURE, normalized)) gates.push("requirements");
if (lexiconMentionsRole(ROLE_SOFTWARE_STEP_GRANULARITY, normalized)) gates.push("each_step");
if (deliveryMode === "code_generation") {
gates.push("generated_code");
} else if (deliveryMode === "manual_instructions") {
gates.push("manual_instructions");
} else {
gates.push("generated_script");
gates.push("bash_command");
}
if (lexiconMentionsRole(ROLE_SOFTWARE_BASH_COMMAND, normalized)) {
gates.push("bash_command");
}
return [...new Set(gates)].sort();
}
function softwareImplementationCode(meaning) {
if (meaning.gameTracker) {
return {
label: "TypeScript",
fence: "typescript",
body: GAME_TRACKER_TYPESCRIPT,
};
}
if (meaning.implementationLanguage === "python") {
return { label: "Python", fence: "python", body: GENERIC_PROJECT_PYTHON };
}
if (meaning.implementationLanguage === "rust") {
return { label: "Rust", fence: "rust", body: GENERIC_PROJECT_RUST };
}
if (meaning.implementationLanguage === "javascript") {
return { label: "JavaScript", fence: "javascript", body: GENERIC_PROJECT_JAVASCRIPT };
}
return { label: "TypeScript", fence: "typescript", body: GENERIC_PROJECT_TYPESCRIPT };
}
function softwareDomainLabel(meaning) {
return meaning.gameTracker ? "tabletop_game_unit_tracker" : "software_project";
}
function softwareApprovalLabel(approved) {
return approved ? "approved" : "proposed";
}
function linoString(value) {
return `"${String(value || "")
.replace(/\\/g, "\\\\")
.replace(/"/g, '\\"')
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")}"`;
}
function softwareMeaningLino(meaning, approved) {
const lines = ["software_project_request"];
lines.push(` action ${linoString(meaning.action)}`);
lines.push(` artifact ${linoString(meaning.artifact)}`);
lines.push(` artifact_surface ${linoString(meaning.artifactSurface)}`);
lines.push(` target ${linoString(meaning.target)}`);
lines.push(` domain ${linoString(softwareDomainLabel(meaning))}`);
lines.push(` delivery_mode ${meaning.deliveryMode}`);
lines.push(` implementation_language ${linoString(meaning.implementationLanguage)}`);
lines.push(` approval_state ${softwareApprovalLabel(approved)}`);
lines.push(" approval_required true");
for (const gate of meaning.approvalGates) {
lines.push(` approval_gate ${linoString(gate)}`);
}
for (const requirement of meaning.requirements) {
lines.push(` requirement ${linoString(requirement)}`);
lines.push(
` requirement_category ${linoString(
classifySoftwareRequirement(requirement, meaning.gameTracker),
)}`,
);
}
for (const subtask of meaning.subtasks) {
lines.push(
` subtask ${linoString(
`${subtask.requirementId} [${subtask.category}] ${subtask.title}`,
)}`,
);
}
if (meaning.gameTracker) {
lines.push(' state_model "unit_state"');
lines.push(' command "apply_damage"');
lines.push(' command "set_stacks"');
lines.push(' command "tick_cooldowns"');
lines.push(' validation "damage_mitigation_floor_at_zero"');
lines.push(' validation "cooldowns_decrement_without_negative_rounds"');
} else {
lines.push(' state_model "project_records"');
lines.push(' command "create_record"');
lines.push(' command "update_record"');
lines.push(' command "export_state"');
lines.push(' validation "pure_state_transitions_before_host_api"');
}
return lines.join("\n") + "\n";
}
function softwareMeaningKey(meaning) {
return [
`action=${meaning.action}`,
`artifact=${meaning.artifact}`,
`target=${meaning.target}`,
`game_tracker=${meaning.gameTracker}`,
`delivery_mode=${meaning.deliveryMode}`,
`implementation_language=${meaning.implementationLanguage}`,
...meaning.requirements.map((requirement) => `requirement=${requirement}`),
...meaning.subtasks.map((subtask) => `subtask=${subtask.category}:${subtask.title}`),
].join(";");
}
function stableSoftwareMeaningId(meaning) {
let hash = 0xcbf29ce484222325n;
const source = softwareMeaningKey(meaning);
for (let index = 0; index < source.length; index += 1) {
hash ^= BigInt(source.charCodeAt(index));
hash = BigInt.asUintN(64, hash * 0x100000001b3n);
}
return `software_project_request_${hash.toString(16)}`;
}
function formalizeSoftwareProjectRequest(prompt) {
const normalized = normalizePrompt(prompt);
if (normalized.includes("hello") && normalized.includes("world")) return null;
const action = detectSoftwareAction(normalized);
const artifact = detectSoftwareArtifact(normalized);
if (!action || !artifact) return null;
const requirements = extractSoftwareFeatures(prompt);
const gameTracker = isGameUnitTracker(normalized);
const deliveryMode = detectSoftwareDeliveryMode(normalized);
return {
action,
artifactSurface: artifact.surface,
artifact: artifact.label,
target: extractSoftwareTarget(prompt, artifact),
requirements,
subtasks: deriveSoftwareSubtasks(requirements, gameTracker),
deliveryMode,
implementationLanguage: detectSoftwareImplementationLanguage(normalized),
approvalGates: softwareApprovalGates(normalized, deliveryMode),
gameTracker,
};
}
function softwareReasoningSteps(meaning) {
const steps = [
`Classify the impulse as a request to ${meaning.action} a ${meaning.artifact} instead of a fact lookup.`,
`Bind the target environment to ${meaning.target} and keep the first response reviewable.`,
`Extract ${meaning.requirements.length} requirement(s) into the meaning record before planning.`,
`Decompose the requirement graph into ${meaning.subtasks.length} implementation subtask(s) with category labels.`,
`Select delivery mode ${meaning.deliveryMode} and approval gates: ${meaning.approvalGates.join(", ")}.`,
];
if (meaning.gameTracker) {
steps.push(
"Map HP, Protection, Resistance, damage, and cooldown phrases to a unit-state domain model.",
);
}
steps.push("Ask for approval before producing code, scripts, manual instructions, or execution steps.");
return steps;
}
function softwarePlanSteps(meaning) {
const steps = [
"Review the formalized task, requirement graph, approval gates, and delivery mode with the user.",
];
if (meaning.gameTracker) {
steps.push(
`Confirm the ${meaning.target} storage and selected-token API boundaries.`,
"Define `UnitState` with HP, max HP, Protection, Resistance, and cooldowns.",
"Write pure transition functions for damage mitigation, stack edits, and round ticks.",
"Add tests for zero damage, overkill damage, stack changes, and cooldown expiry.",
"Wire the tested core into the extension panel and host persistence.",
);
return steps;
}
steps.push(
`Confirm the host API and data boundaries for ${meaning.target}.`,
"Define the smallest serializable state records for the requirements.",
);
for (const subtask of meaning.subtasks) {
steps.push(`Implement ${subtask.category}: ${subtask.title}.`);
}
steps.push(
`Generate a ${meaning.implementationLanguage} starter core plus language-appropriate repository initialization and checks.`,
);
steps.push("Keep shell, Docker, or WebVM commands behind the configured approval gates.");
return steps;
}
function softwareEvidence(meaning, approved) {
const evidence = [
"formalization:text_to_links_notation",
`meaning:${stableSoftwareMeaningId(meaning)}`,
`software_project:action:${meaning.action}`,
`software_project:artifact:${meaning.artifact}`,
`software_project:target:${meaning.target}`,
`software_project:domain:${softwareDomainLabel(meaning)}`,
`software_project:delivery_mode:${meaning.deliveryMode}`,
`software_project:implementation_language:${meaning.implementationLanguage}`,
`approval_state:${softwareApprovalLabel(approved)}`,
`software_project:strategy:${meaning.gameTracker ? "game_unit_tracker" : "bounded_project_plan"}`,
];
for (const gate of meaning.approvalGates) {
evidence.push(`approval_gate:${gate}`);
}
for (const requirement of meaning.requirements) {
evidence.push(`requirement:${requirement}`);
evidence.push(`requirement_category:${classifySoftwareRequirement(requirement, meaning.gameTracker)}`);
}
for (const subtask of meaning.subtasks) {
evidence.push(`software_project:subtask:${subtask.requirementId}:${subtask.category}:${subtask.title}`);
}
return evidence;
}
function renderSoftwareProjectPlan(meaning) {
const lines = [];
lines.push(
`Implementation plan pending approval for a ${meaning.artifact} targeting ${meaning.target}.`,
);
lines.push("");
lines.push("Formalized meaning:");
lines.push("```lino");
lines.push(softwareMeaningLino(meaning, false).trimEnd());
lines.push("```");
lines.push("");
lines.push("Reasoning steps:");
softwareReasoningSteps(meaning).forEach((step, index) => {
lines.push(`${index + 1}. ${step}`);
});
lines.push("");
lines.push("Requirement model:");
meaning.requirements.forEach((requirement, index) => {
const category = classifySoftwareRequirement(requirement, meaning.gameTracker);
lines.push(`${index + 1}. [${category}] ${requirement}`);
});
lines.push("");
lines.push("Subtasks:");
meaning.subtasks.forEach((subtask, index) => {
lines.push(`${index + 1}. ${subtask.requirementId} -> ${subtask.title}`);
});
lines.push("");
lines.push("Approval gates:");
meaning.approvalGates.forEach((gate) => {
lines.push(`- ${gate}`);
});
lines.push("");
lines.push("Proposed plan:");
softwarePlanSteps(meaning).forEach((step, index) => {
lines.push(`${index + 1}. ${step}`);
});
lines.push("");
lines.push(
"Reply `approve plan` to generate the starter implementation, or describe what to change.",
);
return lines.join("\n");
}
function renderSoftwareProjectImplementation(meaning) {
const lines = [];
lines.push(
`Approved implementation starter for a ${meaning.artifact} targeting ${meaning.target}.`,
);
lines.push("");
lines.push("Formalized meaning:");
lines.push("```lino");
lines.push(softwareMeaningLino(meaning, true).trimEnd());
lines.push("```");
lines.push("");
lines.push("Implementation steps:");
softwarePlanSteps(meaning).forEach((step, index) => {
lines.push(`${index + 1}. ${step}`);
});
lines.push("");
const code = softwareImplementationCode(meaning);
lines.push(`Starter ${code.label} core:`);
lines.push("");
lines.push(`\`\`\`${code.fence}`);
lines.push(code.body);
lines.push("```");
lines.push("");
lines.push("Generated code checks:");
lines.push(`1. Initialize a ${code.label} project in an isolated workspace.`);
lines.push("2. Run the language-native syntax/type check before host integration.");
return lines.join("\n");
}
// Mirror is_approval_prompt in src/solver_handlers/software_project.rs: strip
// leading/trailing non-alphanumerics (Unicode-aware, so a non-Latin go-ahead
// compacts the same way), drop interior sentence punctuation, then match the
// whole compacted prompt against any approval-trigger surface word. The words
// live in data/seed/meanings-software-project.lino, not in code.
function isSoftwareApprovalPrompt(normalized) {
const compact = String(normalized || "")
.trim()
.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, "")
.replace(/[.!,]/g, "");
return meaningsWithRole(ROLE_SOFTWARE_APPROVAL_TRIGGER).some((meaning) =>
meaning.words.some((word) => compact === word),
);
}
function lastHistoryTurn(history, role) {
if (!Array.isArray(history)) return null;
for (let index = history.length - 1; index >= 0; index -= 1) {
const turn = history[index];
if (turn && turn.role === role && turn.content) return String(turn.content);
}
return null;
}
function priorSoftwareProjectMeaning(history) {
const assistant = lastHistoryTurn(history, "assistant");
if (
!assistant ||
!assistant.includes("software_project_request") ||
!assistant.includes("approve plan")
) {
return null;
}
const user = lastHistoryTurn(history, "user");
return user ? formalizeSoftwareProjectRequest(user) : null;
}
function trySoftwareProjectRequest(prompt, history = []) {
const normalized = normalizePrompt(prompt);
if (isSoftwareApprovalPrompt(normalized)) {
const prior = priorSoftwareProjectMeaning(history);
if (prior) {
return {
intent: "software_project_implementation",
content: renderSoftwareProjectImplementation(prior),
confidence: 0.82,
evidence: softwareEvidence(prior, true),
};
}
}
const meaning = formalizeSoftwareProjectRequest(prompt);
if (!meaning) return null;
return {
intent: "software_project_plan",
content: renderSoftwareProjectPlan(meaning),
confidence: 0.78,
evidence: softwareEvidence(meaning, false),
};
}
// Maps a follow-up kind to the imperative verb used when rendering it. Mirrors
// `FollowUpKind::action` in src/solver_handlers/software_project_followup.rs.
// The surface words that *recognise* each kind no longer live here — they are
// self-describing meanings in data/seed/meanings-software-project.lino, queried
// by detectSoftwareFollowUp via the software_followup_* roles (issue #386).
const SOFTWARE_FOLLOW_UP_ACTIONS = {
verification: "test",
execution: "run",
demonstration: "show",
};
const SOFTWARE_FOLLOW_UP_GATES = ["generated_code", "test_execution", "network_access"];
// Recover the active software-project dialogue from history regardless of
// whether the plan was approved. Mirrors `prior_software_project_dialogue`.
function priorSoftwareProjectDialogue(history) {
const assistant = lastHistoryTurn(history, "assistant");
if (!assistant || !assistant.includes("software_project_request")) {
return null;
}
const approved = assistant.includes("approval_state approved");
const user = lastHistoryTurn(history, "user");
const meaning = user ? formalizeSoftwareProjectRequest(user) : null;
return meaning ? { meaning, approved } : null;
}
// Pull the first domain-like token (e.g. `wikipedia.org`) out of the prompt.
function extractFollowUpTargetSite(prompt) {
for (const raw of String(prompt || "").split(/\s+/)) {
const token = raw.replace(/^[^A-Za-z0-9]+|[^A-Za-z0-9]+$/g, "");
if (!token.includes(".")) continue;
const lastDot = token.lastIndexOf(".");
const host = token.slice(0, lastDot);
const tld = token.slice(lastDot + 1);
if (
tld.length >= 2 &&
/^[A-Za-z]+$/.test(tld) &&
/[A-Za-z]/.test(host)
) {
return token.toLowerCase();
}
}
return null;
}
// Capture the clause after "show me"/"show"/"print"/"display" (capped at 12
// words) so the follow-up records what the user wants surfaced.
function extractFollowUpExpectedOutput(prompt) {
const source = String(prompt || "");
const lower = source.toLowerCase();
for (const marker of ["show me ", "show ", "print ", "display "]) {
const found = lower.indexOf(marker);
if (found < 0) continue;
const start = found + marker.length;
const tail = source.slice(start);
const stopMatch = tail.match(/[.?\n;]/);
const stop = stopMatch ? stopMatch.index : tail.length;
const clause = tail
.slice(0, stop)
.split(/\s+/)
.filter(Boolean)
.slice(0, 12)
.join(" ");
if (clause) return clause;
}
return null;
}
// Recognise which follow-up a prompt evidences by *meaning*, not a hardcoded
// per-language marker table (issue #386). Each follow-up kind is a
// self-describing meaning in data/seed/meanings-software-project.lino; its
// surface words (every supported language) live there, while this code knows
// only the concepts and their precedence — verification outranks execution
// outranks demonstration, so a combined "test it and run it" records the
// stronger goal. Mirrors follow_up_kind in
// src/solver_handlers/software_project_followup.rs.
function detectSoftwareFollowUp(prompt, normalized) {
let kind = null;
for (const [role, candidate] of [
[ROLE_SOFTWARE_FOLLOWUP_VERIFICATION, "verification"],
[ROLE_SOFTWARE_FOLLOWUP_EXECUTION, "execution"],
[ROLE_SOFTWARE_FOLLOWUP_DEMONSTRATION, "demonstration"],
]) {
if (lexiconMentionsRoleSubstring(role, normalized)) {
kind = candidate;
break;
}
}
if (!kind) return null;
return {
kind,
action: SOFTWARE_FOLLOW_UP_ACTIONS[kind],
targetSite: extractFollowUpTargetSite(prompt),
expectedOutput: extractFollowUpExpectedOutput(prompt),
};
}
function followUpMeaningId(meaning, followUp) {
const key = [
`parent=${stableSoftwareMeaningId(meaning)}`,
`kind=${followUp.kind}`,
`site=${followUp.targetSite || ""}`,
`output=${followUp.expectedOutput || ""}`,
].join(";");
return stableBehaviorRuleId("software_project_followup", key);
}
function followUpReasoningSteps(meaning, followUp) {
const steps = [
`Recognize "${followUp.action}" as a ${followUp.kind} request that exercises the ${meaning.artifact} from the active plan, not a fact lookup.`,
];
if (followUp.targetSite) {
steps.push(
`Bind the test target to ${followUp.targetSite} and keep live fetches behind the network_access gate.`,
);
}
if (followUp.expectedOutput) {
steps.push(
`Record the expected output as "${followUp.expectedOutput}" so the test harness can assert it.`,
);
}
steps.push(
"Drive the artifact through a deterministic fixture before any host API or network call.",
);
steps.push(
"Keep code execution behind approval gates because the sandbox cannot run untrusted code.",
);
return steps;
}
function followUpPlanSteps(meaning, followUp) {
const site = followUp.targetSite || "the requested target";
const steps = [
`Generate the ${meaning.artifact} core plus a deterministic test harness with a captured ${site} fixture.`,
"Assert each requirement (parsing, extraction, counting, summary) against the fixture.",
];
if (followUp.expectedOutput) {
steps.push(`Surface ${followUp.expectedOutput} from the fixture run.`);
}
steps.push(
`Run the ${meaning.implementationLanguage} test command once the generated_code gate is approved.`,
);
steps.push(
`Promote the run to live ${site} only after the test_execution and network_access gates pass.`,
);
return steps;
}
function followUpEvidence(meaning, followUp, approved) {
const evidence = [
"formalization:text_to_links_notation",
`meaning:${followUpMeaningId(meaning, followUp)}`,
`software_project:parent:${stableSoftwareMeaningId(meaning)}`,
`software_project:follow_up_kind:${followUp.kind}`,
];
if (followUp.targetSite) {
evidence.push(`software_project:target_site:${followUp.targetSite}`);
}
if (followUp.expectedOutput) {
evidence.push(`software_project:expected_output:${followUp.expectedOutput}`);
}
evidence.push(`approval_state:${softwareApprovalLabel(approved)}`);
for (const gate of SOFTWARE_FOLLOW_UP_GATES) {
evidence.push(`approval_gate:${gate}`);
}
return evidence;
}
function renderSoftwareProjectFollowUp(meaning, followUp, approved) {
const lines = [];
lines.push(
`Recorded a ${followUp.kind} follow-up for the ${meaning.artifact} from the active plan.`,
);
lines.push("");
lines.push("Formalized meaning:");
lines.push("```lino");
lines.push("software_project_followup");
lines.push(` parent_request ${linoString(stableSoftwareMeaningId(meaning))}`);
lines.push(` parent_artifact ${linoString(meaning.artifact)}`);
lines.push(` action ${linoString(followUp.action)}`);
lines.push(` follow_up_kind ${followUp.kind}`);
if (followUp.targetSite) {
lines.push(` target_site ${linoString(followUp.targetSite)}`);
}
if (followUp.expectedOutput) {
lines.push(` expected_output ${linoString(followUp.expectedOutput)}`);
}
lines.push(` delivery_mode ${meaning.deliveryMode}`);
lines.push(` implementation_language ${linoString(meaning.implementationLanguage)}`);
lines.push(` approval_state ${softwareApprovalLabel(approved)}`);
lines.push(" approval_required true");
for (const gate of SOFTWARE_FOLLOW_UP_GATES) {
lines.push(` approval_gate ${linoString(gate)}`);
}
lines.push("```");
lines.push("");
lines.push("Reasoning steps:");
followUpReasoningSteps(meaning, followUp).forEach((step, index) => {
lines.push(`${index + 1}. ${step}`);
});
lines.push("");
lines.push("Verification plan:");
followUpPlanSteps(meaning, followUp).forEach((step, index) => {
lines.push(`${index + 1}. ${step}`);
});
lines.push("");
if (approved) {
lines.push(
"The plan is approved, so the generated starter already includes this test harness. " +
"Running it live needs the test_execution and network_access gates.",
);
} else {
lines.push(
"Reply `approve plan` to generate the artifact plus this test harness. Running it live " +
"against the target needs the test_execution and network_access gates.",
);
}
return lines.join("\n");
}
// Follow-up handler for an active software-project dialogue (issue #341). Runs
// before `tryConceptLookup` so a decomposed step like "test it by scraping
// wikipedia.org and show me the top 10 most frequent words" stays bound to the
// project instead of resolving the `wikipedia` concept or falling to the
// unknown opener. Mirrors `try_software_project_followup` in the Rust solver.
function trySoftwareProjectFollowup(prompt, history = []) {
const normalized = normalizePrompt(prompt);
// Approval prompts stay with the main request handler, which advances to the
// implementation starter.
if (isSoftwareApprovalPrompt(normalized)) return null;
const dialogue = priorSoftwareProjectDialogue(history);
if (!dialogue) return null;
const followUp = detectSoftwareFollowUp(prompt, normalized);
if (!followUp) return null;
return {
intent: "software_project_followup",
content: renderSoftwareProjectFollowUp(dialogue.meaning, followUp, dialogue.approved),
confidence: 0.74,
evidence: followUpEvidence(dialogue.meaning, followUp, dialogue.approved),
};
}
function tryJavaScriptExecution(prompt) {
const program = extractJavaScriptProgram(prompt);
if (program === null) return null;
const logs = [];
const captureConsole = {
log: (...args) =>
logs.push(
args
.map((value) =>
typeof value === "string" ? value : JSON.stringify(value),
)
.join(" "),
),
};
let result;
let error = null;
try {
const runner = new Function(
"console",
`"use strict"; return (function(){ ${program}\n })();`,
);
result = runner(captureConsole);
} catch (err) {
error = err;
}
const lines = [];
lines.push("Execution status: ran in the demo's Web Worker sandbox.");
lines.push("Source:");
lines.push("```javascript");
lines.push(program);
lines.push("```");
if (error) {
lines.push("");
lines.push(`Error: ${error.message || String(error)}`);
} else {
if (logs.length > 0) {
lines.push("");
lines.push("Output:");
lines.push("```text");
lines.push(logs.join("\n"));
lines.push("```");
}
if (result !== undefined) {
lines.push("");
lines.push(`Returned: \`${String(result)}\``);
}
if (logs.length === 0 && result === undefined) {
lines.push("");
lines.push("Program completed without output or return value.");
}
}
lines.push("");
lines.push(
"Note: the browser worker has no DOM or network access, so side effects are limited.",
);
return {
intent: error ? "javascript_execution_error" : "javascript_execution",
content: lines.join("\n"),
confidence: error ? 0.5 : 0.95,
evidence: [
`execution_status:javascript:${error ? "error" : "ran"}`,
"language:javascript",
],
};
}
// `saveAs`, `setupHint`, `runCommand` and `checkCommand` mirror the same fields
// on `coding::catalog::ProgramLanguage` so the demo's novice "How to test it
// yourself" steps match the Rust engine exactly (issue #330). No entry carries
// its alias surfaces inline: the words a prompt must contain to resolve a
// language live in the `program_language_<slug>` meaning (role
// `program_language_alias`) and `programLanguageFromPrompt` reads them by slug
// (issue #386), matching the Rust catalog byte-for-byte through the shared seed.
const WRITE_PROGRAM_LANGUAGES = {
rust: {
name: "Rust",
fence: "rust",
saveAs: "main.rs",
setupHint: "the Rust toolchain from https://rustup.rs",
checkCommand: "rustc main.rs -o main",
runCommand: "./main",
},
python: {
name: "Python",
fence: "python",
saveAs: "main.py",
setupHint: "Python 3 from https://www.python.org/downloads/",
checkCommand: "python3 -m py_compile main.py",
runCommand: "python3 main.py",
},
javascript: {
name: "JavaScript",
fence: "javascript",
saveAs: "main.js",
setupHint: "Node.js from https://nodejs.org/",
checkCommand: "node --check main.js",
runCommand: "node main.js",
},
typescript: {
name: "TypeScript",
fence: "typescript",
saveAs: "hello.ts",
setupHint:
"Node.js from https://nodejs.org/ plus TypeScript via `npm install -g typescript`",
checkCommand: "tsc hello.ts",
runCommand: "node hello.js",
},
go: {
name: "Go",
fence: "go",
saveAs: "main.go",
setupHint: "Go from https://go.dev/dl/",
checkCommand: null,
runCommand: "go run main.go",
},
c: {
name: "C",
fence: "c",
saveAs: "main.c",
setupHint:
"a C compiler such as GCC from https://gcc.gnu.org/ or your package manager",
checkCommand: "gcc main.c -o main",
runCommand: "./main",
},
cpp: {
name: "C++",
fence: "cpp",
saveAs: "main.cpp",
setupHint:
"a C++ compiler such as g++ from https://gcc.gnu.org/ or your package manager",
checkCommand: "g++ main.cpp -o main",
runCommand: "./main",
},
java: {
name: "Java",
fence: "java",
saveAs: "Main.java",
setupHint: "a JDK from https://adoptium.net/",
checkCommand: "javac Main.java",
runCommand: "java Main",
},
csharp: {
name: "C#",
fence: "csharp",
saveAs: "Program.cs",
setupHint: "the .NET SDK from https://dotnet.microsoft.com/download",
checkCommand: "dotnet build",
runCommand: "dotnet run",
},
ruby: {
name: "Ruby",
fence: "ruby",
saveAs: "main.rb",
setupHint: "Ruby from https://www.ruby-lang.org/en/downloads/",
checkCommand: "ruby -c main.rb",
runCommand: "ruby main.rb",
},
};
const WRITE_PROGRAM_TASKS = {
hello_world: {
label: "hello world",
output: "Hello, world!",
},
count_to_three: {
label: "count to three",
output: "1\n2\n3",
},
list_files: {
label: "list files in the current directory",
// Verified output for the documented sample directory containing exactly
// `Cargo.toml`, `README.md`, and `main.rs`. Every template sorts names in
// byte order, so the output is identical across languages (issue #312).
output: "Cargo.toml\nREADME.md\nmain.rs",
},
list_files_arg: {
label: "list files in the directory given as a path argument",
// Issue #324 follow-up: "Сделай так, чтобы программа принимала путь как
// аргумент" (make the program accept a path as an argument). This is the
// path-argument variant of `list_files`; conversation context maps a bare
// "accept a path argument" modification onto it through the program-plan
// rules. Mirrors the Rust `list_files_arg` task.
output: "Cargo.toml\nREADME.md\nmain.rs",
},
list_files_reverse_sort: {
label: "list files in the current directory in reverse-sorted order",
output: "main.rs\nREADME.md\nCargo.toml",
},
list_files_arg_reverse_sort: {
label: "list files from a path argument in reverse-sorted order",
output: "main.rs\nREADME.md\nCargo.toml",
},
// Issue #330: classic branching/looping exercise over 1..=15. Mirrors the Rust
// `fizzbuzz` task; fixed range so the output is deterministic and verifiable.
fizzbuzz: {
label: "FizzBuzz",
output: "1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz",
},
// Issue #330: fixed to 5! = 120 so the verified output is unambiguous (the
// aliases require the number 5). Mirrors the Rust `factorial` task.
factorial: {
label: "factorial of 5",
output: "120",
},
// Issue #330: reverses the literal string "hello" -> "olleh". Mirrors the Rust
// `reverse_string` task; fixed input keeps the output verifiable.
reverse_string: {
label: "string reversal",
output: "olleh",
},
// Issue #330: sums 1..=10 -> 55. Mirrors the Rust `sum_to_ten` task.
sum_to_ten: {
label: "sum from 1 to 10",
output: "55",
},
// Issue #334: a recursive `fibonacci` function evaluated at the 10th term
// (F(1)=F(2)=1 -> F(10)=55). Mirrors the Rust `fibonacci` task; fixed index so
// the output is verifiable.
fibonacci: {
label: "recursive Fibonacci",
output: "55",
},
};
const WRITE_PROGRAM_TEMPLATES = {
hello_world: {
rust: 'fn main() {\n println!("Hello, world!");\n}',
python: 'print("Hello, world!")',
javascript: 'console.log("Hello, world!");',
typescript: 'console.log("Hello, world!");',
go: 'package main\n\nimport "fmt"\n\nfunc main() {\n fmt.Println("Hello, world!")\n}',
c: '#include <stdio.h>\n\nint main(void) {\n puts("Hello, world!");\n return 0;\n}',
cpp: '#include <iostream>\n\nint main() {\n std::cout << "Hello, world!" << std::endl;\n return 0;\n}',
java: 'public class Main {\n public static void main(String[] args) {\n System.out.println("Hello, world!");\n }\n}',
csharp:
'using System;\n\nclass Program {\n static void Main() {\n Console.WriteLine("Hello, world!");\n }\n}',
ruby: 'puts "Hello, world!"',
},
count_to_three: {
rust:
'fn main() {\n for number in 1..=3 {\n println!("{number}");\n }\n}',
python: "for number in range(1, 4):\n print(number)",
javascript:
"for (let number = 1; number <= 3; number += 1) {\n console.log(number);\n}",
typescript:
"for (let number = 1; number <= 3; number += 1) {\n console.log(number);\n}",
go:
'package main\n\nimport "fmt"\n\nfunc main() {\n for number := 1; number <= 3; number++ {\n fmt.Println(number)\n }\n}',
c:
'#include <stdio.h>\n\nint main(void) {\n for (int number = 1; number <= 3; number++) {\n printf("%d\\n", number);\n }\n return 0;\n}',
},
list_files: {
rust:
'use std::fs;\n\nfn main() -> std::io::Result<()> {\n let mut names: Vec<String> = fs::read_dir(".")?\n .filter_map(Result::ok)\n .filter(|entry| entry.path().is_file())\n .map(|entry| entry.file_name().to_string_lossy().into_owned())\n .collect();\n names.sort();\n for name in names {\n println!("{name}");\n }\n Ok(())\n}',
python:
'import os\n\nnames = sorted(name for name in os.listdir(".") if os.path.isfile(name))\nfor name in names:\n print(name)',
javascript:
'const fs = require("fs");\n\nconst names = fs\n .readdirSync(".")\n .filter((name) => fs.statSync(name).isFile())\n .sort();\n\nfor (const name of names) {\n console.log(name);\n}',
typescript:
'import * as fs from "fs";\n\nconst names: string[] = fs\n .readdirSync(".")\n .filter((name) => fs.statSync(name).isFile())\n .sort();\n\nfor (const name of names) {\n console.log(name);\n}',
go:
'package main\n\nimport (\n "fmt"\n "os"\n "sort"\n)\n\nfunc main() {\n entries, err := os.ReadDir(".")\n if err != nil {\n panic(err)\n }\n var names []string\n for _, entry := range entries {\n if !entry.IsDir() {\n names = append(names, entry.Name())\n }\n }\n sort.Strings(names)\n for _, name := range names {\n fmt.Println(name)\n }\n}',
c:
'#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\nstatic int compare(const void *a, const void *b) {\n return strcmp(*(const char *const *)a, *(const char *const *)b);\n}\n\nint main(void) {\n DIR *dir = opendir(".");\n if (dir == NULL) {\n return 1;\n }\n char *names[1024];\n size_t count = 0;\n struct dirent *entry;\n while ((entry = readdir(dir)) != NULL && count < 1024) {\n struct stat info;\n if (stat(entry->d_name, &info) == 0 && S_ISREG(info.st_mode)) {\n names[count++] = strdup(entry->d_name);\n }\n }\n closedir(dir);\n qsort(names, count, sizeof(char *), compare);\n for (size_t i = 0; i < count; i++) {\n printf("%s\\n", names[i]);\n free(names[i]);\n }\n return 0;\n}',
cpp:
"#include <algorithm>\n#include <filesystem>\n#include <iostream>\n#include <string>\n#include <vector>\n\nint main() {\n namespace fs = std::filesystem;\n std::vector<std::string> names;\n for (const auto &entry : fs::directory_iterator(\".\")) {\n if (entry.is_regular_file()) {\n names.push_back(entry.path().filename().string());\n }\n }\n std::sort(names.begin(), names.end());\n for (const auto &name : names) {\n std::cout << name << '\\n';\n }\n}",
java:
'import java.io.File;\nimport java.util.Arrays;\n\npublic class Main {\n public static void main(String[] args) {\n File[] entries = new File(".").listFiles();\n if (entries == null) {\n return;\n }\n String[] names = Arrays.stream(entries)\n .filter(File::isFile)\n .map(File::getName)\n .sorted()\n .toArray(String[]::new);\n for (String name : names) {\n System.out.println(name);\n }\n }\n}',
csharp:
'using System;\nusing System.IO;\nusing System.Linq;\n\nclass Program {\n static void Main() {\n var names = Directory.GetFiles(".")\n .Select(Path.GetFileName)\n .OrderBy(name => name, StringComparer.Ordinal);\n foreach (var name in names) {\n Console.WriteLine(name);\n }\n }\n}',
ruby:
'names = Dir.entries(".").select { |name| File.file?(name) }.sort\nnames.each { |name| puts name }',
},
// Issue #324 follow-up: list files in the directory passed as the first
// command-line argument, defaulting to "." when none is supplied. Mirrors the
// Rust `list_files_arg` templates.
list_files_arg: {
rust:
'use std::env;\nuse std::fs;\n\nfn main() {\n let path = env::args().nth(1).unwrap_or_else(|| String::from("."));\n let mut names: Vec<String> = fs::read_dir(&path)\n .expect("failed to read directory")\n .filter_map(|entry| entry.ok())\n .filter(|entry| entry.path().is_file())\n .map(|entry| entry.file_name().to_string_lossy().into_owned())\n .collect();\n names.sort();\n for name in names {\n println!("{name}");\n }\n}',
python:
'import os\nimport sys\n\npath = sys.argv[1] if len(sys.argv) > 1 else "."\nnames = sorted(\n name for name in os.listdir(path) if os.path.isfile(os.path.join(path, name))\n)\nfor name in names:\n print(name)',
javascript:
'const fs = require("fs");\nconst path = require("path");\n\nconst dir = process.argv[2] || ".";\nconst names = fs\n .readdirSync(dir)\n .filter((name) => fs.statSync(path.join(dir, name)).isFile())\n .sort();\n\nfor (const name of names) {\n console.log(name);\n}',
typescript:
'import * as fs from "fs";\nimport * as path from "path";\n\nconst dir: string = process.argv[2] ?? ".";\nconst names: string[] = fs\n .readdirSync(dir)\n .filter((name) => fs.statSync(path.join(dir, name)).isFile())\n .sort();\n\nfor (const name of names) {\n console.log(name);\n}',
go:
'package main\n\nimport (\n "fmt"\n "os"\n "sort"\n)\n\nfunc main() {\n dir := "."\n if len(os.Args) > 1 {\n dir = os.Args[1]\n }\n entries, err := os.ReadDir(dir)\n if err != nil {\n panic(err)\n }\n var names []string\n for _, entry := range entries {\n if !entry.IsDir() {\n names = append(names, entry.Name())\n }\n }\n sort.Strings(names)\n for _, name := range names {\n fmt.Println(name)\n }\n}',
c:
'#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\nstatic int compare(const void *a, const void *b) {\n return strcmp(*(const char *const *)a, *(const char *const *)b);\n}\n\nint main(int argc, char *argv[]) {\n const char *path = argc > 1 ? argv[1] : ".";\n DIR *dir = opendir(path);\n if (dir == NULL) {\n return 1;\n }\n char *names[1024];\n size_t count = 0;\n struct dirent *entry;\n while ((entry = readdir(dir)) != NULL && count < 1024) {\n char full[4096];\n snprintf(full, sizeof(full), "%s/%s", path, entry->d_name);\n struct stat info;\n if (stat(full, &info) == 0 && S_ISREG(info.st_mode)) {\n names[count++] = strdup(entry->d_name);\n }\n }\n closedir(dir);\n qsort(names, count, sizeof(char *), compare);\n for (size_t i = 0; i < count; i++) {\n printf("%s\\n", names[i]);\n free(names[i]);\n }\n return 0;\n}',
cpp:
"#include <algorithm>\n#include <filesystem>\n#include <iostream>\n#include <string>\n#include <vector>\n\nint main(int argc, char *argv[]) {\n namespace fs = std::filesystem;\n std::string path = argc > 1 ? argv[1] : \".\";\n std::vector<std::string> names;\n for (const auto &entry : fs::directory_iterator(path)) {\n if (entry.is_regular_file()) {\n names.push_back(entry.path().filename().string());\n }\n }\n std::sort(names.begin(), names.end());\n for (const auto &name : names) {\n std::cout << name << '\\n';\n }\n}",
java:
'import java.io.File;\nimport java.util.Arrays;\n\npublic class Main {\n public static void main(String[] args) {\n String path = args.length > 0 ? args[0] : ".";\n File[] entries = new File(path).listFiles();\n if (entries == null) {\n return;\n }\n String[] names = Arrays.stream(entries)\n .filter(File::isFile)\n .map(File::getName)\n .sorted()\n .toArray(String[]::new);\n for (String name : names) {\n System.out.println(name);\n }\n }\n}',
csharp:
'using System;\nusing System.IO;\nusing System.Linq;\n\nclass Program {\n static void Main(string[] args) {\n var path = args.Length > 0 ? args[0] : ".";\n var names = Directory.GetFiles(path)\n .Select(Path.GetFileName)\n .OrderBy(name => name, StringComparer.Ordinal);\n foreach (var name in names) {\n Console.WriteLine(name);\n }\n }\n}',
ruby:
'path = ARGV[0] || "."\nnames = Dir.entries(path).select { |name| File.file?(File.join(path, name)) }.sort\nnames.each { |name| puts name }',
},
list_files_reverse_sort: {
rust:
'use std::fs;\n\nfn main() -> std::io::Result<()> {\n let mut names: Vec<String> = fs::read_dir(".")?\n .filter_map(Result::ok)\n .filter(|entry| entry.path().is_file())\n .map(|entry| entry.file_name().to_string_lossy().into_owned())\n .collect();\n names.sort_by(|a, b| b.cmp(a));\n for name in names {\n println!("{name}");\n }\n Ok(())\n}',
python:
'import os\n\nnames = sorted(\n (name for name in os.listdir(".") if os.path.isfile(name)),\n reverse=True,\n)\nfor name in names:\n print(name)',
javascript:
'const fs = require("fs");\n\nconst names = fs\n .readdirSync(".")\n .filter((name) => fs.statSync(name).isFile())\n .sort()\n .reverse();\n\nfor (const name of names) {\n console.log(name);\n}',
typescript:
'import * as fs from "fs";\n\nconst names: string[] = fs\n .readdirSync(".")\n .filter((name) => fs.statSync(name).isFile())\n .sort()\n .reverse();\n\nfor (const name of names) {\n console.log(name);\n}',
go:
'package main\n\nimport (\n "fmt"\n "os"\n "sort"\n)\n\nfunc main() {\n entries, err := os.ReadDir(".")\n if err != nil {\n panic(err)\n }\n var names []string\n for _, entry := range entries {\n if !entry.IsDir() {\n names = append(names, entry.Name())\n }\n }\n sort.Sort(sort.Reverse(sort.StringSlice(names)))\n for _, name := range names {\n fmt.Println(name)\n }\n}',
c:
'#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\nstatic int compare_desc(const void *a, const void *b) {\n return strcmp(*(const char *const *)b, *(const char *const *)a);\n}\n\nint main(void) {\n DIR *dir = opendir(".");\n if (dir == NULL) {\n return 1;\n }\n char *names[1024];\n size_t count = 0;\n struct dirent *entry;\n while ((entry = readdir(dir)) != NULL && count < 1024) {\n struct stat info;\n if (stat(entry->d_name, &info) == 0 && S_ISREG(info.st_mode)) {\n names[count++] = strdup(entry->d_name);\n }\n }\n closedir(dir);\n qsort(names, count, sizeof(char *), compare_desc);\n for (size_t i = 0; i < count; i++) {\n printf("%s\\n", names[i]);\n free(names[i]);\n }\n return 0;\n}',
cpp:
"#include <algorithm>\n#include <filesystem>\n#include <iostream>\n#include <string>\n#include <vector>\n\nint main() {\n namespace fs = std::filesystem;\n std::vector<std::string> names;\n for (const auto &entry : fs::directory_iterator(\".\")) {\n if (entry.is_regular_file()) {\n names.push_back(entry.path().filename().string());\n }\n }\n std::sort(names.rbegin(), names.rend());\n for (const auto &name : names) {\n std::cout << name << '\\n';\n }\n}",
java:
'import java.io.File;\nimport java.util.Arrays;\nimport java.util.Comparator;\n\npublic class Main {\n public static void main(String[] args) {\n File[] entries = new File(".").listFiles();\n if (entries == null) {\n return;\n }\n String[] names = Arrays.stream(entries)\n .filter(File::isFile)\n .map(File::getName)\n .sorted(Comparator.reverseOrder())\n .toArray(String[]::new);\n for (String name : names) {\n System.out.println(name);\n }\n }\n}',
csharp:
'using System;\nusing System.IO;\nusing System.Linq;\n\nclass Program {\n static void Main() {\n var names = Directory.GetFiles(".")\n .Select(Path.GetFileName)\n .OrderByDescending(name => name, StringComparer.Ordinal);\n foreach (var name in names) {\n Console.WriteLine(name);\n }\n }\n}',
ruby:
'names = Dir.entries(".").select { |name| File.file?(name) }.sort.reverse\nnames.each { |name| puts name }',
},
list_files_arg_reverse_sort: {
rust:
'use std::env;\nuse std::fs;\n\nfn main() {\n let path = env::args().nth(1).unwrap_or_else(|| String::from("."));\n let mut names: Vec<String> = fs::read_dir(&path)\n .expect("failed to read directory")\n .filter_map(|entry| entry.ok())\n .filter(|entry| entry.path().is_file())\n .map(|entry| entry.file_name().to_string_lossy().into_owned())\n .collect();\n names.sort_by(|a, b| b.cmp(a));\n for name in names {\n println!("{name}");\n }\n}',
python:
'import os\nimport sys\n\npath = sys.argv[1] if len(sys.argv) > 1 else "."\nnames = sorted(\n (\n name\n for name in os.listdir(path)\n if os.path.isfile(os.path.join(path, name))\n ),\n reverse=True,\n)\nfor name in names:\n print(name)',
javascript:
'const fs = require("fs");\nconst path = require("path");\n\nconst dir = process.argv[2] || ".";\nconst names = fs\n .readdirSync(dir)\n .filter((name) => fs.statSync(path.join(dir, name)).isFile())\n .sort()\n .reverse();\n\nfor (const name of names) {\n console.log(name);\n}',
typescript:
'import * as fs from "fs";\nimport * as path from "path";\n\nconst dir: string = process.argv[2] ?? ".";\nconst names: string[] = fs\n .readdirSync(dir)\n .filter((name) => fs.statSync(path.join(dir, name)).isFile())\n .sort()\n .reverse();\n\nfor (const name of names) {\n console.log(name);\n}',
go:
'package main\n\nimport (\n "fmt"\n "os"\n "sort"\n)\n\nfunc main() {\n dir := "."\n if len(os.Args) > 1 {\n dir = os.Args[1]\n }\n entries, err := os.ReadDir(dir)\n if err != nil {\n panic(err)\n }\n var names []string\n for _, entry := range entries {\n if !entry.IsDir() {\n names = append(names, entry.Name())\n }\n }\n sort.Sort(sort.Reverse(sort.StringSlice(names)))\n for _, name := range names {\n fmt.Println(name)\n }\n}',
c:
'#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\nstatic int compare_desc(const void *a, const void *b) {\n return strcmp(*(const char *const *)b, *(const char *const *)a);\n}\n\nint main(int argc, char *argv[]) {\n const char *path = argc > 1 ? argv[1] : ".";\n DIR *dir = opendir(path);\n if (dir == NULL) {\n return 1;\n }\n char *names[1024];\n size_t count = 0;\n struct dirent *entry;\n while ((entry = readdir(dir)) != NULL && count < 1024) {\n char full[4096];\n snprintf(full, sizeof(full), "%s/%s", path, entry->d_name);\n struct stat info;\n if (stat(full, &info) == 0 && S_ISREG(info.st_mode)) {\n names[count++] = strdup(entry->d_name);\n }\n }\n closedir(dir);\n qsort(names, count, sizeof(char *), compare_desc);\n for (size_t i = 0; i < count; i++) {\n printf("%s\\n", names[i]);\n free(names[i]);\n }\n return 0;\n}',
cpp:
"#include <algorithm>\n#include <filesystem>\n#include <iostream>\n#include <string>\n#include <vector>\n\nint main(int argc, char *argv[]) {\n namespace fs = std::filesystem;\n std::string path = argc > 1 ? argv[1] : \".\";\n std::vector<std::string> names;\n for (const auto &entry : fs::directory_iterator(path)) {\n if (entry.is_regular_file()) {\n names.push_back(entry.path().filename().string());\n }\n }\n std::sort(names.rbegin(), names.rend());\n for (const auto &name : names) {\n std::cout << name << '\\n';\n }\n}",
java:
'import java.io.File;\nimport java.util.Arrays;\nimport java.util.Comparator;\n\npublic class Main {\n public static void main(String[] args) {\n String path = args.length > 0 ? args[0] : ".";\n File[] entries = new File(path).listFiles();\n if (entries == null) {\n return;\n }\n String[] names = Arrays.stream(entries)\n .filter(File::isFile)\n .map(File::getName)\n .sorted(Comparator.reverseOrder())\n .toArray(String[]::new);\n for (String name : names) {\n System.out.println(name);\n }\n }\n}',
csharp:
'using System;\nusing System.IO;\nusing System.Linq;\n\nclass Program {\n static void Main(string[] args) {\n var path = args.Length > 0 ? args[0] : ".";\n var names = Directory.GetFiles(path)\n .Select(Path.GetFileName)\n .OrderByDescending(name => name, StringComparer.Ordinal);\n foreach (var name in names) {\n Console.WriteLine(name);\n }\n }\n}',
ruby:
'path = ARGV[0] || "."\nnames = Dir.entries(path).select { |name| File.file?(File.join(path, name)) }.sort.reverse\nnames.each { |name| puts name }',
},
fizzbuzz: {
rust:
"fn main() {\n for number in 1..=15 {\n if number % 15 == 0 {\n println!(\"FizzBuzz\");\n } else if number % 3 == 0 {\n println!(\"Fizz\");\n } else if number % 5 == 0 {\n println!(\"Buzz\");\n } else {\n println!(\"{number}\");\n }\n }\n}",
python:
"for number in range(1, 16):\n if number % 15 == 0:\n print(\"FizzBuzz\")\n elif number % 3 == 0:\n print(\"Fizz\")\n elif number % 5 == 0:\n print(\"Buzz\")\n else:\n print(number)",
javascript:
"for (let number = 1; number <= 15; number += 1) {\n if (number % 15 === 0) {\n console.log(\"FizzBuzz\");\n } else if (number % 3 === 0) {\n console.log(\"Fizz\");\n } else if (number % 5 === 0) {\n console.log(\"Buzz\");\n } else {\n console.log(number);\n }\n}",
typescript:
"for (let number = 1; number <= 15; number += 1) {\n if (number % 15 === 0) {\n console.log(\"FizzBuzz\");\n } else if (number % 3 === 0) {\n console.log(\"Fizz\");\n } else if (number % 5 === 0) {\n console.log(\"Buzz\");\n } else {\n console.log(number);\n }\n}",
go:
"package main\n\nimport \"fmt\"\n\nfunc main() {\n for number := 1; number <= 15; number++ {\n switch {\n case number%15 == 0:\n fmt.Println(\"FizzBuzz\")\n case number%3 == 0:\n fmt.Println(\"Fizz\")\n case number%5 == 0:\n fmt.Println(\"Buzz\")\n default:\n fmt.Println(number)\n }\n }\n}",
c:
"#include <stdio.h>\n\nint main(void) {\n for (int number = 1; number <= 15; number++) {\n if (number % 15 == 0) {\n puts(\"FizzBuzz\");\n } else if (number % 3 == 0) {\n puts(\"Fizz\");\n } else if (number % 5 == 0) {\n puts(\"Buzz\");\n } else {\n printf(\"%d\\n\", number);\n }\n }\n return 0;\n}",
cpp:
"#include <iostream>\n\nint main() {\n for (int number = 1; number <= 15; number++) {\n if (number % 15 == 0) {\n std::cout << \"FizzBuzz\\n\";\n } else if (number % 3 == 0) {\n std::cout << \"Fizz\\n\";\n } else if (number % 5 == 0) {\n std::cout << \"Buzz\\n\";\n } else {\n std::cout << number << '\\n';\n }\n }\n}",
java:
"public class Main {\n public static void main(String[] args) {\n for (int number = 1; number <= 15; number++) {\n if (number % 15 == 0) {\n System.out.println(\"FizzBuzz\");\n } else if (number % 3 == 0) {\n System.out.println(\"Fizz\");\n } else if (number % 5 == 0) {\n System.out.println(\"Buzz\");\n } else {\n System.out.println(number);\n }\n }\n }\n}",
csharp:
"using System;\n\nclass Program {\n static void Main() {\n for (int number = 1; number <= 15; number++) {\n if (number % 15 == 0) {\n Console.WriteLine(\"FizzBuzz\");\n } else if (number % 3 == 0) {\n Console.WriteLine(\"Fizz\");\n } else if (number % 5 == 0) {\n Console.WriteLine(\"Buzz\");\n } else {\n Console.WriteLine(number);\n }\n }\n }\n}",
ruby:
"(1..15).each do |number|\n if (number % 15).zero?\n puts \"FizzBuzz\"\n elsif (number % 3).zero?\n puts \"Fizz\"\n elsif (number % 5).zero?\n puts \"Buzz\"\n else\n puts number\n end\nend",
},
factorial: {
rust:
"fn main() {\n let mut result: u64 = 1;\n for number in 1..=5 {\n result *= number;\n }\n println!(\"{result}\");\n}",
python:
"result = 1\nfor number in range(1, 6):\n result *= number\nprint(result)",
javascript:
"let result = 1;\nfor (let number = 1; number <= 5; number += 1) {\n result *= number;\n}\nconsole.log(result);",
typescript:
"let result = 1;\nfor (let number = 1; number <= 5; number += 1) {\n result *= number;\n}\nconsole.log(result);",
go:
"package main\n\nimport \"fmt\"\n\nfunc main() {\n result := 1\n for number := 1; number <= 5; number++ {\n result *= number\n }\n fmt.Println(result)\n}",
c:
"#include <stdio.h>\n\nint main(void) {\n unsigned long long result = 1;\n for (int number = 1; number <= 5; number++) {\n result *= number;\n }\n printf(\"%llu\\n\", result);\n return 0;\n}",
cpp:
"#include <iostream>\n\nint main() {\n unsigned long long result = 1;\n for (int number = 1; number <= 5; number++) {\n result *= number;\n }\n std::cout << result << '\\n';\n}",
java:
"public class Main {\n public static void main(String[] args) {\n long result = 1;\n for (int number = 1; number <= 5; number++) {\n result *= number;\n }\n System.out.println(result);\n }\n}",
csharp:
"using System;\n\nclass Program {\n static void Main() {\n long result = 1;\n for (int number = 1; number <= 5; number++) {\n result *= number;\n }\n Console.WriteLine(result);\n }\n}",
ruby:
"result = (1..5).reduce(1, :*)\nputs result",
},
reverse_string: {
rust:
"fn main() {\n let text = \"hello\";\n let reversed: String = text.chars().rev().collect();\n println!(\"{reversed}\");\n}",
python:
"text = \"hello\"\nprint(text[::-1])",
javascript:
"const text = \"hello\";\nconsole.log(text.split(\"\").reverse().join(\"\"));",
typescript:
"const text: string = \"hello\";\nconsole.log(text.split(\"\").reverse().join(\"\"));",
go:
"package main\n\nimport \"fmt\"\n\nfunc main() {\n text := \"hello\"\n runes := []rune(text)\n for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {\n runes[i], runes[j] = runes[j], runes[i]\n }\n fmt.Println(string(runes))\n}",
c:
"#include <stdio.h>\n#include <string.h>\n\nint main(void) {\n char text[] = \"hello\";\n size_t length = strlen(text);\n for (size_t i = 0; i < length / 2; i++) {\n char temp = text[i];\n text[i] = text[length - 1 - i];\n text[length - 1 - i] = temp;\n }\n puts(text);\n return 0;\n}",
cpp:
"#include <algorithm>\n#include <iostream>\n#include <string>\n\nint main() {\n std::string text = \"hello\";\n std::reverse(text.begin(), text.end());\n std::cout << text << '\\n';\n}",
java:
"public class Main {\n public static void main(String[] args) {\n String text = \"hello\";\n System.out.println(new StringBuilder(text).reverse().toString());\n }\n}",
csharp:
"using System;\n\nclass Program {\n static void Main() {\n var text = \"hello\".ToCharArray();\n Array.Reverse(text);\n Console.WriteLine(new string(text));\n }\n}",
ruby:
"text = \"hello\"\nputs text.reverse",
},
sum_to_ten: {
rust:
"fn main() {\n let total: u32 = (1..=10).sum();\n println!(\"{total}\");\n}",
python:
"total = sum(range(1, 11))\nprint(total)",
javascript:
"let total = 0;\nfor (let number = 1; number <= 10; number += 1) {\n total += number;\n}\nconsole.log(total);",
typescript:
"let total = 0;\nfor (let number = 1; number <= 10; number += 1) {\n total += number;\n}\nconsole.log(total);",
go:
"package main\n\nimport \"fmt\"\n\nfunc main() {\n total := 0\n for number := 1; number <= 10; number++ {\n total += number\n }\n fmt.Println(total)\n}",
c:
"#include <stdio.h>\n\nint main(void) {\n int total = 0;\n for (int number = 1; number <= 10; number++) {\n total += number;\n }\n printf(\"%d\\n\", total);\n return 0;\n}",
cpp:
"#include <iostream>\n\nint main() {\n int total = 0;\n for (int number = 1; number <= 10; number++) {\n total += number;\n }\n std::cout << total << '\\n';\n}",
java:
"public class Main {\n public static void main(String[] args) {\n int total = 0;\n for (int number = 1; number <= 10; number++) {\n total += number;\n }\n System.out.println(total);\n }\n}",
csharp:
"using System;\n\nclass Program {\n static void Main() {\n int total = 0;\n for (int number = 1; number <= 10; number++) {\n total += number;\n }\n Console.WriteLine(total);\n }\n}",
ruby:
"total = (1..10).sum\nputs total",
},
// Issue #334: recursive `fibonacci` function evaluated at the 10th term (55).
fibonacci: {
rust:
"fn fibonacci(n: u64) -> u64 {\n if n <= 2 {\n 1\n } else {\n fibonacci(n - 1) + fibonacci(n - 2)\n }\n}\n\nfn main() {\n println!(\"{}\", fibonacci(10));\n}",
python:
"def fibonacci(n):\n if n <= 2:\n return 1\n return fibonacci(n - 1) + fibonacci(n - 2)\n\n\nprint(fibonacci(10))",
javascript:
"function fibonacci(n) {\n if (n <= 2) {\n return 1;\n }\n return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\nconsole.log(fibonacci(10));",
typescript:
"function fibonacci(n: number): number {\n if (n <= 2) {\n return 1;\n }\n return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\nconsole.log(fibonacci(10));",
go:
"package main\n\nimport \"fmt\"\n\nfunc fibonacci(n int) int {\n if n <= 2 {\n return 1\n }\n return fibonacci(n-1) + fibonacci(n-2)\n}\n\nfunc main() {\n fmt.Println(fibonacci(10))\n}",
c:
"#include <stdio.h>\n\nunsigned long long fibonacci(int n) {\n if (n <= 2) {\n return 1;\n }\n return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\nint main(void) {\n printf(\"%llu\\n\", fibonacci(10));\n return 0;\n}",
cpp:
"#include <iostream>\n\nunsigned long long fibonacci(int n) {\n if (n <= 2) {\n return 1;\n }\n return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\nint main() {\n std::cout << fibonacci(10) << '\\n';\n}",
java:
"public class Main {\n static long fibonacci(int n) {\n if (n <= 2) {\n return 1;\n }\n return fibonacci(n - 1) + fibonacci(n - 2);\n }\n\n public static void main(String[] args) {\n System.out.println(fibonacci(10));\n }\n}",
csharp:
"using System;\n\nclass Program {\n static long Fibonacci(int n) {\n if (n <= 2) {\n return 1;\n }\n return Fibonacci(n - 1) + Fibonacci(n - 2);\n }\n\n static void Main() {\n Console.WriteLine(Fibonacci(10));\n }\n}",
ruby:
"def fibonacci(n)\n return 1 if n <= 2\n\n fibonacci(n - 1) + fibonacci(n - 2)\nend\n\nputs fibonacci(10)",
},
};
function normalizeProgramPrompt(prompt) {
return normalizePrompt(String(prompt || "").replace(/c\+\+/gi, " cpp ").replace(/c#/gi, " csharp "));
}
// CJK scripts have no inter-word spaces, so the whitespace-based phrase/token
// matchers never isolate a CJK word. When the expected alias itself contains a
// CJK ideograph we fall back to a substring test (issue #312). Mirrors
// `coding::catalog::contains_cjk` on the Rust side.
function containsCjk(text) {
return /[㐀-䶿一-鿿豈--ヿ-ㄯ]/.test(
String(text || ""),
);
}
// Devanagari (Hindi, …) is written without spaces between words, so the
// whitespace-based phrase/token matchers never isolate a Devanagari word —
// exactly as for CJK above. Mirrors `coding::catalog::contains_devanagari` on
// the Rust side (range U+0900–U+097F): lets a handler split a role's word forms
// by script (Devanagari vs. Han) straight from the seed, so the head-final
// Hindi and Chinese extraction strategies never name a raw word (issue #386).
function containsDevanagari(text) {
return /[ऀ-ॿ]/.test(String(text || ""));
}
// Whether every character of `text` is ASCII. Mirrors Rust's `str::is_ascii`:
// the calculator-domain signal builder shapes a surface differently by script,
// matching an ASCII code on a word boundary but a non-ASCII surface as a raw
// substring (issue #386).
function isAsciiText(text) {
return /^[\x00-\x7f]*$/.test(String(text || ""));
}
function containsProgramToken(normalized, token) {
if (containsCjk(token)) return String(normalized || "").includes(token);
return String(normalized || "")
.split(/\s+/)
.includes(token);
}
function containsProgramPhrase(normalized, phrase) {
if (containsCjk(phrase)) return normalized.includes(phrase);
return (
normalized === phrase ||
normalized.startsWith(`${phrase} `) ||
normalized.endsWith(` ${phrase}`) ||
normalized.includes(` ${phrase} `)
);
}
function programTaskFromPrompt(normalized) {
// The request phrasings live in each task's `program_task_<slug>` meaning, not
// inline on WRITE_PROGRAM_TASKS — read them by slug (issue #386). Declaration
// order (Object.keys) is the catalog priority order, identical to Rust.
return (
Object.keys(WRITE_PROGRAM_TASKS).find((slug) =>
wordsForMeaning(`program_task_${slug}`).some((alias) =>
containsProgramPhrase(normalized, alias),
),
) || null
);
}
// Issue #386: the function words that introduce an *unknown* implementation
// language ("write a program in <name>", "на языке <name>") are seed data, not
// literals baked into the worker. Mirror src/intent_formalization.rs by sourcing
// the head-initial English/Russian surfaces of the target-preposition and
// "language" noun roles from the lexicon. The WRITE_PROGRAM_LANGUAGES catalog
// scan below already resolves every *known* language across all four supported
// languages; this fallback only reads the bare name trailing the marker, so it
// consults the two head-initial languages whose name follows the marker (the
// head-final Hindi/Chinese surfaces are carried in the seed for coverage but
// place the name before the marker, which this scan does not chase).
const ROLE_IMPLEMENTATION_LANGUAGE_PREPOSITION =
"implementation_language_preposition";
const ROLE_IMPLEMENTATION_LANGUAGE_NOUN = "implementation_language_noun";
function programLanguageFromPrompt(normalized) {
const tokens = normalized.split(/\s+/).filter(Boolean);
// Each language's alias surfaces live in its `program_language_<slug>` meaning,
// not inline on WRITE_PROGRAM_LANGUAGES — read them by slug (issue #386). Names
// are single tokens, matched on whitespace boundaries exactly as in Rust.
for (const slug of Object.keys(WRITE_PROGRAM_LANGUAGES)) {
const surfaces = wordsForMeaning(`program_language_${slug}`);
if (surfaces.some((alias) => tokens.includes(alias))) return slug;
}
const prepositionSurfaces = wordsForRoleInLanguages(
ROLE_IMPLEMENTATION_LANGUAGE_PREPOSITION,
["en", "ru"],
);
const languageNounSurfaces = wordsForRoleInLanguages(
ROLE_IMPLEMENTATION_LANGUAGE_NOUN,
["en", "ru"],
);
for (let index = 0; index < tokens.length - 1; index += 1) {
if (!prepositionSurfaces.includes(tokens[index])) continue;
if (languageNounSurfaces.includes(tokens[index + 1])) {
return tokens[index + 2] || null;
}
return tokens[index + 1];
}
return null;
}
// ---------------------------------------------------------------------------
// Issue #324 R4/R7: the program-modification step as a data-driven Links
// Notation substitution pipeline. This mirrors `src/program_plan.rs` (the
// pipeline) and `src/substitution.rs` (the engine). The rule text below is
// byte-identical to `data/seed/program-plan-rules.lino`; the parity experiment
// (`experiments/issue-324-js-worker.mjs`) keeps the two copies in lockstep.
//
// Adding a new modifier transform is rule data; recognition is constrained by
// the operation slugs declared in that rule data.
// ---------------------------------------------------------------------------
const PROGRAM_PLAN_RULES_LINO = [
"substitution_rules",
' id "program_plan_rules"',
' rule "path_argument_list_files"',
' order "10"',
' event "manual"',
' when "request:modifier -> path_argument"',
' replace "request:task -> list_files"',
' with "request:task -> list_files_arg"',
' rule "path_argument_list_files_reverse_sort"',
' order "10"',
' event "manual"',
' when "request:modifier -> path_argument"',
' replace "request:task -> list_files_reverse_sort"',
' with "request:task -> list_files_arg_reverse_sort"',
' rule "reverse_sort_list_files"',
' order "20"',
' event "manual"',
' when "request:modifier -> reverse_sort"',
' replace "request:task -> list_files"',
' with "request:task -> list_files_reverse_sort"',
' rule "reverse_sort_list_files_arg"',
' order "20"',
' event "manual"',
' when "request:modifier -> reverse_sort"',
' replace "request:task -> list_files_arg"',
' with "request:task -> list_files_arg_reverse_sort"',
"",
].join("\n");
const TASK_NODE = "request:task";
const MODIFIER_NODE = "request:modifier";
// Issue #386: the FULL multilingual operation vocabulary, embedded inline and
// byte-identical to data/seed/operation-vocabulary.lino (regenerated by
// experiments/issue-386-sync-worker-lexicon.mjs). The JS mirror of
// OperationVocabulary in src/seed/operation_vocabulary.rs: the CODE matches
// canonical concept tokens (write / function / similar_elements / reverse_sort …),
// while the per-language surface phrases live once here in the seed data — never
// a hardcoded per-language word list in code.
const OPERATION_VOCABULARY_LINO = [
"operation_vocabulary",
' operation "uppercase"',
' language "en"',
' phrase "uppercase"',
' phrase "upper case"',
' language "ru"',
' phrase "верхний регистр"',
' phrase "заглавными буквами"',
' phrase "прописными буквами"',
' combo "верхний+регистр"',
' language "hi"',
' phrase "बड़े अक्षर"',
' phrase "अपरकेस"',
' language "zh"',
' phrase "大写"',
' operation "lowercase"',
' language "en"',
' phrase "lowercase"',
' phrase "lower case"',
' language "ru"',
' phrase "нижний регистр"',
' phrase "строчными буквами"',
' combo "нижний+регистр"',
' language "hi"',
' phrase "छोटे अक्षर"',
' phrase "लोअरकेस"',
' language "zh"',
' phrase "小写"',
' operation "replace"',
' language "en"',
' phrase "replace"',
' language "ru"',
' phrase "замен"',
' language "hi"',
' phrase "बदल"',
' language "zh"',
' phrase "替换"',
' operation "reverse_words"',
' language "en"',
' phrase "reverse words"',
' phrase "reverse the words"',
' language "ru"',
' phrase "обратный порядок слов"',
' combo "переверни+слова"',
' combo "обрати+слова"',
' combo "разверни+слова"',
' language "hi"',
' phrase "शब्दों को उल्टा"',
' combo "शब्द+उल्टा"',
' language "zh"',
' phrase "反转单词"',
' phrase "倒转单词"',
' phrase "颠倒单词"',
' operation "extract_email"',
' language "en"',
' combo "extract+email"',
' combo "extract+e-mail"',
' language "ru"',
' combo "извлеки+имейл"',
' combo "извлеки+почт"',
' combo "извлечь+имейл"',
' combo "найди+имейл"',
' language "hi"',
' combo "ईमेल+निकाल"',
' language "zh"',
' phrase "提取邮箱"',
' phrase "提取电子邮件"',
' phrase "提取邮件"',
' operation "count_occurrences"',
' language "en"',
' phrase "count occurrences"',
' language "ru"',
' phrase "количество вхождений"',
' combo "посчитай+вхождени"',
' combo "подсчитай+вхождени"',
' combo "сколько+раз"',
' language "hi"',
' phrase "कितनी बार"',
' combo "बार+गिन"',
' language "zh"',
' phrase "出现次数"',
' phrase "统计出现"',
' phrase "计算出现"',
' operation "count_unique_words"',
' language "en"',
' phrase "count unique words"',
' phrase "count distinct words"',
' language "ru"',
' phrase "количество уникальных слов"',
' combo "уникальн+слов"',
' combo "различн+слов"',
' language "hi"',
' combo "अद्वितीय+शब्द"',
' combo "विशिष्ट+शब्द"',
' language "zh"',
' phrase "唯一单词"',
' phrase "不同单词"',
' phrase "统计唯一"',
' operation "deduplicate_lines"',
' language "en"',
' phrase "deduplicate lines"',
' phrase "dedupe lines"',
' combo "deduplicate+line"',
' language "ru"',
' combo "дубликат+строк"',
' combo "дедупликац+строк"',
' language "hi"',
' combo "डुप्लिकेट+लाइन"',
' combo "दोहराव+लाइन"',
' language "zh"',
' phrase "去重行"',
' phrase "行去重"',
' phrase "删除重复行"',
' operation "sort"',
' language "en"',
' phrase "sort"',
' language "ru"',
' phrase "сортир"',
' phrase "упорядоч"',
' language "hi"',
' phrase "क्रमबद्ध"',
' phrase "क्रमित"',
' phrase "सॉर्ट"',
' language "zh"',
' phrase "排序"',
' phrase "排列"',
' operation "sort_lines"',
' language "en"',
' phrase "sort lines"',
' phrase "sort the lines"',
' combo "sort+line"',
' language "ru"',
' combo "сортир+строк"',
' language "hi"',
' combo "लाइन+क्रमबद्ध"',
' combo "लाइनों+क्रमबद्ध"',
' language "zh"',
' phrase "排序行"',
' phrase "行排序"',
' phrase "对行排序"',
' operation "path_argument"',
' language "en"',
' phrase "path argument"',
' phrase "path as an argument"',
' combo "path+argument"',
' language "ru"',
' combo "путь+аргумент"',
' combo "путь+аргумента"',
' combo "путь+аргументом"',
' language "hi"',
' phrase "पथ को तर्क"',
' combo "पथ+तर्क"',
' language "zh"',
' phrase "路径作为参数"',
' combo "路径+参数"',
' operation "reverse_sort"',
' language "en"',
' phrase "reverse sort"',
' phrase "reverse sorted"',
' phrase "sort in reverse order"',
' phrase "sort the results in reverse order"',
' phrase "reverse order"',
' phrase "descending order"',
' combo "sort+reverse"',
' combo "sort+descending"',
' language "ru"',
' phrase "в обратном порядке"',
' combo "сортиров+обратн"',
' combo "отсортир+обратн"',
' language "hi"',
' phrase "उल्टे क्रम"',
' combo "क्रमबद्ध+उल्टे"',
' combo "क्रमबद्ध+उल्टा"',
' language "zh"',
' phrase "相反顺序排序"',
' combo "排序+相反"',
' combo "排序+反向"',
' combo "排序+倒序"',
' operation "cancel_reverse_sort"',
' inverse "reverse_sort"',
' language "en"',
' phrase "cancel sort"',
' phrase "cancel the sort"',
' phrase "cancel sorting"',
' phrase "cancel the sorting"',
' phrase "undo sort"',
' phrase "undo the sort"',
' phrase "undo sorting"',
' phrase "undo the sorting"',
' phrase "remove sort"',
' phrase "remove the sort"',
' phrase "remove sorting"',
' phrase "remove the sorting"',
' phrase "no sorting"',
' phrase "without sorting"',
' combo "cancel+sort"',
' combo "undo+sort"',
' combo "remove+sort"',
' language "ru"',
' phrase "отмени сортировку"',
' phrase "отмените сортировку"',
' phrase "убери сортировку"',
' phrase "уберите сортировку"',
' phrase "без сортировки"',
' combo "отмени+сортиров"',
' combo "отмените+сортиров"',
' combo "отменить+сортиров"',
' combo "убери+сортиров"',
' combo "уберите+сортиров"',
' combo "убрать+сортиров"',
' combo "без+сортиров"',
' language "hi"',
' phrase "सॉर्ट हटाओ"',
' phrase "क्रम हटाओ"',
' phrase "सॉर्ट रद्द करो"',
' combo "सॉर्ट+हटा"',
' combo "क्रमबद्ध+हटा"',
' combo "सॉर्ट+रद्द"',
' language "zh"',
' phrase "取消排序"',
' phrase "撤销排序"',
' phrase "去掉排序"',
' phrase "取消排序顺序"',
' combo "取消+排序"',
' combo "撤销+排序"',
' operation "function"',
' language "en"',
' phrase "function"',
' language "ru"',
' phrase "функция"',
' phrase "функцию"',
' phrase "функции"',
' language "hi"',
' phrase "फ़ंक्शन"',
' phrase "फंक्शन"',
' language "zh"',
' phrase "函数"',
' operation "implement"',
' language "en"',
' phrase "implement"',
' language "ru"',
' phrase "реализуй"',
' phrase "реализовать"',
' phrase "реализуйте"',
' language "hi"',
' phrase "लागू करें"',
' phrase "लागू करो"',
' language "zh"',
' phrase "实现"',
' operation "write"',
' language "en"',
' phrase "write"',
' language "ru"',
' phrase "напиши"',
' phrase "напишите"',
' phrase "написать"',
' language "hi"',
' phrase "लिखें"',
' phrase "लिखो"',
' language "zh"',
' phrase "编写"',
' phrase "写"',
' operation "return"',
' language "en"',
' phrase "return"',
' language "ru"',
' phrase "верни"',
' phrase "верните"',
' phrase "возвращает"',
' language "hi"',
' phrase "लौटाएँ"',
' phrase "लौटाएं"',
' phrase "वापस करें"',
' language "zh"',
' phrase "返回"',
' operation "tuple"',
' language "en"',
' phrase "tuple"',
' language "ru"',
' phrase "кортеж"',
' language "hi"',
' phrase "टपल"',
' language "zh"',
' phrase "元组"',
' operation "numbers"',
' language "en"',
' phrase "numbers"',
' phrase "number"',
' language "ru"',
' phrase "числа"',
' phrase "чисел"',
' language "hi"',
' phrase "संख्याएँ"',
' phrase "संख्याएं"',
' phrase "संख्या"',
' language "zh"',
' phrase "数字"',
' phrase "数值"',
' operation "vowels"',
' language "en"',
' phrase "vowels"',
' phrase "vowel"',
' language "ru"',
' phrase "гласные"',
' phrase "гласных"',
' language "hi"',
' phrase "स्वर"',
' phrase "स्वरों"',
' language "zh"',
' phrase "元音"',
' operation "count_vowels"',
' language "en"',
' phrase "count vowels"',
' phrase "number of vowels"',
' phrase "count_vowels"',
' language "ru"',
' phrase "посчитай гласные"',
' phrase "количество гласных"',
' language "hi"',
' phrase "स्वर गिनें"',
' phrase "स्वरों की संख्या"',
' language "zh"',
' phrase "统计元音"',
' phrase "元音数量"',
' operation "similar_elements"',
' language "en"',
' phrase "similar elements"',
' phrase "similar_elements"',
' language "ru"',
' phrase "общие элементы"',
' phrase "похожие элементы"',
' language "hi"',
' phrase "समान तत्व"',
' language "zh"',
' phrase "相同元素"',
' phrase "相似元素"',
' operation "distinct_numbers"',
' language "en"',
' phrase "distinct numbers"',
' language "ru"',
' phrase "различные числа"',
' phrase "разных числа"',
' language "hi"',
' phrase "अलग संख्याएँ"',
' phrase "अलग संख्याएं"',
' language "zh"',
' phrase "不同数字"',
' phrase "不同数值"',
' operation "differ"',
' language "en"',
' phrase "differ"',
' language "ru"',
' phrase "отличаются"',
' phrase "разница"',
' language "hi"',
' phrase "अंतर"',
' language "zh"',
' phrase "相差"',
' phrase "差"',
' operation "threshold"',
' language "en"',
' phrase "threshold"',
' language "ru"',
' phrase "порог"',
' phrase "порога"',
' language "hi"',
' phrase "सीमा"',
' language "zh"',
' phrase "阈值"',
' operation "reverse"',
' language "en"',
' phrase "reverse"',
' phrase "flip"',
' language "ru"',
' phrase "переверни"',
' phrase "перевернуть"',
' phrase "разверни"',
' phrase "развернуть"',
' phrase "задом наперёд"',
' language "hi"',
' phrase "उलट"',
' phrase "पलट"',
' language "zh"',
' phrase "反转"',
' phrase "倒转"',
' phrase "颠倒"',
' operation "sum"',
' language "en"',
' phrase "sum of"',
' phrase "sum the"',
' phrase "sum up"',
' phrase "total of"',
' combo "add+up"',
' language "ru"',
' phrase "сумм"',
' phrase "сложи"',
' phrase "складыва"',
' phrase "суммируй"',
' language "hi"',
' phrase "योग"',
' phrase "जोड़"',
' language "zh"',
' phrase "求和"',
' phrase "总和"',
' phrase "相加"',
' phrase "之和"',
' operation "product"',
' language "en"',
' phrase "product of"',
' phrase "multiply"',
' combo "multiply+together"',
' language "ru"',
' phrase "произведение"',
' phrase "перемнож"',
' phrase "умнож"',
' language "hi"',
' phrase "गुणनफल"',
' phrase "गुणा"',
' language "zh"',
' phrase "乘积"',
' phrase "相乘"',
' phrase "之积"',
' operation "minimum"',
' language "en"',
' phrase "minimum"',
' phrase "smallest"',
' phrase "lowest"',
' language "ru"',
' phrase "минимум"',
' phrase "минимальн"',
' phrase "наименьш"',
' language "hi"',
' phrase "न्यूनतम"',
' phrase "सबसे छोटी"',
' phrase "सबसे छोटा"',
' language "zh"',
' phrase "最小"',
' operation "maximum"',
' language "en"',
' phrase "maximum"',
' phrase "largest"',
' phrase "biggest"',
' phrase "highest"',
' language "ru"',
' phrase "максимум"',
' phrase "максимальн"',
' phrase "наибольш"',
' language "hi"',
' phrase "अधिकतम"',
' phrase "सबसे बड़ी"',
' phrase "सबसे बड़ा"',
' language "zh"',
' phrase "最大"',
' operation "code_request"',
' language "en"',
' phrase "code"',
' phrase "program"',
' phrase "script"',
' phrase "snippet"',
' language "ru"',
' phrase "код"',
' phrase "программ"',
' phrase "скрипт"',
' language "hi"',
' phrase "कोड"',
' phrase "प्रोग्राम"',
' phrase "स्क्रिप्ट"',
' language "zh"',
' phrase "代码"',
' phrase "代碼"',
' phrase "程序"',
' phrase "程式"',
].join("\n");
let cachedOperationVocabulary = null;
// Parse the embedded operation vocabulary into language-pooled triggers
// ({ slug, phrases, combos, inverse? }). Mirrors operation_vocabulary() in
// src/seed/operation_vocabulary.rs; phrases/combos are pooled across every
// language because operationFormMatches (below) is language-agnostic.
function operationVocabulary() {
if (cachedOperationVocabulary) return cachedOperationVocabulary;
const root = parseLinoTree(OPERATION_VOCABULARY_LINO);
const container =
root.children.find((child) => child.name === "operation_vocabulary") || root;
const operations = [];
for (const operationNode of container.children) {
if (operationNode.name !== "operation") continue;
const phrases = [];
const combos = [];
let inverse = null;
for (const child of operationNode.children) {
if (child.name === "inverse") inverse = child.value;
else if (child.name === "language") {
for (const form of child.children) {
if (form.name === "phrase") phrases.push(form.value);
else if (form.name === "combo") {
combos.push(
form.value
.split("+")
.map((token) => token.trim())
.filter(Boolean),
);
}
}
}
}
const operation = { slug: operationNode.value, phrases, combos };
if (inverse) operation.inverse = inverse;
operations.push(operation);
}
cachedOperationVocabulary = operations;
return cachedOperationVocabulary;
}
let cachedProgramModifierSlugs = null;
function literalPatternValue(node) {
return node && node.kind === "literal" ? node.value : null;
}
function programModifierSlugs() {
if (cachedProgramModifierSlugs) return cachedProgramModifierSlugs;
const slugs = new Set();
for (const rule of programPlanRules().rules) {
for (const condition of rule.conditions || []) {
const from = literalPatternValue(condition.from);
const to = literalPatternValue(condition.to);
if (from === MODIFIER_NODE && to) slugs.add(to);
}
}
cachedProgramModifierSlugs = slugs;
return slugs;
}
function operationFormMatches(normalized, operation) {
const source = String(normalized || "");
return (
(operation.phrases || []).some((phrase) => source.includes(phrase)) ||
(operation.combos || []).some((combo) =>
combo.every((token) => source.includes(String(token || ""))),
)
);
}
// Issue #386: every canonical operation token whose phrasing appears in
// `normalized`, in declaration order. Mirrors OperationVocabulary::detect in
// src/seed/operation_vocabulary.rs (substring match, so a native verb still
// matches when punctuation such as the danda `।` is glued to it).
function detectOperations(normalized) {
const detected = [];
for (const operation of operationVocabulary()) {
if (operationFormMatches(normalized, operation)) detected.push(operation.slug);
}
return detected;
}
// Issue #386: append canonical English operation tokens to a normalized prompt so
// handlers keep matching canonical concepts while accepting native verbs from
// data/seed/operation-vocabulary.lino. Mirrors
// OperationVocabulary::canonicalized_prompt — additive (never removes text), so
// English prompts are unchanged and multilingual prompts gain boundary-matchable
// tokens (e.g. a Hindi "लिखें।" yields an appended " write").
function canonicalizedPrompt(normalized) {
const detected = detectOperations(normalized);
if (!detected.length) return String(normalized || "");
let out = String(normalized || "");
for (const canonical of detected) {
out += ` ${canonical}`;
const phrase = canonical.replace(/_/g, " ");
if (phrase !== canonical) out += ` ${phrase}`;
}
return out;
}
// Issue #386: the language-independent *meaning* lexicon — the JS mirror of
// `data/seed/meanings.lino` and `src/seed/meanings.rs`. Recognition references
// semantic *roles* (which surface words evidence a program artifact / a program
// modification, in any language), never a hardcoded per-language word list. This
// is an inline copy generated from the canonical seed by
// `scripts/migrate-meaning-seed.rs` (the same convention as
// PROGRAM_PLAN_RULES_LINO) so the worker stays self-contained when no seed has
// been fetched; parity is guarded by `experiments/issue-386-js-cancel-sort.mjs`.
const MEANINGS_LINO = [
"meanings",
" result",
" defined-by output",
" role program_artifact",
" lexeme en",
" surface",
" text result",
" surface",
" text results",
" lexeme ru",
" surface",
" text результат",
" surface",
" text результата",
" surface",
" text результаты",
" surface",
" text результатов",
" lexeme hi",
" surface",
" text परिणाम",
" surface",
" text परिणामों",
" surface",
" text नतीजा",
" surface",
" text नतीजे",
" lexeme zh",
" surface",
" text 结果",
" output",
" defined-by result",
" defined-by program",
" role program_artifact",
" lexeme en",
" surface",
" text output",
" lexeme ru",
" surface",
" text вывод",
" lexeme hi",
" surface",
" text आउटपुट",
" lexeme zh",
" surface",
" text 输出",
" program",
" grounded-in Q40056",
" defined-by code",
" defined-by entity",
" role program_artifact",
" role program_kind",
" role program_genus",
" lexeme en",
" surface",
" text program",
" surface",
" text programme",
" lexeme ru",
" surface",
" text программа",
" surface",
" text программу",
" surface",
" text программе",
" surface",
" text программы",
" surface",
" text программку",
" lexeme hi",
" surface",
" text प्रोग्राम",
" lexeme zh",
" surface",
" text 程序",
" script",
" defined-by program",
" defined-by code",
" role program_artifact",
" role program_kind",
" role script_or_code_artifact",
" lexeme en",
" surface",
" text script",
" lexeme ru",
" surface",
" text скрипт",
" lexeme hi",
" surface",
" text स्क्रिप्ट",
" lexeme zh",
" surface",
" text 脚本",
" code",
" grounded-in Q128751",
" defined-by program",
" role program_artifact",
" role program_kind",
" role script_or_code_artifact",
" lexeme en",
" surface",
" text code",
" lexeme ru",
" surface",
" text код",
" surface",
" text кода",
" surface",
" text коде",
" lexeme hi",
" surface",
" text कोड",
" lexeme zh",
" surface",
" text 代码",
" hello_world",
" defined-by program",
" defined-by code",
" role hello_world_reference",
" lexeme en",
" surface",
" text \"hello world\"",
" lexeme ru",
" surface",
" text \"привет мир\"",
" surface",
" text \"хелло ворлд\"",
" lexeme hi",
" surface",
" text \"नमस्ते दुनिया\"",
" surface",
" text \"हैलो वर्ल्ड\"",
" lexeme zh",
" surface",
" text 你好世界",
" surface",
" text 你好,世界",
" sort",
" grounded-in Q2303697",
" defined-by order",
" defined-by action",
" role program_artifact",
" role program_modification",
" lexeme en",
" surface",
" text sort",
" surface",
" text sorts",
" surface",
" text sorting",
" surface",
" text sorted",
" lexeme ru",
" surface",
" text сортировка",
" surface",
" text сортировку",
" surface",
" text сортировки",
" surface",
" text сортировке",
" surface",
" text сортировкой",
" surface",
" text сортировать",
" surface",
" text отсортируй",
" surface",
" text отсортируйте",
" surface",
" text отсортировать",
" lexeme hi",
" surface",
" text सॉर्ट",
" surface",
" text क्रमबद्ध",
" lexeme zh",
" surface",
" text 排序",
" order",
" defined-by sort",
" role program_artifact",
" role program_modification",
" lexeme en",
" surface",
" text order",
" surface",
" text ordering",
" surface",
" text reorder",
" lexeme ru",
" surface",
" text порядок",
" surface",
" text упорядочи",
" lexeme hi",
" surface",
" text क्रम",
" lexeme zh",
" surface",
" text 顺序",
" reverse",
" defined-by order",
" role program_modification",
" lexeme en",
" surface",
" text reverse",
" surface",
" text reversed",
" lexeme ru",
" surface",
" text обратный",
" surface",
" text обратном",
" surface",
" text обратную",
" surface",
" text обратного",
" lexeme hi",
" surface",
" text उल्टा",
" surface",
" text उल्टे",
" lexeme zh",
" surface",
" text 反向",
" surface",
" text 相反",
" surface",
" text 倒序",
" cancel",
" defined-by reverse",
" defined-by modify",
" role program_modification",
" lexeme en",
" surface",
" text cancel",
" surface",
" text undo",
" surface",
" text remove",
" surface",
" text revert",
" lexeme ru",
" surface",
" text отмени",
" surface",
" text отмените",
" surface",
" text отменить",
" surface",
" text убери",
" surface",
" text уберите",
" surface",
" text убрать",
" lexeme hi",
" surface",
" text रद्द",
" surface",
" text हटाओ",
" surface",
" text हटाएं",
" surface",
" text हटा",
" lexeme zh",
" surface",
" text 取消",
" surface",
" text 撤销",
" surface",
" text 去掉",
" surface",
" text 去除",
" modify",
" defined-by update",
" defined-by action",
" role program_modification",
" lexeme en",
" surface",
" text change",
" surface",
" text modify",
" surface",
" text alter",
" lexeme ru",
" surface",
" text измени",
" surface",
" text изменить",
" surface",
" text измените",
" lexeme hi",
" surface",
" text बदलें",
" surface",
" text बदलो",
" lexeme zh",
" surface",
" text 修改",
" surface",
" text 改",
" update",
" defined-by modify",
" role program_modification",
" lexeme en",
" surface",
" text update",
" lexeme ru",
" surface",
" text обнови",
" surface",
" text обновить",
" surface",
" text обновите",
" lexeme hi",
" surface",
" text अपडेट",
" lexeme zh",
" surface",
" text 更新",
" make",
" defined-by program",
" defined-by output",
" role program_modification",
" role program_request",
" role software_authoring_action",
" lexeme en",
" surface",
" text make",
" lexeme ru",
" surface",
" text сделай",
" surface",
" text сделайте",
" surface",
" text сделать",
" lexeme hi",
" surface",
" text बनाओ",
" surface",
" text बनाएं",
" lexeme zh",
" surface",
" text 制作",
" function",
" defined-by code",
" defined-by program",
" role program_artifact",
" role program_kind",
" lexeme en",
" surface",
" text function",
" surface",
" text func",
" lexeme ru",
" surface",
" text функция",
" surface",
" text функцию",
" lexeme hi",
" surface",
" text फ़ंक्शन",
" surface",
" text फंक्शन",
" lexeme zh",
" surface",
" text 函数",
" write",
" defined-by code",
" defined-by program",
" role program_request",
" role software_authoring_action",
" role script_authoring_verb",
" lexeme en",
" surface",
" text write",
" lexeme ru",
" surface",
" text напиши",
" surface",
" text напишите",
" surface",
" text написать",
" lexeme hi",
" surface",
" text लिखो",
" surface",
" text लिखें",
" lexeme zh",
" surface",
" text 编写",
" surface",
" text 写",
" create",
" defined-by make",
" defined-by program",
" role program_request",
" role software_authoring_action",
" lexeme en",
" surface",
" text create",
" lexeme ru",
" surface",
" text создай",
" surface",
" text создайте",
" lexeme hi",
" surface",
" text बनाओ",
" surface",
" text बनाएं",
" lexeme zh",
" surface",
" text 创建",
" show",
" defined-by output",
" defined-by result",
" role program_request",
" lexeme en",
" surface",
" text show",
" lexeme ru",
" surface",
" text покажи",
" surface",
" text покажите",
" lexeme hi",
" surface",
" text दिखाओ",
" surface",
" text दिखाएं",
" lexeme zh",
" surface",
" text 显示",
" generate",
" defined-by output",
" defined-by program",
" role program_request",
" role software_authoring_action",
" lexeme en",
" surface",
" text generate",
" lexeme ru",
" surface",
" text сгенерируй",
" surface",
" text сгенерируйте",
" lexeme hi",
" surface",
" text उत्पन्न",
" surface",
" text जनरेट",
" lexeme zh",
" surface",
" text 生成",
" build",
" defined-by make",
" defined-by program",
" role program_request",
" role software_authoring_action",
" lexeme en",
" surface",
" text build",
" lexeme ru",
" surface",
" text построй",
" surface",
" text собери",
" lexeme hi",
" surface",
" text बनाओ",
" surface",
" text बनाएं",
" lexeme zh",
" surface",
" text 构建",
"meanings",
" quantity",
" grounded-in Q309314",
" defined-by unit",
" defined-by property",
" defined-by amount",
" defined-by size",
" role measurement_concept",
" lexeme en",
" surface",
" text quantity",
" lexeme ru",
" surface",
" text количество",
" lexeme hi",
" surface",
" text मात्रा",
" lexeme zh",
" surface",
" text 数量",
" unit",
" grounded-in Q47574",
" defined-by quantity",
" role measurement_concept",
" lexeme en",
" surface",
" text unit",
" lexeme ru",
" surface",
" text единица",
" lexeme hi",
" surface",
" text इकाई",
" lexeme zh",
" surface",
" text 单位",
" cardinal_number",
" grounded-in Q1329258",
" defined-by quantity",
" role numeric_concept",
" lexeme en",
" surface",
" text \"cardinal number\"",
" surface",
" text number",
" lexeme ru",
" surface",
" text \"количественное число\"",
" lexeme hi",
" surface",
" text \"गणन संख्या\"",
" lexeme zh",
" surface",
" text 基数",
" zero",
" grounded-in Q204",
" defined-by cardinal_number",
" defined-by empty",
" role cardinal_number_word",
" lexeme en",
" surface",
" text zero",
" surface",
" text 0",
" lexeme ru",
" surface",
" text ноль",
" surface",
" text нуль",
" lexeme hi",
" surface",
" text शून्य",
" lexeme zh",
" surface",
" text 零",
" one",
" grounded-in Q199",
" defined-by cardinal_number",
" defined-by single",
" role cardinal_number_word",
" lexeme en",
" surface",
" text one",
" surface",
" text 1",
" lexeme ru",
" surface",
" text один",
" surface",
" text одна",
" surface",
" text одно",
" lexeme hi",
" surface",
" text एक",
" lexeme zh",
" surface",
" text 一",
" two",
" grounded-in Q200",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text two",
" surface",
" text 2",
" lexeme ru",
" surface",
" text два",
" surface",
" text две",
" lexeme hi",
" surface",
" text दो",
" lexeme zh",
" surface",
" text 二",
" three",
" grounded-in Q201",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text three",
" surface",
" text 3",
" lexeme ru",
" surface",
" text три",
" lexeme hi",
" surface",
" text तीन",
" lexeme zh",
" surface",
" text 三",
" four",
" grounded-in Q202",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text four",
" surface",
" text 4",
" lexeme ru",
" surface",
" text четыре",
" lexeme hi",
" surface",
" text चार",
" lexeme zh",
" surface",
" text 四",
" five",
" grounded-in Q203",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text five",
" surface",
" text 5",
" lexeme ru",
" surface",
" text пять",
" lexeme hi",
" surface",
" text पाँच",
" lexeme zh",
" surface",
" text 五",
" six",
" grounded-in Q23488",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text six",
" surface",
" text 6",
" lexeme ru",
" surface",
" text шесть",
" lexeme hi",
" surface",
" text छह",
" lexeme zh",
" surface",
" text 六",
" seven",
" grounded-in Q23350",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text seven",
" surface",
" text 7",
" lexeme ru",
" surface",
" text семь",
" lexeme hi",
" surface",
" text सात",
" lexeme zh",
" surface",
" text 七",
" eight",
" grounded-in Q23355",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text eight",
" surface",
" text 8",
" lexeme ru",
" surface",
" text восемь",
" lexeme hi",
" surface",
" text आठ",
" lexeme zh",
" surface",
" text 八",
" nine",
" grounded-in Q19108",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text nine",
" surface",
" text 9",
" lexeme ru",
" surface",
" text девять",
" lexeme hi",
" surface",
" text नौ",
" lexeme zh",
" surface",
" text 九",
" ten",
" grounded-in Q23806",
" defined-by cardinal_number",
" role cardinal_number_word",
" lexeme en",
" surface",
" text ten",
" surface",
" text 10",
" lexeme ru",
" surface",
" text десять",
" lexeme hi",
" surface",
" text दस",
" lexeme zh",
" surface",
" text 十",
" length",
" grounded-in Q36253",
" defined-by quantity",
" role physical_dimension",
" lexeme en",
" surface",
" text length",
" lexeme ru",
" surface",
" text длина",
" lexeme hi",
" surface",
" text लंबाई",
" lexeme zh",
" surface",
" text 长度",
" data_storage",
" grounded-in Q105666562",
" defined-by quantity",
" role physical_dimension",
" lexeme en",
" surface",
" text \"data storage\"",
" lexeme ru",
" surface",
" text \"хранение данных\"",
" lexeme hi",
" surface",
" text \"डेटा भंडारण\"",
" lexeme zh",
" surface",
" text 数据存储",
" mass",
" grounded-in Q11423",
" defined-by quantity",
" role physical_dimension",
" lexeme en",
" surface",
" text mass",
" lexeme ru",
" surface",
" text масса",
" lexeme hi",
" surface",
" text द्रव्यमान",
" lexeme zh",
" surface",
" text 质量",
" time",
" grounded-in Q11471",
" defined-by quantity",
" role physical_dimension",
" lexeme en",
" surface",
" text time",
" lexeme ru",
" surface",
" text время",
" lexeme hi",
" surface",
" text समय",
" lexeme zh",
" surface",
" text 时间",
" temperature",
" grounded-in Q11466",
" defined-by quantity",
" role physical_dimension",
" lexeme en",
" surface",
" text temperature",
" lexeme ru",
" surface",
" text температура",
" lexeme hi",
" surface",
" text तापमान",
" lexeme zh",
" surface",
" text 温度",
" meter",
" grounded-in Q11573",
" defined-by length",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text meter",
" surface",
" text metre",
" surface",
" text meters",
" surface",
" text metres",
" lexeme ru",
" surface",
" text метр",
" surface",
" text метра",
" surface",
" text метров",
" lexeme hi",
" surface",
" text मीटर",
" lexeme zh",
" surface",
" text 米",
" kilometer",
" grounded-in Q828224",
" defined-by length",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text km",
" surface",
" text kilometer",
" surface",
" text kilometre",
" surface",
" text kilometers",
" surface",
" text kilometres",
" lexeme ru",
" surface",
" text километр",
" surface",
" text километра",
" surface",
" text километров",
" lexeme hi",
" surface",
" text किलोमीटर",
" lexeme zh",
" surface",
" text 千米",
" surface",
" text 公里",
" centimeter",
" grounded-in Q174728",
" defined-by length",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text cm",
" surface",
" text centimeter",
" surface",
" text centimetre",
" lexeme ru",
" surface",
" text сантиметр",
" lexeme hi",
" surface",
" text सेंटीमीटर",
" lexeme zh",
" surface",
" text 厘米",
" millimeter",
" grounded-in Q174789",
" defined-by length",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text mm",
" surface",
" text millimeter",
" surface",
" text millimetre",
" lexeme ru",
" surface",
" text миллиметр",
" lexeme hi",
" surface",
" text मिलीमीटर",
" lexeme zh",
" surface",
" text 毫米",
" byte",
" grounded-in Q8799",
" defined-by data_storage",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text byte",
" surface",
" text bytes",
" lexeme ru",
" surface",
" text байт",
" surface",
" text байта",
" surface",
" text байтов",
" lexeme hi",
" surface",
" text बाइट",
" lexeme zh",
" surface",
" text 字节",
" kilobyte",
" grounded-in Q79726",
" defined-by data_storage",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text kb",
" surface",
" text kilobyte",
" surface",
" text kilobytes",
" lexeme ru",
" surface",
" text килобайт",
" lexeme hi",
" surface",
" text किलोबाइट",
" lexeme zh",
" surface",
" text 千字节",
" megabyte",
" grounded-in Q79735",
" defined-by data_storage",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text mb",
" surface",
" text megabyte",
" surface",
" text megabytes",
" lexeme ru",
" surface",
" text мегабайт",
" lexeme hi",
" surface",
" text मेगाबाइट",
" lexeme zh",
" surface",
" text 兆字节",
" gigabyte",
" grounded-in Q79738",
" defined-by data_storage",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text gb",
" surface",
" text gigabyte",
" surface",
" text gigabytes",
" lexeme ru",
" surface",
" text гигабайт",
" lexeme hi",
" surface",
" text गीगाबाइट",
" lexeme zh",
" surface",
" text 吉字节",
" terabyte",
" grounded-in Q79741",
" defined-by data_storage",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text tb",
" surface",
" text terabyte",
" surface",
" text terabytes",
" lexeme ru",
" surface",
" text терабайт",
" lexeme hi",
" surface",
" text टेराबाइट",
" lexeme zh",
" surface",
" text 太字节",
" bit",
" grounded-in Q8805",
" defined-by data_storage",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text bit",
" surface",
" text bits",
" lexeme ru",
" surface",
" text бит",
" surface",
" text бита",
" surface",
" text битов",
" lexeme hi",
" surface",
" text बिट",
" lexeme zh",
" surface",
" text 比特",
" kilogram",
" grounded-in Q11570",
" defined-by mass",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text kg",
" surface",
" text kilogram",
" surface",
" text kilograms",
" lexeme ru",
" surface",
" text кг",
" surface",
" text килограмм",
" lexeme hi",
" surface",
" text किलोग्राम",
" lexeme zh",
" surface",
" text 千克",
" surface",
" text 公斤",
" gram",
" grounded-in Q41803",
" defined-by mass",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text gram",
" surface",
" text grams",
" lexeme ru",
" surface",
" text грамм",
" lexeme hi",
" surface",
" text ग्राम",
" lexeme zh",
" surface",
" text 克",
" pound",
" grounded-in Q100995",
" defined-by mass",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text pound",
" surface",
" text pounds",
" lexeme ru",
" surface",
" text фунт",
" lexeme hi",
" surface",
" text पाउंड",
" lexeme zh",
" surface",
" text 磅",
" ton",
" grounded-in Q191118",
" defined-by mass",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text ton",
" surface",
" text tons",
" surface",
" text tonne",
" lexeme ru",
" surface",
" text тонна",
" surface",
" text тонны",
" surface",
" text тонн",
" lexeme hi",
" surface",
" text टन",
" lexeme zh",
" surface",
" text 吨",
" second",
" grounded-in Q11574",
" defined-by time",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text second",
" surface",
" text seconds",
" lexeme ru",
" surface",
" text секунда",
" surface",
" text секунды",
" surface",
" text секунд",
" lexeme hi",
" surface",
" text सेकंड",
" lexeme zh",
" surface",
" text 秒",
" minute",
" grounded-in Q7727",
" defined-by time",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text minute",
" surface",
" text minutes",
" lexeme ru",
" surface",
" text минута",
" surface",
" text минуты",
" surface",
" text минут",
" lexeme hi",
" surface",
" text मिनट",
" lexeme zh",
" surface",
" text 分钟",
" hour",
" grounded-in Q25235",
" defined-by time",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text hour",
" surface",
" text hours",
" lexeme ru",
" surface",
" text час",
" surface",
" text часа",
" surface",
" text часов",
" lexeme hi",
" surface",
" text घंटा",
" lexeme zh",
" surface",
" text 小时",
" millisecond",
" grounded-in Q723733",
" defined-by time",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text ms",
" surface",
" text millisecond",
" surface",
" text milliseconds",
" lexeme ru",
" surface",
" text миллисекунда",
" surface",
" text миллисекунд",
" lexeme hi",
" surface",
" text मिलीसेकंड",
" lexeme zh",
" surface",
" text 毫秒",
" day",
" grounded-in Q573",
" defined-by time",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text day",
" surface",
" text days",
" lexeme ru",
" surface",
" text день",
" surface",
" text дня",
" surface",
" text дней",
" lexeme hi",
" surface",
" text दिन",
" lexeme zh",
" surface",
" text 天",
" surface",
" text 日",
" month",
" grounded-in Q5151",
" defined-by time",
" defined-by unit",
" role measurement_unit",
" role calculation_domain_term",
" lexeme en",
" surface",
" text month",
" surface",
" text months",
" lexeme ru",
" surface",
" text месяц",
" surface",
" text месяца",
" surface",
" text месяцев",
" lexeme hi",
" surface",
" text महीना",
" surface",
" text महीने",
" lexeme zh",
" surface",
" text 月",
" surface",
" text 个月",
" celsius",
" grounded-in Q25267",
" defined-by temperature",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text celsius",
" lexeme ru",
" surface",
" text цельсий",
" lexeme hi",
" surface",
" text सेल्सियस",
" lexeme zh",
" surface",
" text 摄氏度",
" fahrenheit",
" grounded-in Q42289",
" defined-by temperature",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text fahrenheit",
" lexeme ru",
" surface",
" text фаренгейт",
" lexeme hi",
" surface",
" text फ़ारेनहाइट",
" lexeme zh",
" surface",
" text 华氏度",
" kelvin",
" grounded-in Q11579",
" defined-by temperature",
" defined-by unit",
" role measurement_unit",
" lexeme en",
" surface",
" text kelvin",
" lexeme ru",
" surface",
" text кельвин",
" lexeme hi",
" surface",
" text केल्विन",
" lexeme zh",
" surface",
" text 开尔文",
"meanings",
" monday",
" grounded-in Q105",
" defined-by calendar_day",
" role calendar_weekday",
" lexeme en",
" surface",
" text monday",
" surface",
" text mon",
" lexeme ru",
" surface",
" text понедельник",
" surface",
" text понедельника",
" surface",
" text понедельником",
" surface",
" text понедельнику",
" surface",
" text понедельнике",
" lexeme hi",
" surface",
" text सोमवार",
" lexeme zh",
" surface",
" text 星期一",
" surface",
" text 周一",
" tuesday",
" grounded-in Q127",
" defined-by calendar_day",
" role calendar_weekday",
" lexeme en",
" surface",
" text tuesday",
" surface",
" text tue",
" surface",
" text tues",
" lexeme ru",
" surface",
" text вторник",
" surface",
" text вторника",
" surface",
" text вторником",
" surface",
" text вторнику",
" surface",
" text вторнике",
" lexeme hi",
" surface",
" text मंगलवार",
" lexeme zh",
" surface",
" text 星期二",
" surface",
" text 周二",
" wednesday",
" grounded-in Q128",
" defined-by calendar_day",
" role calendar_weekday",
" lexeme en",
" surface",
" text wednesday",
" surface",
" text wed",
" lexeme ru",
" surface",
" text среда",
" surface",
" text среды",
" surface",
" text среде",
" surface",
" text среду",
" surface",
" text средой",
" lexeme hi",
" surface",
" text बुधवार",
" lexeme zh",
" surface",
" text 星期三",
" surface",
" text 周三",
" thursday",
" grounded-in Q129",
" defined-by calendar_day",
" role calendar_weekday",
" lexeme en",
" surface",
" text thursday",
" surface",
" text thu",
" surface",
" text thur",
" surface",
" text thurs",
" lexeme ru",
" surface",
" text четверг",
" surface",
" text четверга",
" surface",
" text четвергом",
" surface",
" text четвергу",
" surface",
" text четверге",
" lexeme hi",
" surface",
" text गुरुवार",
" lexeme zh",
" surface",
" text 星期四",
" surface",
" text 周四",
" friday",
" grounded-in Q130",
" defined-by calendar_day",
" role calendar_weekday",
" lexeme en",
" surface",
" text friday",
" surface",
" text fri",
" lexeme ru",
" surface",
" text пятница",
" surface",
" text пятницы",
" surface",
" text пятнице",
" surface",
" text пятницу",
" surface",
" text пятницей",
" lexeme hi",
" surface",
" text शुक्रवार",
" lexeme zh",
" surface",
" text 星期五",
" surface",
" text 周五",
" saturday",
" grounded-in Q131",
" defined-by calendar_day",
" role calendar_weekday",
" lexeme en",
" surface",
" text saturday",
" surface",
" text sat",
" lexeme ru",
" surface",
" text суббота",
" surface",
" text субботы",
" surface",
" text субботе",
" surface",
" text субботу",
" surface",
" text субботой",
" lexeme hi",
" surface",
" text शनिवार",
" lexeme zh",
" surface",
" text 星期六",
" surface",
" text 周六",
" sunday",
" grounded-in Q132",
" defined-by calendar_day",
" role calendar_weekday",
" lexeme en",
" surface",
" text sunday",
" surface",
" text sun",
" lexeme ru",
" surface",
" text воскресенье",
" surface",
" text воскресенья",
" surface",
" text воскресенью",
" surface",
" text воскресеньем",
" lexeme hi",
" surface",
" text रविवार",
" lexeme zh",
" surface",
" text 星期日",
" surface",
" text 星期天",
" surface",
" text 周日",
" calendar_day",
" grounded-in Q573",
" defined-by calendar_week",
" defined-by calendar_date",
" defined-by concept",
" role calendar_day_reference",
" lexeme en",
" surface",
" text day",
" surface",
" text weekday",
" surface",
" text \"week day\"",
" lexeme ru",
" surface",
" text день",
" surface",
" text дня",
" surface",
" text дни",
" surface",
" text дней",
" lexeme hi",
" surface",
" text दिन",
" lexeme zh",
" surface",
" text 星期几",
" surface",
" text 日子",
" calendar_date",
" grounded-in Q205892",
" defined-by calendar_day",
" role calendar_day_reference",
" lexeme en",
" surface",
" text date",
" lexeme ru",
" surface",
" text дата",
" surface",
" text дату",
" surface",
" text число",
" lexeme hi",
" surface",
" text तारीख",
" surface",
" text दिनांक",
" lexeme zh",
" surface",
" text 日期",
" surface",
" text 几号",
" calendar_week",
" grounded-in Q23387",
" defined-by calendar_day",
" role calendar_day_reference",
" lexeme en",
" surface",
" text week",
" lexeme ru",
" surface",
" text неделя",
" surface",
" text недели",
" surface",
" text неделе",
" surface",
" text неделю",
" surface",
" text неделей",
" surface",
" text недель",
" lexeme hi",
" surface",
" text सप्ताह",
" lexeme zh",
" surface",
" text 星期",
" surface",
" text 周",
" calendar_following",
" defined-by calendar_day",
" role calendar_direction_next",
" lexeme en",
" surface",
" text after",
" surface",
" text \"comes after\"",
" surface",
" text \"day after\"",
" surface",
" text \"next day\"",
" surface",
" text \"following day\"",
" surface",
" text \"following weekday\"",
" surface",
" text follows",
" lexeme ru",
" surface",
" text после",
" surface",
" text \"наступает после\"",
" surface",
" text \"следующий день\"",
" surface",
" text следующая",
" surface",
" text \"следом за\"",
" lexeme hi",
" surface",
" text \"के बाद\"",
" surface",
" text बाद",
" surface",
" text अगला",
" surface",
" text अगले",
" lexeme zh",
" surface",
" text 之后",
" surface",
" text 后",
" surface",
" text 下一个",
" surface",
" text 下一天",
" calendar_preceding",
" defined-by calendar_day",
" role calendar_direction_previous",
" lexeme en",
" surface",
" text before",
" surface",
" text \"comes before\"",
" surface",
" text \"day before\"",
" surface",
" text \"previous day\"",
" surface",
" text \"previous weekday\"",
" surface",
" text precedes",
" lexeme ru",
" surface",
" text перед",
" surface",
" text \"предыдущий день\"",
" surface",
" text предыдущая",
" surface",
" text предшествует",
" lexeme hi",
" surface",
" text \"से पहले\"",
" surface",
" text \"के पहले\"",
" surface",
" text पहले",
" surface",
" text पिछला",
" lexeme zh",
" surface",
" text 之前",
" surface",
" text 前",
" surface",
" text 上一个",
" surface",
" text 上一天",
" calendar_today",
" grounded-in Q3151690",
" defined-by calendar_day",
" role calendar_today",
" lexeme en",
" surface",
" text today",
" lexeme ru",
" surface",
" text сегодня",
" lexeme hi",
" surface",
" text आज",
" lexeme zh",
" surface",
" text 今天",
" calendar_interrogative",
" defined-by calendar_day",
" role calendar_question",
" lexeme en",
" surface",
" text what",
" surface",
" text which",
" surface",
" text \"tell me\"",
" surface",
" text show",
" surface",
" text ?",
" lexeme ru",
" surface",
" text какой",
" surface",
" text какая",
" surface",
" text какое",
" surface",
" text скажи",
" surface",
" text покажи",
" lexeme hi",
" surface",
" text कौन",
" surface",
" text क्या",
" surface",
" text बताओ",
" surface",
" text दिखाओ",
" lexeme zh",
" surface",
" text 什么",
" surface",
" text 几",
" surface",
" text 告诉",
" surface",
" text 显示",
"meanings",
" money",
" grounded-in Q1368",
" defined-by concept",
" role monetary_concept",
" lexeme en",
" surface",
" text money",
" surface",
" text currency",
" lexeme ru",
" surface",
" text деньги",
" surface",
" text валюта",
" lexeme hi",
" surface",
" text पैसा",
" surface",
" text मुद्रा",
" lexeme zh",
" surface",
" text 货币",
" surface",
" text 钱",
" exchange_rate",
" grounded-in Q66100",
" defined-by money",
" defined-by relation",
" role exchange_rate_reference",
" lexeme en",
" surface",
" text \"exchange rate\"",
" surface",
" text \"currency rate\"",
" lexeme ru",
" surface",
" text курс",
" lexeme hi",
" surface",
" text \"विनिमय दर\"",
" lexeme zh",
" surface",
" text 汇率",
" us_dollar",
" grounded-in Q4917",
" defined-by money",
" role currency_usd_reference",
" role calculation_domain_term",
" lexeme en",
" surface",
" text usd",
" surface",
" text dollar",
" surface",
" text dollars",
" lexeme ru",
" surface",
" text доллар",
" surface",
" text долар",
" surface",
" text долор",
" lexeme hi",
" surface",
" text डॉलर",
" lexeme zh",
" surface",
" text 美元",
" euro",
" grounded-in Q4916",
" defined-by money",
" role currency_eur_reference",
" role calculation_domain_term",
" lexeme en",
" surface",
" text eur",
" surface",
" text euro",
" surface",
" text euros",
" lexeme ru",
" surface",
" text евро",
" lexeme hi",
" surface",
" text यूरो",
" lexeme zh",
" surface",
" text 欧元",
" ruble",
" grounded-in Q41044",
" defined-by money",
" role currency_rub_reference",
" role calculation_domain_term",
" lexeme en",
" surface",
" text rub",
" surface",
" text ruble",
" surface",
" text rubles",
" lexeme ru",
" surface",
" text рубль",
" surface",
" text рублей",
" surface",
" text руб",
" lexeme hi",
" surface",
" text रूबल",
" lexeme zh",
" surface",
" text 卢布",
" calculation_basis",
" defined-by action",
" defined-by inquiry",
" role calculation_basis_reference",
" lexeme en",
" surface",
" text calculation",
" surface",
" text calculations",
" surface",
" text \"do you use\"",
" surface",
" text \"used for\"",
" surface",
" text \"your rate\"",
" lexeme ru",
" surface",
" text \"при расчет\"",
" surface",
" text \"при расчёт\"",
" surface",
" text \"в расчет\"",
" surface",
" text \"в расчёт\"",
" surface",
" text \"для расчет\"",
" surface",
" text \"для расчёт\"",
" surface",
" text \"у тебя\"",
" surface",
" text использ",
" surface",
" text берешь",
" surface",
" text берёшь",
" surface",
" text примен",
" lexeme hi",
" surface",
" text गणना",
" surface",
" text उपयोग",
" lexeme zh",
" surface",
" text 计算",
" surface",
" text 使用",
" arithmetic_operation",
" grounded-in Q12170668",
" defined-by action",
" role numeric_concept",
" lexeme en",
" surface",
" text \"arithmetic operation\"",
" surface",
" text operator",
" lexeme ru",
" surface",
" text \"арифметическая операция\"",
" lexeme hi",
" surface",
" text \"अंकगणितीय संक्रिया\"",
" lexeme zh",
" surface",
" text 算术运算",
" addition",
" grounded-in Q32043",
" defined-by arithmetic_operation",
" role arithmetic_operator_word",
" lexeme en",
" surface",
" text plus",
" surface",
" text +",
" lexeme ru",
" surface",
" text плюс",
" lexeme hi",
" surface",
" text जोड़",
" lexeme zh",
" surface",
" text 加上",
" subtraction",
" grounded-in Q40754",
" defined-by arithmetic_operation",
" role arithmetic_operator_word",
" lexeme en",
" surface",
" text minus",
" surface",
" text -",
" lexeme ru",
" surface",
" text минус",
" lexeme hi",
" surface",
" text घटा",
" lexeme zh",
" surface",
" text 减去",
" multiplication",
" grounded-in Q40276",
" defined-by arithmetic_operation",
" role arithmetic_operator_word",
" lexeme en",
" surface",
" text times",
" surface",
" text \"multiplied by\"",
" surface",
" text *",
" lexeme ru",
" surface",
" text умножить",
" surface",
" text умножь",
" surface",
" text \"умножить на\"",
" lexeme hi",
" surface",
" text गुणा",
" lexeme zh",
" surface",
" text 乘以",
" division",
" grounded-in Q1226939",
" defined-by arithmetic_operation",
" role arithmetic_operator_word",
" lexeme en",
" surface",
" text \"divided by\"",
" surface",
" text /",
" lexeme ru",
" surface",
" text \"разделить на\"",
" surface",
" text \"делить на\"",
" lexeme hi",
" surface",
" text भाग",
" lexeme zh",
" surface",
" text 除以",
" modulo",
" grounded-in Q1799665",
" defined-by arithmetic_operation",
" role arithmetic_operator_word",
" lexeme en",
" surface",
" text modulo",
" surface",
" text mod",
" surface",
" text %",
" lexeme ru",
" surface",
" text \"по модулю\"",
" lexeme hi",
" surface",
" text मॉड्यूलो",
" lexeme zh",
" surface",
" text 取模",
" calculation_request",
" defined-by action",
" defined-by inquiry",
" role calculation_request_cue",
" lexeme en",
" surface",
" text \"please calculate\"",
" surface",
" text \"please compute\"",
" surface",
" text \"can you calculate\"",
" surface",
" text \"can you compute\"",
" surface",
" text \"could you calculate\"",
" surface",
" text \"could you compute\"",
" surface",
" text \"what is\"",
" surface",
" text \"what's\"",
" surface",
" text \"what does\"",
" surface",
" text calculate",
" surface",
" text compute",
" surface",
" text evaluate",
" surface",
" text \"how much is\"",
" surface",
" text solve",
" lexeme ru",
" surface",
" text \"сколько будет\"",
" surface",
" text посчитай",
" surface",
" text посчитайте",
" surface",
" text вычисли",
" surface",
" text вычислите",
" surface",
" text рассчитай",
" surface",
" text рассчитайте",
" lexeme zh",
" surface",
" text 请计算",
" surface",
" text 请算一下",
" surface",
" text 计算一下",
" surface",
" text 算一下",
" surface",
" text 计算",
" lexeme hi",
" surface",
" text \"कृपया गणना करें\"",
" surface",
" text \"गणना करें\"",
" politeness",
" grounded-in Q281287",
" defined-by property",
" role politeness_cue",
" lexeme en",
" surface",
" text please",
" surface",
" text \"for me\"",
" lexeme ru",
" surface",
" text пожалуйста",
" lexeme hi",
" surface",
" text कृपया",
" lexeme zh",
" surface",
" text 请",
" calculation_result_query",
" defined-by action",
" defined-by inquiry",
" role calculation_result_query_cue",
" lexeme en",
" surface",
" text equal",
" surface",
" text equals",
" surface",
" text =",
" lexeme ru",
" surface",
" text равно",
" lexeme zh",
" surface",
" text 是多少",
" surface",
" text 等于多少",
" surface",
" text 等于几",
" lexeme hi",
" surface",
" text \"कितना है\"",
" surface",
" text \"क्या है\"",
" surface",
" text \"की गणना करें\"",
" quantity_conversion",
" grounded-in Q618655",
" defined-by action",
" defined-by relation",
" role quantity_conversion_cue",
" lexeme en",
" surface",
" text to",
" surface",
" text into",
" surface",
" text convert",
" surface",
" text exchange",
" lexeme ru",
" surface",
" text конвертировать",
" surface",
" text обмен",
" lexeme hi",
" surface",
" text बदलें",
" surface",
" text परिवर्तित",
" lexeme zh",
" surface",
" text 转换",
" surface",
" text 兑换",
" surface",
" text 换成",
" mathematical_function",
" grounded-in Q11348",
" defined-by function",
" role numeric_concept",
" lexeme en",
" surface",
" text \"mathematical function\"",
" lexeme ru",
" surface",
" text \"математическая функция\"",
" lexeme hi",
" surface",
" text \"गणितीय फलन\"",
" lexeme zh",
" surface",
" text 数学函数",
" square_root",
" grounded-in Q134237",
" defined-by mathematical_function",
" role math_function_name",
" lexeme en",
" surface",
" text sqrt",
" surface",
" text \"square root\"",
" lexeme ru",
" surface",
" text \"квадратный корень\"",
" surface",
" text корень",
" lexeme hi",
" surface",
" text वर्गमूल",
" lexeme zh",
" surface",
" text 平方根",
" sine",
" grounded-in Q152415",
" defined-by mathematical_function",
" role math_function_name",
" lexeme en",
" surface",
" text sin",
" surface",
" text sine",
" lexeme ru",
" surface",
" text синус",
" lexeme hi",
" surface",
" text ज्या",
" lexeme zh",
" surface",
" text 正弦",
" cosine",
" grounded-in Q1256164",
" defined-by mathematical_function",
" role math_function_name",
" lexeme en",
" surface",
" text cos",
" surface",
" text cosine",
" lexeme ru",
" surface",
" text косинус",
" lexeme hi",
" surface",
" text कोज्या",
" lexeme zh",
" surface",
" text 余弦",
" tangent",
" grounded-in Q1129196",
" defined-by mathematical_function",
" role math_function_name",
" lexeme en",
" surface",
" text tan",
" surface",
" text tangent",
" lexeme ru",
" surface",
" text тангенс",
" lexeme hi",
" surface",
" text स्पर्शज्या",
" lexeme zh",
" surface",
" text 正切",
" logarithm",
" grounded-in Q11197",
" defined-by mathematical_function",
" role math_function_name",
" lexeme en",
" surface",
" text log",
" surface",
" text logarithm",
" lexeme ru",
" surface",
" text логарифм",
" lexeme hi",
" surface",
" text लघुगणक",
" lexeme zh",
" surface",
" text 对数",
" natural_logarithm",
" grounded-in Q204037",
" defined-by mathematical_function",
" role math_function_name",
" lexeme en",
" surface",
" text ln",
" surface",
" text \"natural logarithm\"",
" lexeme ru",
" surface",
" text \"натуральный логарифм\"",
" lexeme hi",
" surface",
" text \"प्राकृतिक लघुगणक\"",
" lexeme zh",
" surface",
" text 自然对数",
"meanings",
" knowledge_relation",
" defined-by knowledge_subject",
" defined-by knowledge_value",
" defined-by relation",
" role knowledge_relation",
" lexeme en",
" surface",
" text relation",
" surface",
" text property",
" lexeme ru",
" surface",
" text отношение",
" surface",
" text свойство",
" lexeme hi",
" surface",
" text संबंध",
" surface",
" text गुण",
" lexeme zh",
" surface",
" text 关系",
" surface",
" text 属性",
" knowledge_subject",
" defined-by knowledge_relation",
" role knowledge_subject",
" lexeme en",
" surface",
" text subject",
" surface",
" text entity",
" surface",
" text topic",
" lexeme ru",
" surface",
" text субъект",
" surface",
" text сущность",
" surface",
" text предмет",
" lexeme hi",
" surface",
" text विषय",
" surface",
" text इकाई",
" lexeme zh",
" surface",
" text 主体",
" surface",
" text 实体",
" knowledge_value",
" defined-by knowledge_relation",
" role knowledge_value",
" lexeme en",
" surface",
" text value",
" surface",
" text answer",
" lexeme ru",
" surface",
" text значение",
" surface",
" text ответ",
" lexeme hi",
" surface",
" text मान",
" surface",
" text उत्तर",
" lexeme zh",
" surface",
" text 值",
" surface",
" text 答案",
" capital",
" grounded-in P36",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text capital",
" lexeme ru",
" surface",
" text столица",
" surface",
" text столицы",
" surface",
" text столицей",
" lexeme hi",
" surface",
" text राजधानी",
" lexeme zh",
" surface",
" text 首都",
" population",
" grounded-in P1082",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text population",
" surface",
" text \"how many people\"",
" lexeme ru",
" surface",
" text население",
" lexeme hi",
" surface",
" text जनसंख्या",
" surface",
" text आबादी",
" lexeme zh",
" surface",
" text 人口",
" currency",
" grounded-in P38",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text currency",
" lexeme ru",
" surface",
" text валюта",
" lexeme hi",
" surface",
" text मुद्रा",
" lexeme zh",
" surface",
" text 货币",
" surface",
" text 貨幣",
" official_language",
" grounded-in P37",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text \"official language\"",
" surface",
" text \"what language\"",
" lexeme ru",
" surface",
" text \"государственный язык\"",
" surface",
" text \"официальный язык\"",
" lexeme hi",
" surface",
" text राजभाषा",
" surface",
" text \"आधिकारिक भाषा\"",
" lexeme zh",
" surface",
" text 官方语言",
" surface",
" text 官方語言",
" continent",
" grounded-in P30",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text continent",
" lexeme ru",
" surface",
" text континент",
" lexeme hi",
" surface",
" text महाद्वीप",
" lexeme zh",
" surface",
" text 大洲",
" author_of_book",
" grounded-in P50",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text \"who wrote\"",
" surface",
" text author",
" surface",
" text \"written by\"",
" lexeme ru",
" surface",
" text \"кто написал\"",
" surface",
" text написал",
" surface",
" text автор",
" lexeme hi",
" surface",
" text \"किसने लिखी\"",
" surface",
" text \"किसने लिखा\"",
" surface",
" text लेखक",
" lexeme zh",
" surface",
" text 是谁写的",
" surface",
" text 作者",
" painter_of_painting",
" grounded-in P170",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text \"who painted\"",
" surface",
" text painted",
" surface",
" text painter",
" surface",
" text artist",
" lexeme ru",
" surface",
" text \"кто написал\"",
" surface",
" text написал",
" surface",
" text художник",
" lexeme hi",
" surface",
" text \"किसने बनाई\"",
" surface",
" text चित्रकार",
" surface",
" text कलाकार",
" lexeme zh",
" surface",
" text 谁画的",
" surface",
" text 画家",
" built_year",
" grounded-in P571",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text when",
" surface",
" text built",
" surface",
" text construction",
" lexeme ru",
" surface",
" text когда",
" surface",
" text построена",
" surface",
" text построили",
" lexeme hi",
" surface",
" text बनी",
" surface",
" text बनाई",
" surface",
" text कब",
" lexeme zh",
" surface",
" text 何时",
" surface",
" text 建于",
" surface",
" text 建造",
" physical_constant",
" grounded-in Q173227",
" defined-by knowledge_relation",
" role fact_relation",
" lexeme en",
" surface",
" text \"speed of light\"",
" surface",
" text \"what is\"",
" surface",
" text \"how fast\"",
" lexeme ru",
" surface",
" text \"скорость света\"",
" surface",
" text какова",
" surface",
" text \"чему равна\"",
" lexeme hi",
" surface",
" text \"प्रकाश की गति\"",
" surface",
" text कितनी",
" lexeme zh",
" surface",
" text 光速",
" surface",
" text 是多少",
"meanings",
" software_followup",
" defined-by software_followup_verification",
" defined-by software_followup_execution",
" defined-by software_followup_demonstration",
" defined-by action",
" role software_followup",
" lexeme en",
" surface",
" text follow-up",
" surface",
" text exercise",
" lexeme ru",
" surface",
" text продолжение",
" surface",
" text отработка",
" lexeme hi",
" surface",
" text अनुवर्ती",
" surface",
" text अभ्यास",
" lexeme zh",
" surface",
" text 后续",
" surface",
" text 演练",
" software_followup_verification",
" defined-by software_followup",
" role software_followup_verification",
" lexeme en",
" surface",
" text \"test it\"",
" surface",
" text \"test the\"",
" surface",
" text \"test this\"",
" surface",
" text verify",
" surface",
" text \"check it\"",
" surface",
" text \"check that\"",
" surface",
" text \"run the tests\"",
" lexeme ru",
" surface",
" text протестируй",
" surface",
" text протестировать",
" surface",
" text проверь",
" surface",
" text тестируй",
" lexeme hi",
" surface",
" text परीक्षण",
" surface",
" text जाँच",
" surface",
" text जांच",
" lexeme zh",
" surface",
" text 测试",
" surface",
" text 检验",
" surface",
" text 检查",
" software_followup_execution",
" defined-by software_followup",
" role software_followup_execution",
" lexeme en",
" surface",
" text \"run it\"",
" surface",
" text \"run this\"",
" surface",
" text \"run the\"",
" surface",
" text \"execute it\"",
" surface",
" text \"execute the\"",
" surface",
" text \"try it\"",
" lexeme ru",
" surface",
" text запусти",
" surface",
" text выполни",
" lexeme hi",
" surface",
" text चलाओ",
" surface",
" text निष्पादित",
" lexeme zh",
" surface",
" text 运行",
" surface",
" text 执行",
" software_followup_demonstration",
" defined-by software_followup",
" role software_followup_demonstration",
" lexeme en",
" surface",
" text \"demo it\"",
" surface",
" text \"show me\"",
" surface",
" text \"show the\"",
" surface",
" text \"print the\"",
" lexeme ru",
" surface",
" text покажи",
" lexeme hi",
" surface",
" text दिखाओ",
" lexeme zh",
" surface",
" text 显示",
" surface",
" text 展示",
" output_display_request",
" defined-by software_followup",
" defined-by action",
" role output_display_request",
" lexeme en",
" surface",
" text \"show me …\"",
" surface",
" text \"show …\"",
" surface",
" text \"print …\"",
" surface",
" text \"display …\"",
" lexeme ru",
" surface",
" text \"покажи мне …\"",
" surface",
" text \"покажи …\"",
" surface",
" text \"выведи …\"",
" lexeme hi",
" surface",
" text \"मुझे दिखाओ …\"",
" surface",
" text \"दिखाओ …\"",
" lexeme zh",
" surface",
" text 给我看…",
" surface",
" text 显示…",
" implement",
" defined-by code",
" defined-by program",
" role software_authoring_action",
" lexeme en",
" surface",
" text implement",
" lexeme ru",
" surface",
" text реализуй",
" surface",
" text реализовать",
" lexeme hi",
" surface",
" text लागू",
" lexeme zh",
" surface",
" text 实现",
" develop",
" defined-by program",
" role software_authoring_action",
" lexeme en",
" surface",
" text develop",
" lexeme ru",
" surface",
" text разработай",
" surface",
" text разработать",
" lexeme hi",
" surface",
" text विकसित",
" lexeme zh",
" surface",
" text 开发",
" design",
" defined-by program",
" role software_authoring_action",
" lexeme en",
" surface",
" text design",
" lexeme ru",
" surface",
" text спроектируй",
" surface",
" text \"создай дизайн\"",
" lexeme hi",
" surface",
" text डिज़ाइन",
" lexeme zh",
" surface",
" text 设计",
" scaffold",
" defined-by code",
" defined-by program",
" role software_authoring_action",
" lexeme en",
" surface",
" text scaffold",
" lexeme ru",
" surface",
" text \"создай каркас\"",
" surface",
" text \"сгенерируй каркас\"",
" lexeme hi",
" surface",
" text \"ढाँचा बनाओ\"",
" lexeme zh",
" surface",
" text 搭建",
" software_artifact",
" defined-by artifact_application",
" defined-by artifact_tool",
" defined-by entity",
" role software_artifact",
" lexeme en",
" surface",
" text \"software artifact\"",
" surface",
" text deliverable",
" lexeme ru",
" surface",
" text \"программный продукт\"",
" surface",
" text артефакт",
" lexeme hi",
" surface",
" text \"सॉफ़्टवेयर कलाकृति\"",
" lexeme zh",
" surface",
" text 软件制品",
" artifact_browser_extension",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text \"browser extension\"",
" lexeme ru",
" surface",
" text \"расширение браузера\"",
" lexeme hi",
" surface",
" text \"ब्राउज़र एक्सटेंशन\"",
" lexeme zh",
" surface",
" text 浏览器扩展",
" artifact_command_line_tool",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text \"command line tool\"",
" surface",
" text \"cli tool\"",
" lexeme ru",
" surface",
" text \"консольная утилита\"",
" surface",
" text \"утилита командной строки\"",
" lexeme hi",
" surface",
" text \"कमांड लाइन टूल\"",
" lexeme zh",
" surface",
" text 命令行工具",
" artifact_github_action",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text \"github action\"",
" lexeme ru",
" surface",
" text \"действие github\"",
" lexeme hi",
" surface",
" text \"गिटहब एक्शन\"",
" lexeme zh",
" surface",
" text \"github 操作\"",
" artifact_mobile_app",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text \"mobile app\"",
" lexeme ru",
" surface",
" text \"мобильное приложение\"",
" lexeme hi",
" surface",
" text \"मोबाइल ऐप\"",
" lexeme zh",
" surface",
" text 移动应用",
" artifact_web_app",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text \"web app\"",
" lexeme ru",
" surface",
" text \"веб приложение\"",
" lexeme hi",
" surface",
" text \"वेब ऐप\"",
" lexeme zh",
" surface",
" text 网页应用",
" artifact_application",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text application",
" lexeme ru",
" surface",
" text приложение",
" lexeme hi",
" surface",
" text एप्लिकेशन",
" lexeme zh",
" surface",
" text 应用程序",
" artifact_extension",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text extension",
" surface",
" text \"add on\"",
" surface",
" text addon",
" lexeme ru",
" surface",
" text расширение",
" surface",
" text дополнение",
" lexeme hi",
" surface",
" text एक्सटेंशन",
" surface",
" text ऐड-ऑन",
" lexeme zh",
" surface",
" text 扩展",
" surface",
" text 扩展程序",
" artifact_dashboard",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text dashboard",
" lexeme ru",
" surface",
" text \"панель управления\"",
" surface",
" text дашборд",
" lexeme hi",
" surface",
" text डैशबोर्ड",
" lexeme zh",
" surface",
" text 仪表板",
" artifact_scraper",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text scraper",
" lexeme ru",
" surface",
" text скрапер",
" surface",
" text парсер",
" lexeme hi",
" surface",
" text स्क्रैपर",
" lexeme zh",
" surface",
" text 爬虫",
" artifact_library",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text library",
" lexeme ru",
" surface",
" text библиотека",
" surface",
" text библиотеку",
" lexeme hi",
" surface",
" text लाइब्रेरी",
" lexeme zh",
" surface",
" text 库",
" artifact_website",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text website",
" lexeme ru",
" surface",
" text сайт",
" surface",
" text \"веб сайт\"",
" lexeme hi",
" surface",
" text वेबसाइट",
" lexeme zh",
" surface",
" text 网站",
" artifact_plugin",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text plugin",
" lexeme ru",
" surface",
" text плагин",
" lexeme hi",
" surface",
" text प्लगइन",
" lexeme zh",
" surface",
" text 插件",
" artifact_service",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text service",
" lexeme ru",
" surface",
" text сервис",
" surface",
" text служба",
" surface",
" text службу",
" lexeme hi",
" surface",
" text सेवा",
" lexeme zh",
" surface",
" text 服务",
" artifact_bot",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text bot",
" lexeme ru",
" surface",
" text бот",
" lexeme hi",
" surface",
" text बॉट",
" lexeme zh",
" surface",
" text 机器人",
" artifact_app",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text app",
" lexeme ru",
" surface",
" text приложение",
" lexeme hi",
" surface",
" text ऐप",
" lexeme zh",
" surface",
" text 应用",
" artifact_api",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text api",
" lexeme ru",
" surface",
" text апи",
" lexeme hi",
" surface",
" text एपीआई",
" lexeme zh",
" surface",
" text 接口",
" artifact_sdk",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text sdk",
" lexeme ru",
" surface",
" text сдк",
" lexeme hi",
" surface",
" text एसडीके",
" lexeme zh",
" surface",
" text 开发包",
" artifact_tool",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text tool",
" lexeme ru",
" surface",
" text инструмент",
" lexeme hi",
" surface",
" text टूल",
" surface",
" text उपकरण",
" lexeme zh",
" surface",
" text 工具",
" artifact_mod",
" defined-by software_artifact",
" role software_artifact_kind",
" lexeme en",
" surface",
" text mod",
" lexeme ru",
" surface",
" text мод",
" lexeme hi",
" surface",
" text मॉड",
" lexeme zh",
" surface",
" text 模组",
" software_feature",
" defined-by requirement_state_tracking",
" defined-by requirement_project_behavior",
" defined-by property",
" role software_feature",
" lexeme en",
" surface",
" text feature",
" surface",
" text features",
" surface",
" text requirement",
" surface",
" text requirements",
" lexeme ru",
" surface",
" text функция",
" surface",
" text требование",
" lexeme hi",
" surface",
" text सुविधा",
" surface",
" text आवश्यकता",
" lexeme zh",
" surface",
" text 功能",
" surface",
" text 需求",
" requirement_state_tracking",
" defined-by software_feature",
" role software_requirement_category",
" lexeme en",
" surface",
" text track",
" surface",
" text hp",
" surface",
" text status",
" surface",
" text damage",
" surface",
" text cooldown",
" lexeme ru",
" surface",
" text состояние",
" surface",
" text урон",
" surface",
" text откат",
" lexeme hi",
" surface",
" text स्थिति",
" surface",
" text क्षति",
" lexeme zh",
" surface",
" text 状态",
" surface",
" text 伤害",
" surface",
" text 冷却",
" requirement_data_exchange",
" defined-by software_feature",
" role software_requirement_category",
" lexeme en",
" surface",
" text import",
" surface",
" text export",
" surface",
" text csv",
" surface",
" text backup",
" surface",
" text report",
" surface",
" text calendar",
" lexeme ru",
" surface",
" text импорт",
" surface",
" text экспорт",
" surface",
" text \"резервная копия\"",
" surface",
" text отчёт",
" lexeme hi",
" surface",
" text आयात",
" surface",
" text निर्यात",
" surface",
" text रिपोर्ट",
" lexeme zh",
" surface",
" text 导入",
" surface",
" text 导出",
" surface",
" text 备份",
" surface",
" text 报告",
" requirement_automation",
" defined-by software_feature",
" role software_requirement_category",
" lexeme en",
" surface",
" text reminder",
" surface",
" text notification",
" surface",
" text schedule",
" surface",
" text weekly",
" lexeme ru",
" surface",
" text напоминание",
" surface",
" text уведомление",
" surface",
" text расписание",
" lexeme hi",
" surface",
" text अनुस्मारक",
" surface",
" text सूचना",
" lexeme zh",
" surface",
" text 提醒",
" surface",
" text 通知",
" surface",
" text 计划",
" requirement_validation",
" defined-by software_feature",
" role software_requirement_category",
" lexeme en",
" surface",
" text validate",
" surface",
" text check",
" surface",
" text conflict",
" surface",
" text audit",
" lexeme ru",
" surface",
" text проверка",
" surface",
" text проверь",
" surface",
" text аудит",
" lexeme hi",
" surface",
" text सत्यापन",
" surface",
" text जाँच",
" lexeme zh",
" surface",
" text 验证",
" surface",
" text 检查",
" surface",
" text 审计",
" requirement_integration",
" defined-by software_feature",
" role software_requirement_category",
" lexeme en",
" surface",
" text api",
" surface",
" text discord",
" surface",
" text telegram",
" surface",
" text github",
" surface",
" text browser",
" lexeme ru",
" surface",
" text интеграция",
" surface",
" text апи",
" surface",
" text браузер",
" lexeme hi",
" surface",
" text एकीकरण",
" surface",
" text एपीआई",
" lexeme zh",
" surface",
" text 集成",
" surface",
" text 接口",
" surface",
" text 浏览器",
" requirement_user_interface",
" defined-by software_feature",
" role software_requirement_category",
" lexeme en",
" surface",
" text dashboard",
" surface",
" text chart",
" surface",
" text filter",
" surface",
" text progress",
" lexeme ru",
" surface",
" text панель",
" surface",
" text график",
" surface",
" text фильтр",
" lexeme hi",
" surface",
" text डैशबोर्ड",
" surface",
" text चार्ट",
" lexeme zh",
" surface",
" text 仪表板",
" surface",
" text 图表",
" surface",
" text 筛选",
" requirement_project_behavior",
" defined-by software_feature",
" role software_requirement_category",
" lexeme en",
" surface",
" text add",
" surface",
" text admin",
" surface",
" text customer",
" surface",
" text date",
" surface",
" text email",
" surface",
" text expense",
" surface",
" text file",
" surface",
" text history",
" surface",
" text invoice",
" surface",
" text log",
" surface",
" text maintenance",
" surface",
" text payment",
" surface",
" text protection",
" surface",
" text record",
" surface",
" text rename",
" surface",
" text resistance",
" surface",
" text retry",
" surface",
" text scrape",
" surface",
" text stack",
" surface",
" text sync",
" surface",
" text tracking",
" surface",
" text upload",
" lexeme ru",
" surface",
" text добавить",
" surface",
" text клиент",
" surface",
" text файл",
" surface",
" text загрузка",
" lexeme hi",
" surface",
" text जोड़ें",
" surface",
" text फ़ाइल",
" lexeme zh",
" surface",
" text 添加",
" surface",
" text 文件",
" surface",
" text 上传",
" software_delivery",
" defined-by software_artifact",
" defined-by delivery_manual_instructions",
" role software_delivery",
" lexeme en",
" surface",
" text delivery",
" lexeme ru",
" surface",
" text доставка",
" lexeme hi",
" surface",
" text वितरण",
" lexeme zh",
" surface",
" text 交付",
" delivery_manual_instructions",
" defined-by software_delivery",
" role software_delivery_mode",
" lexeme en",
" surface",
" text \"manual instruction\"",
" surface",
" text \"manual instructions\"",
" surface",
" text instruction",
" surface",
" text instructions",
" surface",
" text \"no code\"",
" lexeme ru",
" surface",
" text инструкция",
" surface",
" text инструкции",
" surface",
" text вручную",
" lexeme hi",
" surface",
" text निर्देश",
" surface",
" text मैनुअल",
" lexeme zh",
" surface",
" text 手动说明",
" surface",
" text 说明",
" delivery_immediate_execution",
" defined-by software_delivery",
" role software_delivery_mode",
" lexeme en",
" surface",
" text execute",
" surface",
" text \"run command\"",
" surface",
" text \"run commands\"",
" surface",
" text \"run it\"",
" surface",
" text webvm",
" lexeme ru",
" surface",
" text выполни",
" surface",
" text \"запусти команду\"",
" lexeme hi",
" surface",
" text \"निष्पादित करें\"",
" surface",
" text चलाएँ",
" lexeme zh",
" surface",
" text 立即运行",
" surface",
" text 执行",
" delivery_script_generation",
" defined-by software_delivery",
" role software_delivery_mode",
" lexeme en",
" surface",
" text bash",
" surface",
" text shell",
" surface",
" text script",
" surface",
" text scripts",
" surface",
" text commands",
" lexeme ru",
" surface",
" text скрипт",
" surface",
" text оболочка",
" surface",
" text команды",
" lexeme hi",
" surface",
" text स्क्रिप्ट",
" surface",
" text शेल",
" lexeme zh",
" surface",
" text 脚本",
" surface",
" text 外壳脚本",
" software_language",
" defined-by software_artifact",
" defined-by language_python",
" role software_language",
" lexeme en",
" surface",
" text \"programming language\"",
" lexeme ru",
" surface",
" text \"язык программирования\"",
" lexeme hi",
" surface",
" text \"प्रोग्रामिंग भाषा\"",
" lexeme zh",
" surface",
" text 编程语言",
" language_python",
" defined-by software_language",
" role software_implementation_language",
" lexeme en",
" surface",
" text python",
" surface",
" text django",
" surface",
" text fastapi",
" lexeme ru",
" surface",
" text питон",
" surface",
" text пайтон",
" lexeme hi",
" surface",
" text पायथन",
" lexeme zh",
" surface",
" text python",
" language_rust",
" defined-by software_language",
" role software_implementation_language",
" lexeme en",
" surface",
" text rust",
" surface",
" text cargo",
" lexeme ru",
" surface",
" text раст",
" lexeme hi",
" surface",
" text रस्ट",
" lexeme zh",
" surface",
" text rust",
" language_javascript",
" defined-by software_language",
" role software_implementation_language",
" lexeme en",
" surface",
" text javascript",
" surface",
" text node",
" lexeme ru",
" surface",
" text джаваскрипт",
" lexeme hi",
" surface",
" text जावास्क्रिप्ट",
" lexeme zh",
" surface",
" text javascript",
" implementation_language_preposition",
" defined-by software_language",
" role implementation_language_preposition",
" lexeme en",
" surface",
" text in",
" lexeme ru",
" surface",
" text на",
" lexeme hi",
" surface",
" text में",
" lexeme zh",
" surface",
" text 用",
" implementation_language_noun",
" defined-by software_language",
" role implementation_language_noun",
" lexeme en",
" surface",
" text language",
" lexeme ru",
" surface",
" text языке",
" lexeme hi",
" surface",
" text भाषा",
" lexeme zh",
" surface",
" text 语言",
" tabletop_game_tracker",
" defined-by game_tracker_domain",
" defined-by game_tracker_mechanic",
" role tabletop_game_tracker",
" lexeme en",
" surface",
" text \"tabletop game tracker\"",
" lexeme ru",
" surface",
" text \"трекер настольной игры\"",
" lexeme hi",
" surface",
" text \"टेबलटॉप गेम ट्रैकर\"",
" lexeme zh",
" surface",
" text 桌游追踪器",
" game_tracker_domain",
" defined-by tabletop_game_tracker",
" role game_tracker_domain",
" lexeme en",
" surface",
" text dnd",
" surface",
" text wargame",
" surface",
" text tabletop",
" surface",
" text unit",
" surface",
" text token",
" surface",
" text owlbear",
" lexeme ru",
" surface",
" text \"настольная игра\"",
" surface",
" text фишка",
" surface",
" text жетон",
" lexeme hi",
" surface",
" text टेबलटॉप",
" surface",
" text मोहरा",
" lexeme zh",
" surface",
" text 桌游",
" surface",
" text 棋子",
" surface",
" text 战棋",
" game_tracker_mechanic",
" defined-by tabletop_game_tracker",
" defined-by requirement_state_tracking",
" role game_tracker_mechanic",
" lexeme en",
" surface",
" text hp",
" surface",
" text damage",
" surface",
" text protection",
" surface",
" text resistance",
" surface",
" text cooldown",
" surface",
" text cooldowns",
" lexeme ru",
" surface",
" text хп",
" surface",
" text урон",
" surface",
" text защита",
" surface",
" text сопротивление",
" surface",
" text откат",
" lexeme hi",
" surface",
" text एचपी",
" surface",
" text क्षति",
" surface",
" text रक्षा",
" lexeme zh",
" surface",
" text 生命值",
" surface",
" text 伤害",
" surface",
" text 防护",
" surface",
" text 抗性",
" surface",
" text 冷却",
" software_approval",
" defined-by software_approval_trigger",
" defined-by software_step_granularity",
" defined-by concept",
" role software_approval",
" lexeme en",
" surface",
" text approval",
" lexeme ru",
" surface",
" text одобрение",
" lexeme hi",
" surface",
" text अनुमोदन",
" lexeme zh",
" surface",
" text 批准",
" software_approval_trigger",
" defined-by software_approval",
" role software_approval_trigger",
" lexeme en",
" surface",
" text approve",
" surface",
" text approved",
" surface",
" text \"approve plan\"",
" surface",
" text yes",
" surface",
" text \"yes proceed\"",
" surface",
" text proceed",
" surface",
" text \"go ahead\"",
" surface",
" text \"looks good\"",
" surface",
" text \"do it\"",
" surface",
" text \"start implementation\"",
" surface",
" text \"generate code\"",
" surface",
" text \"convert to code\"",
" lexeme ru",
" surface",
" text одобряю",
" surface",
" text да",
" surface",
" text приступай",
" surface",
" text поехали",
" lexeme hi",
" surface",
" text स्वीकृत",
" surface",
" text हाँ",
" surface",
" text \"आगे बढ़ो\"",
" lexeme zh",
" surface",
" text 批准",
" surface",
" text 同意",
" surface",
" text 开始",
" software_step_granularity",
" defined-by software_approval",
" role software_step_granularity",
" lexeme en",
" surface",
" text \"each step\"",
" surface",
" text \"step by step\"",
" lexeme ru",
" surface",
" text \"каждый шаг\"",
" surface",
" text пошагово",
" lexeme hi",
" surface",
" text \"हर चरण\"",
" surface",
" text \"चरण दर चरण\"",
" lexeme zh",
" surface",
" text 每一步",
" surface",
" text 逐步",
" software_bash_command",
" defined-by software_delivery",
" role software_bash_command",
" lexeme en",
" surface",
" text shell",
" surface",
" text bash",
" surface",
" text command",
" surface",
" text commands",
" surface",
" text docker",
" surface",
" text webvm",
" lexeme ru",
" surface",
" text оболочка",
" surface",
" text команда",
" surface",
" text команды",
" lexeme hi",
" surface",
" text शेल",
" surface",
" text कमांड",
" lexeme zh",
" surface",
" text 外壳",
" surface",
" text 命令",
" surface",
" text docker",
"meanings",
" program_synthesis",
" defined-by function",
" defined-by code",
" defined-by implement",
" role program_synthesis",
" lexeme en",
" surface",
" text \"program synthesis\"",
" surface",
" text \"function synthesis\"",
" lexeme ru",
" surface",
" text \"синтез программ\"",
" surface",
" text \"синтез функции\"",
" lexeme hi",
" surface",
" text \"प्रोग्राम संश्लेषण\"",
" lexeme zh",
" surface",
" text 程序合成",
" synthesis_subject_function",
" defined-by program_synthesis",
" defined-by function",
" role program_synthesis_subject",
" lexeme en",
" surface",
" text function",
" surface",
" text func",
" lexeme ru",
" surface",
" text функция",
" surface",
" text функцию",
" lexeme hi",
" surface",
" text फ़ंक्शन",
" surface",
" text फंक्शन",
" lexeme zh",
" surface",
" text 函数",
" synthesis_domain_python",
" defined-by program_synthesis",
" defined-by language_python",
" role program_synthesis_domain",
" lexeme en",
" surface",
" text python",
" lexeme ru",
" surface",
" text питон",
" surface",
" text пайтон",
" lexeme hi",
" surface",
" text पायथन",
" lexeme zh",
" surface",
" text python",
" synthesis_domain_tuple",
" defined-by program_synthesis",
" defined-by code",
" role program_synthesis_domain",
" lexeme en",
" surface",
" text tuple",
" surface",
" text tuples",
" lexeme ru",
" surface",
" text кортеж",
" surface",
" text кортежи",
" lexeme hi",
" surface",
" text टपल",
" lexeme zh",
" surface",
" text 元组",
" synthesis_domain_numbers",
" defined-by program_synthesis",
" defined-by result",
" role program_synthesis_domain",
" lexeme en",
" surface",
" text numbers",
" lexeme ru",
" surface",
" text числа",
" surface",
" text чисел",
" lexeme hi",
" surface",
" text संख्याएँ",
" surface",
" text संख्याओं",
" lexeme zh",
" surface",
" text 数字",
" synthesis_domain_vowels",
" defined-by program_synthesis",
" defined-by result",
" role program_synthesis_domain",
" lexeme en",
" surface",
" text vowels",
" surface",
" text vowel",
" lexeme ru",
" surface",
" text гласные",
" surface",
" text гласных",
" lexeme hi",
" surface",
" text स्वर",
" surface",
" text स्वरों",
" lexeme zh",
" surface",
" text 元音",
" synthesis_action_implement",
" defined-by program_synthesis",
" defined-by implement",
" role program_synthesis_action",
" lexeme en",
" surface",
" text implement",
" lexeme ru",
" surface",
" text реализуй",
" surface",
" text реализовать",
" lexeme hi",
" surface",
" text लागू",
" lexeme zh",
" surface",
" text 实现",
" synthesis_action_write",
" defined-by program_synthesis",
" defined-by write",
" role program_synthesis_action",
" lexeme en",
" surface",
" text write",
" lexeme ru",
" surface",
" text напиши",
" surface",
" text напишите",
" lexeme hi",
" surface",
" text लिखो",
" surface",
" text लिखें",
" lexeme zh",
" surface",
" text 编写",
" surface",
" text 写",
" synthesis_action_return",
" defined-by program_synthesis",
" defined-by output",
" defined-by result",
" role program_synthesis_action",
" lexeme en",
" surface",
" text return",
" surface",
" text returns",
" lexeme ru",
" surface",
" text верни",
" surface",
" text вернуть",
" surface",
" text возврати",
" lexeme hi",
" surface",
" text लौटाएँ",
" surface",
" text लौटाओ",
" surface",
" text लौटा",
" lexeme zh",
" surface",
" text 返回",
" signal_distinct_numbers",
" defined-by program_synthesis",
" defined-by synthesis_domain_numbers",
" role program_synthesis_signal",
" lexeme en",
" surface",
" text \"distinct numbers\"",
" lexeme ru",
" surface",
" text \"различные числа\"",
" surface",
" text \"разные числа\"",
" lexeme hi",
" surface",
" text \"अलग संख्याएँ\"",
" lexeme zh",
" surface",
" text 不同的数字",
" signal_difference",
" defined-by program_synthesis",
" role program_synthesis_signal",
" lexeme en",
" surface",
" text differ",
" surface",
" text differs",
" surface",
" text difference",
" lexeme ru",
" surface",
" text отличаются",
" surface",
" text различаются",
" surface",
" text разница",
" lexeme hi",
" surface",
" text भिन्न",
" surface",
" text अंतर",
" lexeme zh",
" surface",
" text 不同",
" surface",
" text 差异",
" signal_threshold",
" defined-by program_synthesis",
" role program_synthesis_signal",
" lexeme en",
" surface",
" text threshold",
" surface",
" text thresholds",
" lexeme ru",
" surface",
" text порог",
" surface",
" text порога",
" lexeme hi",
" surface",
" text सीमा",
" surface",
" text थ्रेशोल्ड",
" lexeme zh",
" surface",
" text 阈值",
" signal_similar_elements",
" defined-by program_synthesis",
" defined-by synthesis_subject_function",
" role program_synthesis_signal",
" lexeme en",
" surface",
" text \"similar elements\"",
" lexeme ru",
" surface",
" text \"похожие элементы\"",
" surface",
" text \"одинаковые элементы\"",
" lexeme hi",
" surface",
" text \"समान तत्व\"",
" lexeme zh",
" surface",
" text 相似元素",
" signal_count_vowels",
" defined-by program_synthesis",
" defined-by synthesis_domain_vowels",
" role program_synthesis_signal",
" lexeme en",
" surface",
" text \"count vowels\"",
" surface",
" text \"number of vowels\"",
" lexeme ru",
" surface",
" text \"количество гласных\"",
" surface",
" text \"число гласных\"",
" lexeme hi",
" surface",
" text \"स्वरों की संख्या\"",
" lexeme zh",
" surface",
" text 元音数量",
" surface",
" text 元音的数量",
" has_close_elements",
" defined-by signal_distinct_numbers",
" defined-by signal_difference",
" defined-by signal_threshold",
" role program_synthesis_task",
" lexeme en",
" surface",
" text has_close_elements",
" lexeme ru",
" surface",
" text has_close_elements",
" lexeme hi",
" surface",
" text has_close_elements",
" lexeme zh",
" surface",
" text has_close_elements",
" similar_elements",
" defined-by signal_similar_elements",
" role program_synthesis_task",
" lexeme en",
" surface",
" text similar_elements",
" lexeme ru",
" surface",
" text similar_elements",
" lexeme hi",
" surface",
" text similar_elements",
" lexeme zh",
" surface",
" text similar_elements",
" count_vowels",
" defined-by signal_count_vowels",
" role program_synthesis_task",
" lexeme en",
" surface",
" text count_vowels",
" lexeme ru",
" surface",
" text count_vowels",
" lexeme hi",
" surface",
" text count_vowels",
" lexeme zh",
" surface",
" text count_vowels",
"meanings",
" assistant",
" defined-by answer",
" defined-by self_reference",
" defined-by entity",
" role conversational_entity",
" lexeme en",
" surface",
" text assistant",
" surface",
" text \"ai assistant\"",
" lexeme ru",
" surface",
" text ассистент",
" surface",
" text помощник",
" lexeme hi",
" surface",
" text सहायक",
" lexeme zh",
" surface",
" text 助手",
" surface",
" text 助理",
" user",
" defined-by inquiry",
" defined-by entity",
" role conversational_entity",
" lexeme en",
" surface",
" text user",
" lexeme ru",
" surface",
" text пользователь",
" lexeme hi",
" surface",
" text उपयोगकर्ता",
" lexeme zh",
" surface",
" text 用户",
" inquiry",
" defined-by answer",
" defined-by user",
" role conversational_act",
" lexeme en",
" surface",
" text inquiry",
" surface",
" text question",
" surface",
" text request",
" lexeme ru",
" surface",
" text вопрос",
" surface",
" text запрос",
" lexeme hi",
" surface",
" text प्रश्न",
" surface",
" text अनुरोध",
" lexeme zh",
" surface",
" text 询问",
" surface",
" text 请求",
" answer",
" defined-by inquiry",
" defined-by assistant",
" role conversational_act",
" lexeme en",
" surface",
" text answer",
" surface",
" text response",
" surface",
" text reply",
" lexeme ru",
" surface",
" text ответ",
" lexeme hi",
" surface",
" text उत्तर",
" surface",
" text जवाब",
" lexeme zh",
" surface",
" text 回答",
" surface",
" text 答复",
" self_reference",
" defined-by assistant",
" role conversational_concept",
" lexeme en",
" surface",
" text yourself",
" surface",
" text myself",
" lexeme ru",
" surface",
" text себе",
" surface",
" text себя",
" lexeme hi",
" surface",
" text स्वयं",
" surface",
" text खुद",
" lexeme zh",
" surface",
" text 自己",
" capability",
" defined-by assistant",
" defined-by answer",
" role conversational_concept",
" lexeme en",
" surface",
" text capability",
" surface",
" text ability",
" lexeme ru",
" surface",
" text возможность",
" surface",
" text умение",
" lexeme hi",
" surface",
" text क्षमता",
" lexeme zh",
" surface",
" text 能力",
" surface",
" text 功能",
" knowledge",
" defined-by assistant",
" defined-by fact",
" role conversational_concept",
" lexeme en",
" surface",
" text knowledge",
" lexeme ru",
" surface",
" text знание",
" lexeme hi",
" surface",
" text ज्ञान",
" lexeme zh",
" surface",
" text 知识",
" fact",
" defined-by knowledge",
" role conversational_concept",
" role knowledge_inventory_noun",
" lexeme en",
" surface",
" text fact",
" surface",
" text facts",
" lexeme ru",
" surface",
" text факт",
" surface",
" text факты",
" lexeme hi",
" surface",
" text तथ्य",
" lexeme zh",
" surface",
" text 事实",
" surface",
" text 事實",
" knowledge_inventory_probe",
" defined-by inquiry",
" role knowledge_inventory_interrogative",
" lexeme en",
" surface",
" text what",
" surface",
" text which",
" surface",
" text list",
" surface",
" text show",
" lexeme ru",
" surface",
" text какие",
" surface",
" text что",
" surface",
" text перечисли",
" surface",
" text покажи",
" surface",
" text назови",
" lexeme hi",
" surface",
" text कौन",
" surface",
" text क्या",
" surface",
" text सूची",
" surface",
" text सूचीबद्ध",
" surface",
" text बताओ",
" surface",
" text दिखाओ",
" lexeme zh",
" surface",
" text 哪些",
" surface",
" text 什么",
" surface",
" text 什麼",
" surface",
" text 你知道",
" surface",
" text 您知道",
" surface",
" text 你有",
" surface",
" text 您有",
" assistant_knowing",
" defined-by knowledge",
" defined-by assistant",
" role knowledge_possession",
" lexeme en",
" surface",
" text \"you know\"",
" surface",
" text \"do you know\"",
" surface",
" text \"you have\"",
" surface",
" text \"available to you\"",
" surface",
" text \"in your knowledge\"",
" surface",
" text \"known to you\"",
" lexeme ru",
" surface",
" text \"ты знаешь\"",
" surface",
" text знаешь",
" surface",
" text \"тебе известно\"",
" surface",
" text \"тебе известны\"",
" surface",
" text \"тебе известен\"",
" surface",
" text \"у тебя есть\"",
" surface",
" text \"твои знания\"",
" surface",
" text \"что ты знаешь\"",
" lexeme hi",
" surface",
" text तुम",
" surface",
" text आप",
" surface",
" text जानते",
" surface",
" text जानती",
" surface",
" text आपके",
" surface",
" text तुम्हारे",
" lexeme zh",
" surface",
" text 你知道",
" surface",
" text 您知道",
" surface",
" text 你有",
" surface",
" text 您有",
" knowledge_inventory_query",
" defined-by knowledge",
" defined-by fact",
" role knowledge_inventory_phrase",
" lexeme en",
" surface",
" text \"what do you know in general\"",
" surface",
" text \"what do you know about the world\"",
" surface",
" text \"what is known to you\"",
" surface",
" text \"what knowledge do you have\"",
" lexeme ru",
" surface",
" text \"что тебе вообще известно\"",
" surface",
" text \"что тебе известно\"",
" surface",
" text \"что ты вообще знаешь\"",
" surface",
" text \"что ты знаешь об окружающем мире\"",
" surface",
" text \"известно об окружающем мире\"",
" surface",
" text \"знаешь про окружающий мир\"",
" surface",
" text \"знаешь об окружающем мире\"",
" lexeme hi",
" surface",
" text \"आप क्या जानते हैं\"",
" surface",
" text \"तुम क्या जानते हो\"",
" surface",
" text \"आपको क्या पता है\"",
" lexeme zh",
" surface",
" text 你知道什么",
" surface",
" text 您知道什么",
" surface",
" text 你知道哪些",
" introduction",
" defined-by self_reference",
" defined-by answer",
" role conversational_concept",
" lexeme en",
" surface",
" text introduction",
" lexeme ru",
" surface",
" text знакомство",
" lexeme hi",
" surface",
" text परिचय",
" lexeme zh",
" surface",
" text 介绍",
" clarification",
" defined-by inquiry",
" defined-by answer",
" role conversational_concept",
" lexeme en",
" surface",
" text clarification",
" lexeme ru",
" surface",
" text разъяснение",
" lexeme hi",
" surface",
" text स्पष्टीकरण",
" lexeme zh",
" surface",
" text 澄清",
" understanding",
" defined-by clarification",
" defined-by knowledge",
" role conversational_concept",
" lexeme en",
" surface",
" text understanding",
" lexeme ru",
" surface",
" text понимание",
" lexeme hi",
" surface",
" text समझ",
" lexeme zh",
" surface",
" text 理解",
" clarification_request",
" defined-by clarification",
" defined-by understanding",
" defined-by inquiry",
" role clarification_request",
" lexeme en",
" surface",
" text \"i don t understand\"",
" surface",
" text \"i dont understand\"",
" surface",
" text \"i didn t understand\"",
" surface",
" text \"i didnt understand\"",
" surface",
" text \"don t understand\"",
" surface",
" text \"dont understand\"",
" surface",
" text \"didn t understand\"",
" surface",
" text \"didnt understand\"",
" surface",
" text \"what do you mean\"",
" surface",
" text \"i m confused\"",
" surface",
" text \"im confused\"",
" surface",
" text \"i am confused\"",
" lexeme ru",
" surface",
" text \"не понял\"",
" surface",
" text \"не понимаю\"",
" surface",
" text \"не поняла\"",
" surface",
" text \"не понятно\"",
" surface",
" text непонятно",
" lexeme hi",
" surface",
" text \"समझ नहीं आया\"",
" surface",
" text \"समझ नहीं आई\"",
" lexeme zh",
" surface",
" text 我不明白",
" surface",
" text 我不懂",
" surface",
" text 听不懂",
" capability_query",
" defined-by capability",
" defined-by inquiry",
" role capability_query",
" lexeme en",
" surface",
" text \"what can you do\"",
" surface",
" text \"what you can do\"",
" surface",
" text \"what are your capabilities\"",
" surface",
" text \"what are you capable of\"",
" surface",
" text \"what do you do\"",
" surface",
" text \"show me what you can do\"",
" surface",
" text \"what features do you have\"",
" surface",
" text \"how can you help\"",
" surface",
" text \"what are your features\"",
" lexeme ru",
" surface",
" text \"что ты умеешь\"",
" surface",
" text \"чем ты можешь\"",
" surface",
" text \"чём ты можешь\"",
" surface",
" text \"что ты можешь\"",
" surface",
" text \"что умеет\"",
" surface",
" text \"что можешь\"",
" surface",
" text \"в чем ты можешь быть полезен\"",
" surface",
" text \"в чём ты можешь быть полезен\"",
" surface",
" text \"твои возможности\"",
" surface",
" text \"что за дичь\"",
" surface",
" text \"что это такое\"",
" surface",
" text \"что происходит\"",
" surface",
" text \"что ты делаешь\"",
" lexeme hi",
" surface",
" text \"आप क्या कर सकते\"",
" surface",
" text \"तुम क्या कर सकते\"",
" surface",
" text \"क्या क्या कर सकते\"",
" lexeme zh",
" surface",
" text 你能做什么",
" surface",
" text 你会做什么",
" surface",
" text 你有什么功能",
" surface",
" text 你能干什么",
" capability_query_more",
" defined-by capability_query",
" defined-by capability",
" role capability_query_more",
" lexeme en",
" surface",
" text \"what else can you do\"",
" surface",
" text \"what else do you do\"",
" surface",
" text \"what other things can you do\"",
" lexeme ru",
" surface",
" text \"что ещё ты умеешь\"",
" surface",
" text \"что еще ты умеешь\"",
" surface",
" text \"что ещё можешь\"",
" surface",
" text \"что еще можешь\"",
" surface",
" text \"что ты ещё умеешь\"",
" surface",
" text \"что ты еще умеешь\"",
" lexeme hi",
" surface",
" text \"और आप क्या कर सकते\"",
" surface",
" text \"और क्या कर सकते\"",
" lexeme zh",
" surface",
" text 你还能做什么",
" surface",
" text 你还会做什么",
" self_fact_query",
" defined-by fact",
" defined-by self_reference",
" defined-by inquiry",
" role self_fact_query",
" lexeme en",
" surface",
" text \"facts you know about yourself\"",
" surface",
" text \"facts about yourself\"",
" surface",
" text \"self facts\"",
" surface",
" text \"list all facts you know about yourself\"",
" lexeme ru",
" surface",
" text \"какие факты ты знаешь о себе\"",
" surface",
" text \"факты о себе\"",
" lexeme hi",
" surface",
" text \"अपने बारे में तथ्य\"",
" surface",
" text \"स्वयं के बारे में तथ्य\"",
" lexeme zh",
" surface",
" text 关于你自己的事实",
" surface",
" text 自我事实",
" self_introduction_request",
" defined-by introduction",
" defined-by self_reference",
" defined-by inquiry",
" role self_introduction_request",
" lexeme en",
" surface",
" text \"tell me about yourself\"",
" surface",
" text \"introduce yourself\"",
" surface",
" text \"let s get acquainted\"",
" surface",
" text \"lets get acquainted\"",
" surface",
" text \"let us get acquainted\"",
" surface",
" text \"let s get to know each other\"",
" lexeme ru",
" surface",
" text \"расскажи о себе\"",
" surface",
" text \"расскажи мне о себе\"",
" surface",
" text \"расскажи про себя\"",
" surface",
" text \"опиши себя\"",
" surface",
" text представься",
" surface",
" text \"давай знакомиться\"",
" surface",
" text \"давай познакомимся\"",
" surface",
" text \"давайте познакомимся\"",
" lexeme hi",
" surface",
" text \"अपने बारे में बताओ\"",
" surface",
" text \"अपना परिचय दो\"",
" surface",
" text \"चलो परिचय करते हैं\"",
" surface",
" text \"आइए परिचय करें\"",
" surface",
" text \"चलो एक दूसरे को जानें\"",
" lexeme zh",
" surface",
" text 介绍一下你自己",
" surface",
" text 告诉我你自己",
" surface",
" text 介紹一下你自己",
" surface",
" text 告訴我你自己",
" surface",
" text 我们认识一下",
" surface",
" text 认识一下吧",
" surface",
" text 让我们认识一下",
" conversation_summary_directive",
" defined-by inquiry",
" defined-by answer",
" role conversation_summary_directive",
" lexeme en",
" surface",
" text summarize",
" surface",
" text summarise",
" surface",
" text summary",
" lexeme ru",
" surface",
" text суммируй",
" surface",
" text суммируйте",
" surface",
" text резюмируй",
" surface",
" text резюмируйте",
" surface",
" text резюме",
" lexeme hi",
" surface",
" text सारांश",
" surface",
" text संक्षेप",
" surface",
" text सारांशित",
" lexeme zh",
" surface",
" text 总结",
" surface",
" text 總結",
" surface",
" text 概括",
" surface",
" text 摘要",
" surface",
" text 归纳",
" surface",
" text 歸納",
" conversation_reference",
" defined-by inquiry",
" defined-by answer",
" role conversation_reference",
" lexeme en",
" surface",
" text conversation",
" surface",
" text chat",
" surface",
" text dialogue",
" surface",
" text dialog",
" surface",
" text discussion",
" lexeme ru",
" surface",
" text беседа",
" surface",
" text беседы",
" surface",
" text беседу",
" surface",
" text беседе",
" surface",
" text разговор",
" surface",
" text разговора",
" surface",
" text разговоре",
" surface",
" text разговором",
" surface",
" text чат",
" surface",
" text чата",
" surface",
" text чате",
" surface",
" text диалог",
" surface",
" text диалога",
" lexeme hi",
" surface",
" text बातचीत",
" surface",
" text संवाद",
" surface",
" text वार्तालाप",
" lexeme zh",
" surface",
" text 对话",
" surface",
" text 對話",
" surface",
" text 聊天",
" surface",
" text 会话",
" surface",
" text 會話",
" surface",
" text 谈话",
" surface",
" text 談話",
" conversation_summary_phrase",
" defined-by inquiry",
" defined-by conversation_reference",
" role conversation_summary_phrase",
" lexeme en",
" surface",
" text \"what have we talked about\"",
" surface",
" text \"what did we talk about\"",
" surface",
" text \"what have we discussed\"",
" surface",
" text \"summary of our chat\"",
" surface",
" text \"summary of our conversation\"",
" surface",
" text \"summarize so far\"",
" surface",
" text \"summarise so far\"",
" lexeme ru",
" surface",
" text \"о чём мы разговаривали\"",
" surface",
" text \"о чем мы разговаривали\"",
" surface",
" text \"о чём мы говорили\"",
" surface",
" text \"о чем мы говорили\"",
" lexeme hi",
" surface",
" text \"हमने किस बारे में बात की\"",
" surface",
" text \"हमने क्या बात की\"",
" lexeme zh",
" surface",
" text 我们聊了什么",
" surface",
" text 我们谈了什么",
" surface",
" text 我们说了什么",
" surface",
" text 我們聊了什麼",
" conversation_summary_courtesy",
" defined-by inquiry",
" defined-by conversation_summary_directive",
" role conversation_summary_courtesy",
" lexeme en",
" surface",
" text \"give me a summary\"",
" surface",
" text \"can you summarize\"",
" surface",
" text \"can you summarise\"",
" surface",
" text \"please summarize\"",
" surface",
" text \"please summarise\"",
" lexeme ru",
" surface",
" text \"подведи итог\"",
" surface",
" text \"подведи итоги\"",
" surface",
" text \"краткое резюме\"",
" surface",
" text \"сделай резюме\"",
" surface",
" text \"краткое содержание\"",
" lexeme hi",
" surface",
" text \"सार दो\"",
" surface",
" text \"सारांश दो\"",
" surface",
" text \"संक्षेप में बताओ\"",
" lexeme zh",
" surface",
" text 帮我总结",
" surface",
" text 帮我概括",
" surface",
" text 请总结",
" surface",
" text 请概括",
" surface",
" text 总结一下",
" surface",
" text 概括一下",
" who_is_question",
" defined-by inquiry",
" role who_question_lead",
" role who_question_tail",
" lexeme en",
" surface",
" text \"who is …\"",
" surface",
" text \"who was …\"",
" surface",
" text \"who are …\"",
" lexeme ru",
" surface",
" text \"кто такой …\"",
" surface",
" text \"кто такая …\"",
" surface",
" text \"кто это …\"",
" surface",
" text \"кто …\"",
" lexeme hi",
" surface",
" text \"… कौन है\"",
" surface",
" text \"… कौन हैं\"",
" lexeme zh",
" surface",
" text …是谁",
" surface",
" text …是誰",
" interrogative_opener",
" defined-by inquiry",
" role interrogative_opener",
" lexeme en",
" surface",
" text what",
" surface",
" text who",
" surface",
" text why",
" surface",
" text where",
" surface",
" text when",
" surface",
" text how",
" surface",
" text which",
" lexeme ru",
" surface",
" text что",
" surface",
" text кто",
" surface",
" text почему",
" surface",
" text где",
" surface",
" text когда",
" surface",
" text как",
" lexeme hi",
" surface",
" text क्या",
" surface",
" text कौन",
" surface",
" text क्यों",
" surface",
" text कहाँ",
" surface",
" text कब",
" surface",
" text कैसे",
" surface",
" text कौनसा",
" lexeme zh",
" surface",
" text 什么",
" surface",
" text 谁",
" surface",
" text 为什么",
" surface",
" text 哪里",
" surface",
" text 何时",
" surface",
" text 如何",
" surface",
" text 哪个",
"meanings",
" mechanism_inquiry",
" defined-by inquiry",
" defined-by action",
" role mechanism_inquiry",
" lexeme en",
" surface",
" text \"how it works\"",
" surface",
" text \"how does it work\"",
" surface",
" text \"how does …\"",
" surface",
" text \"how do …\"",
" surface",
" text \"how did …\"",
" surface",
" text \"how is …\"",
" surface",
" text \"how … works\"",
" surface",
" text \"how … work\"",
" lexeme ru",
" surface",
" text \"как это работает\"",
" surface",
" text \"как оно работает\"",
" surface",
" text \"как устроен …\"",
" surface",
" text \"как устроена …\"",
" surface",
" text \"как устроено …\"",
" surface",
" text \"как устроены …\"",
" surface",
" text \"как работает …\"",
" surface",
" text \"как работают …\"",
" surface",
" text \"как … работает\"",
" surface",
" text \"как … работают\"",
" surface",
" text \"… как работает\"",
" surface",
" text \"… как работают\"",
" lexeme hi",
" surface",
" text \"यह कैसे काम करता है\"",
" surface",
" text \"यह कैसे काम करती है\"",
" surface",
" text \"यह कैसे काम करता\"",
" surface",
" text \"… कैसे काम करता है\"",
" surface",
" text \"… कैसे काम करती है\"",
" surface",
" text \"… कैसे काम करते हैं\"",
" surface",
" text \"… कैसे काम करता\"",
" surface",
" text \"… कैसे काम करती\"",
" surface",
" text \"… कैसे काम करते\"",
" lexeme zh",
" surface",
" text 这是如何工作的",
" surface",
" text 这是怎么工作的",
" surface",
" text 这个如何工作",
" surface",
" text 它如何工作",
" surface",
" text 它是如何工作的",
" surface",
" text 它怎么工作",
" surface",
" text \"… 是如何工作的\"",
" surface",
" text …是如何工作的",
" surface",
" text \"… 是怎么工作的\"",
" surface",
" text …是怎么工作的",
" surface",
" text \"… 如何工作\"",
" surface",
" text …如何工作",
" surface",
" text \"… 怎么工作\"",
" surface",
" text …怎么工作",
" surface",
" text \"… 的工作原理是什么\"",
" surface",
" text …的工作原理是什么",
" procedural_request",
" defined-by inquiry",
" defined-by action",
" role procedural_request",
" lexeme en",
" surface",
" text \"please tell me how to …\"",
" surface",
" text \"please show me how to …\"",
" surface",
" text \"tell me how to …\"",
" surface",
" text \"show me how to …\"",
" surface",
" text \"what are the steps to …\"",
" surface",
" text \"what steps do i need to …\"",
" surface",
" text \"what steps do we need to …\"",
" surface",
" text \"how should i …\"",
" surface",
" text \"how should we …\"",
" surface",
" text \"how could i …\"",
" surface",
" text \"how could we …\"",
" surface",
" text \"how would i …\"",
" surface",
" text \"how would we …\"",
" surface",
" text \"how can i …\"",
" surface",
" text \"how can we …\"",
" surface",
" text \"how do i …\"",
" surface",
" text \"how do we …\"",
" surface",
" text \"how to do …\"",
" action do",
" surface",
" text \"how to …\"",
" lexeme ru",
" surface",
" text \"как сделать …\"",
" action do",
" surface",
" text \"как делать …\"",
" action do",
" surface",
" text \"как выполнить …\"",
" action perform",
" surface",
" text \"как реализовать …\"",
" action implement",
" surface",
" text \"как создать …\"",
" action create",
" surface",
" text \"как написать …\"",
" action write",
" lexeme hi",
" surface",
" text \"कैसे करें …\"",
" action do",
" surface",
" text \"कैसे करे …\"",
" action do",
" surface",
" text \"कैसे लागू करें …\"",
" action implement",
" surface",
" text \"कैसे बनाएं …\"",
" action create",
" surface",
" text \"कैसे बनाएँ …\"",
" action create",
" surface",
" text \"कैसे लिखें …\"",
" action write",
" lexeme zh",
" surface",
" text \"如何做 …\"",
" action do",
" surface",
" text \"怎么做 …\"",
" action do",
" surface",
" text \"如何实现 …\"",
" action implement",
" surface",
" text \"怎么实现 …\"",
" action implement",
" surface",
" text \"如何创建 …\"",
" action create",
" surface",
" text \"怎么创建 …\"",
" action create",
" surface",
" text \"如何写 …\"",
" action write",
" surface",
" text \"怎么写 …\"",
" action write",
" mechanism_predicate",
" defined-by action",
" defined-by mechanism_inquiry",
" role mechanism_predicate",
" lexeme en",
" surface",
" text \"… work\"",
" surface",
" text \"… works\"",
" surface",
" text \"… structured\"",
" surface",
" text \"… organized\"",
" surface",
" text \"… organised\"",
" surface",
" text \"… built\"",
" lexeme ru",
" surface",
" text \"… работает\"",
" surface",
" text \"… устроен\"",
" lexeme hi",
" surface",
" text \"… काम करता है\"",
" surface",
" text \"… बना है\"",
" lexeme zh",
" surface",
" text …工作",
" surface",
" text …构建",
" detail_modifier",
" defined-by property",
" defined-by mechanism_inquiry",
" role detail_modifier",
" lexeme en",
" surface",
" text \"… in detail\"",
" surface",
" text \"… internally\"",
" surface",
" text \"… exactly\"",
" surface",
" text \"… please\"",
" lexeme ru",
" surface",
" text \"… подробнее\"",
" surface",
" text \"… подробно\"",
" surface",
" text \"… пожалуйста\"",
" lexeme hi",
" surface",
" text \"… विस्तार से\"",
" surface",
" text \"… कृपया\"",
" lexeme zh",
" surface",
" text …详细",
" surface",
" text …请",
" non_referential_subject",
" defined-by entity",
" defined-by mechanism_inquiry",
" role non_referential_subject",
" lexeme en",
" surface",
" text it",
" surface",
" text this",
" surface",
" text that",
" surface",
" text you",
" surface",
" text yourself",
" surface",
" text does",
" surface",
" text do",
" surface",
" text \"does …\"",
" surface",
" text \"do …\"",
" surface",
" text \"to …\"",
" surface",
" text \"you …\"",
" lexeme ru",
" surface",
" text это",
" surface",
" text оно",
" surface",
" text он",
" surface",
" text она",
" surface",
" text они",
" surface",
" text ты",
" surface",
" text вы",
" lexeme hi",
" surface",
" text यह",
" surface",
" text ये",
" lexeme zh",
" surface",
" text 这",
" surface",
" text 这个",
" surface",
" text 它",
" procedural_task_modifier",
" defined-by property",
" defined-by procedural_request",
" role procedural_task_modifier",
" lexeme en",
" surface",
" text \"… step by step\"",
" surface",
" text \"… in steps\"",
" surface",
" text \"… with steps\"",
" surface",
" text \"… for me\"",
" surface",
" text \"… please\"",
" lexeme ru",
" surface",
" text \"… напиши по шагам\"",
" surface",
" text \"… по шагам\"",
" surface",
" text \"… пошагово\"",
" surface",
" text \"… пожалуйста\"",
" lexeme hi",
" surface",
" text \"… चरणों में लिखो\"",
" surface",
" text \"… चरणों में बताओ\"",
" surface",
" text \"… कदम दर कदम\"",
" surface",
" text \"… कृपया\"",
" lexeme zh",
" surface",
" text \"… 按步骤写\"",
" surface",
" text \"… 按步骤说明\"",
" surface",
" text \"… 一步一步写\"",
" surface",
" text \"… 请\"",
" common_typo",
" defined-by relation",
" role common_typo",
" lexeme en",
" surface",
" text dirven",
" action driven",
" lexeme ru",
" surface",
" text руский",
" action русский",
" lexeme hi",
" surface",
" text वेबसाईट",
" action वेबसाइट",
" lexeme zh",
" surface",
" text 登陆",
" action 登录",
" topic_scan_stop_word",
" defined-by concept",
" role topic_scan_stop_word",
" lexeme en",
" surface",
" text i",
" surface",
" text the",
" surface",
" text a",
" surface",
" text an",
" surface",
" text in",
" surface",
" text to",
" surface",
" text for",
" surface",
" text of",
" surface",
" text and",
" surface",
" text or",
" surface",
" text source",
" lexeme ru",
" surface",
" text и",
" surface",
" text или",
" surface",
" text в",
" surface",
" text к",
" surface",
" text для",
" surface",
" text из",
" surface",
" text источник",
" lexeme hi",
" surface",
" text और",
" surface",
" text या",
" surface",
" text में",
" surface",
" text को",
" surface",
" text के",
" surface",
" text का",
" surface",
" text स्रोत",
" lexeme zh",
" surface",
" text 和",
" surface",
" text 或",
" surface",
" text 在",
" surface",
" text 的",
" surface",
" text 来源",
"meanings",
" causal_interrogative",
" defined-by inquiry",
" role causal_interrogative",
" lexeme en",
" surface",
" text why",
" lexeme ru",
" surface",
" text почему",
" lexeme hi",
" surface",
" text क्यों",
" lexeme zh",
" surface",
" text 为什么",
" prior_answer_reference",
" defined-by answer",
" role prior_answer_reference",
" lexeme en",
" surface",
" text answer",
" lexeme ru",
" surface",
" text ответ",
" lexeme hi",
" surface",
" text जवाब",
" surface",
" text उत्तर",
" lexeme zh",
" surface",
" text 回答",
" answer_rationale_inquiry",
" defined-by causal_interrogative",
" defined-by prior_answer_reference",
" defined-by inquiry",
" role answer_rationale_lead",
" lexeme en",
" surface",
" text \"why …\"",
" surface",
" text \"why did you answer\"",
" lexeme ru",
" surface",
" text \"почему …\"",
" surface",
" text \"почему ты ответил\"",
" surface",
" text \"почему ты так ответил\"",
" surface",
" text \"почему вы ответили\"",
" lexeme hi",
" surface",
" text \"ऐसा जवाब क्यों दिया\"",
" lexeme zh",
" surface",
" text 为什么这样回答",
" assistant_self_reference",
" defined-by assistant",
" defined-by self_reference",
" role assistant_self_reference",
" lexeme en",
" surface",
" text you",
" surface",
" text your",
" surface",
" text \"formal ai\"",
" lexeme ru",
" surface",
" text ты",
" surface",
" text теб",
" surface",
" text твоя",
" surface",
" text твой",
" surface",
" text тво",
" surface",
" text вы",
" lexeme hi",
" surface",
" text आप",
" surface",
" text तुम",
" lexeme zh",
" surface",
" text 你",
" surface",
" text 您",
" assistant_mechanism_inquiry",
" defined-by mechanism_inquiry",
" defined-by assistant",
" role assistant_mechanism_inquiry",
" lexeme en",
" surface",
" text \"how do you work\"",
" surface",
" text \"how does this work\"",
" surface",
" text \"how does it work\"",
" surface",
" text \"show me how you work\"",
" surface",
" text \"explain how you work\"",
" lexeme ru",
" surface",
" text \"как ты работаешь\"",
" surface",
" text \"покажи как ты работаешь\"",
" surface",
" text \"расскажи как ты работаешь\"",
" surface",
" text \"объясни как ты работаешь\"",
" surface",
" text \"как ты устроен\"",
" surface",
" text \"покажи как ты устроен\"",
" surface",
" text \"какая у тебя модель окружающего мира\"",
" surface",
" text \"модель окружающего мира\"",
" surface",
" text \"идея твоей разработки\"",
" surface",
" text \"идея твоего проекта\"",
" surface",
" text \"зачем тебя разработ\"",
" lexeme hi",
" surface",
" text \"तुम कैसे काम करते हो\"",
" surface",
" text \"आप कैसे काम करते हैं\"",
" lexeme zh",
" surface",
" text 你是怎么工作的",
" surface",
" text 你怎么运作",
" operating_principle",
" defined-by concept",
" defined-by action",
" role operating_principle",
" lexeme en",
" surface",
" text \"operating principle\"",
" lexeme ru",
" surface",
" text \"принцип работы\"",
" lexeme hi",
" surface",
" text \"कार्य सिद्धांत\"",
" lexeme zh",
" surface",
" text 工作原理",
" architecture_concept",
" defined-by concept",
" defined-by assistant",
" role architecture_concept",
" lexeme en",
" surface",
" text llm",
" surface",
" text \"large language model\"",
" surface",
" text \"language model\"",
" surface",
" text \"openai api\"",
" surface",
" text openai",
" surface",
" text \"neural inference\"",
" surface",
" text \"neural network\"",
" surface",
" text \"links notation rules\"",
" surface",
" text \"local rules\"",
" surface",
" text \"world model\"",
" surface",
" text \"model of the world\"",
" lexeme ru",
" surface",
" text бям",
" surface",
" text \"языковая модель\"",
" surface",
" text \"языковой моделью\"",
" surface",
" text нейросет",
" surface",
" text нейрон",
" surface",
" text \"локальных правил\"",
" surface",
" text \"локальных правилах\"",
" surface",
" text \"область знаний\"",
" surface",
" text \"модель окружающего мира\"",
" surface",
" text \"модель мира\"",
" surface",
" text \"принцип работы\"",
" surface",
" text \"идея твоей разработки\"",
" surface",
" text \"идея твоего проекта\"",
" surface",
" text \"зачем тебя разработ\"",
" surface",
" text ссылк",
" lexeme hi",
" surface",
" text न्यूरल",
" surface",
" text \"भाषा मॉडल\"",
" lexeme zh",
" surface",
" text 神经",
" surface",
" text 語言模型",
" surface",
" text 语言模型",
"meanings",
" web_resource",
" defined-by entity",
" role web_navigation_concept",
" lexeme en",
" surface",
" text url",
" surface",
" text \"web page\"",
" surface",
" text website",
" lexeme ru",
" surface",
" text сайт",
" surface",
" text страница",
" surface",
" text ссылка",
" surface",
" text урл",
" lexeme hi",
" surface",
" text वेबसाइट",
" surface",
" text \"वेब पेज\"",
" surface",
" text लिंक",
" lexeme zh",
" surface",
" text 网址",
" surface",
" text 网页",
" surface",
" text 网站",
" http_fetch",
" defined-by inquiry",
" defined-by action",
" defined-by web_resource",
" role http_fetch",
" lexeme en",
" surface",
" text \"fetch …\"",
" surface",
" text \"fetch url …\"",
" surface",
" text \"http fetch …\"",
" surface",
" text \"request …\"",
" surface",
" text \"make request to …\"",
" surface",
" text \"send request to …\"",
" surface",
" text \"make a request to\"",
" surface",
" text \"make an http request to\"",
" surface",
" text \"send a request to\"",
" surface",
" text \"send an http request to\"",
" surface",
" text \"http request to\"",
" surface",
" text \"http get to\"",
" surface",
" text \"fetch the url\"",
" surface",
" text \"fetch this url\"",
" surface",
" text \"fetch the page\"",
" lexeme ru",
" surface",
" text \"сделай запрос …\"",
" surface",
" text \"сделай http запрос …\"",
" surface",
" text \"выполни запрос …\"",
" surface",
" text \"выполни http запрос …\"",
" surface",
" text \"запроси …\"",
" surface",
" text \"получи …\"",
" surface",
" text \"http запрос к …\"",
" surface",
" text \"http запрос на …\"",
" surface",
" text \"сделать запрос к …\"",
" surface",
" text \"выполнить запрос к …\"",
" surface",
" text \"сделай запрос к\"",
" surface",
" text \"сделай запрос на\"",
" surface",
" text \"сделай http запрос к\"",
" surface",
" text \"сделай http запрос на\"",
" surface",
" text \"выполни запрос к\"",
" surface",
" text \"выполни запрос на\"",
" surface",
" text \"выполни http запрос к\"",
" surface",
" text \"выполни http запрос на\"",
" surface",
" text \"запрос к\"",
" surface",
" text \"запрос на\"",
" surface",
" text \"http запрос к\"",
" surface",
" text \"http запрос на\"",
" lexeme hi",
" surface",
" text \"अनुरोध भेजें\"",
" surface",
" text \"अनुरोध करें\"",
" lexeme zh",
" surface",
" text 发送请求",
" surface",
" text 获取",
" url_navigate",
" defined-by inquiry",
" defined-by action",
" defined-by web_resource",
" role url_navigate",
" lexeme en",
" surface",
" text \"navigate to …\"",
" surface",
" text \"navigate …\"",
" surface",
" text \"go to …\"",
" surface",
" text \"goto …\"",
" surface",
" text \"visit …\"",
" surface",
" text \"browse to …\"",
" surface",
" text \"browse …\"",
" surface",
" text \"show …\"",
" surface",
" text \"show me …\"",
" surface",
" text \"display …\"",
" surface",
" text \"load …\"",
" surface",
" text \"open …\"",
" surface",
" text \"open url …\"",
" surface",
" text \"open the url …\"",
" surface",
" text \"open site …\"",
" surface",
" text \"open website …\"",
" surface",
" text \"open page …\"",
" surface",
" text \"open the page …\"",
" surface",
" text \"open the website …\"",
" surface",
" text \"take me to …\"",
" surface",
" text \"preview …\"",
" surface",
" text \"view …\"",
" surface",
" text \"see …\"",
" surface",
" text \"get …\"",
" surface",
" text \"navigate to\"",
" surface",
" text \"go to\"",
" surface",
" text goto",
" surface",
" text \"browse to\"",
" surface",
" text \"take me to\"",
" surface",
" text \"open the page\"",
" surface",
" text \"open the site\"",
" surface",
" text \"open the website\"",
" surface",
" text \"open the url\"",
" surface",
" text \"open url\"",
" lexeme ru",
" surface",
" text \"перейди …\"",
" surface",
" text \"перейди на …\"",
" surface",
" text \"переходи на …\"",
" surface",
" text \"переходи …\"",
" surface",
" text \"перейдите на …\"",
" surface",
" text \"открой …\"",
" surface",
" text \"открой сайт …\"",
" surface",
" text \"открой страницу …\"",
" surface",
" text \"открой ссылку …\"",
" surface",
" text \"открой урл …\"",
" surface",
" text \"покажи …\"",
" surface",
" text \"покажи сайт …\"",
" surface",
" text \"покажи страницу …\"",
" surface",
" text \"покажи мне …\"",
" surface",
" text \"загрузи …\"",
" surface",
" text \"загрузи страницу …\"",
" surface",
" text \"посети …\"",
" surface",
" text \"зайди на …\"",
" surface",
" text \"зайди …\"",
" surface",
" text \"просмотри …\"",
" surface",
" text \"отобрази …\"",
" surface",
" text \"перейди на\"",
" surface",
" text \"переходи на\"",
" surface",
" text \"перейдите на\"",
" surface",
" text \"открой сайт\"",
" surface",
" text \"открой страницу\"",
" surface",
" text \"открой ссылку\"",
" surface",
" text \"открой урл\"",
" surface",
" text \"покажи сайт\"",
" surface",
" text \"покажи страницу\"",
" surface",
" text \"зайди на\"",
" lexeme hi",
" surface",
" text \"पर जाएं\"",
" surface",
" text खोलें",
" surface",
" text देखें",
" lexeme zh",
" surface",
" text 打开",
" surface",
" text 访问",
" surface",
" text 前往",
" surface",
" text 查看",
"meanings",
" web_search",
" defined-by inquiry",
" defined-by action",
" defined-by web_resource",
" role web_search_concept",
" lexeme en",
" surface",
" text \"web search\"",
" surface",
" text \"search the web\"",
" lexeme ru",
" surface",
" text \"поиск в интернете\"",
" surface",
" text веб-поиск",
" lexeme hi",
" surface",
" text \"वेब खोज\"",
" surface",
" text \"इंटरनेट पर खोज\"",
" lexeme zh",
" surface",
" text 网络搜索",
" surface",
" text 在网上搜索",
" reference_internet",
" defined-by web_resource",
" defined-by entity",
" role web_search_signal",
" role web_search_source_only",
" role web_medium",
" lexeme en",
" surface",
" text \" web \"",
" surface",
" text \" internet \"",
" surface",
" text \" online \"",
" lexeme ru",
" surface",
" text \" интернете \"",
" surface",
" text \" интернет \"",
" surface",
" text \" онлайн \"",
" surface",
" text \" сети \"",
" lexeme hi",
" surface",
" text इंटरनेट",
" surface",
" text ऑनलाइन",
" surface",
" text वेब",
" lexeme zh",
" surface",
" text 网上",
" surface",
" text 網上",
" surface",
" text 在线",
" surface",
" text 在線",
" surface",
" text 互联网",
" surface",
" text 網路",
" surface",
" text 网络",
" reference_encyclopedia",
" defined-by web_resource",
" defined-by entity",
" role web_search_signal",
" role web_search_source_only",
" lexeme en",
" surface",
" text \" wikipedia \"",
" surface",
" text \" wikidata \"",
" surface",
" text \" wiktionary \"",
" lexeme ru",
" surface",
" text \" википед\"",
" surface",
" text \" викиданн\"",
" surface",
" text википедии",
" lexeme hi",
" surface",
" text विकिपीडिया",
" surface",
" text विकिडाटा",
" lexeme zh",
" surface",
" text 百科",
" surface",
" text 维基百科",
" surface",
" text 維基百科",
" surface",
" text 维基数据",
" surface",
" text 維基數據",
" reference_information",
" defined-by concept",
" role web_search_signal",
" lexeme en",
" surface",
" text \" information \"",
" surface",
" text \" info \"",
" surface",
" text \" details \"",
" surface",
" text \" data \"",
" surface",
" text \" material \"",
" surface",
" text \" materials \"",
" surface",
" text \" resource \"",
" surface",
" text \" resources \"",
" surface",
" text \" source \"",
" surface",
" text \" sources \"",
" surface",
" text \" article \"",
" surface",
" text \" articles \"",
" surface",
" text \" fact \"",
" surface",
" text \" facts \"",
" lexeme ru",
" surface",
" text \" информац\"",
" surface",
" text \" инфу \"",
" surface",
" text \" сведения \"",
" surface",
" text \" материал\"",
" surface",
" text \" данные \"",
" surface",
" text \" источник\"",
" lexeme hi",
" surface",
" text जानकारी",
" surface",
" text सूचना",
" surface",
" text विवरण",
" surface",
" text सामग्री",
" surface",
" text स्रोत",
" surface",
" text लेख",
" lexeme zh",
" surface",
" text 信息",
" surface",
" text 資料",
" surface",
" text 资料",
" surface",
" text 内容",
" surface",
" text 來源",
" surface",
" text 来源",
" surface",
" text 资源",
" surface",
" text 資源",
" surface",
" text 文章",
" reference_news",
" defined-by web_resource",
" defined-by concept",
" role web_search_signal",
" role web_search_news_subject",
" lexeme en",
" surface",
" text \" news \"",
" surface",
" text \" headlines \"",
" lexeme ru",
" surface",
" text \" новост\"",
" lexeme hi",
" surface",
" text समाचार",
" surface",
" text खबर",
" lexeme zh",
" surface",
" text 新闻",
" surface",
" text 新聞",
" surface",
" text 头条",
" surface",
" text 頭條",
" news_recency_request",
" defined-by time",
" role web_search_news_recency",
" lexeme en",
" surface",
" text \" latest \"",
" surface",
" text \" current \"",
" surface",
" text \" breaking \"",
" surface",
" text \" today \"",
" lexeme ru",
" surface",
" text \" последн\"",
" surface",
" text \" свеж\"",
" surface",
" text \" актуальн\"",
" surface",
" text \" сегодня\"",
" lexeme hi",
" surface",
" text नवीनतम",
" surface",
" text ताज़ा",
" surface",
" text ताजा",
" surface",
" text आज",
" lexeme zh",
" surface",
" text 最新",
" surface",
" text 今日",
" surface",
" text 今天",
" act_search_strong",
" defined-by inquiry",
" defined-by action",
" role web_search_action",
" role web_search_strong_action",
" role web_search_imperative_lead",
" lexeme en",
" surface",
" text \" search \"",
" surface",
" text \" look up \"",
" surface",
" text \" lookup \"",
" surface",
" text \" research \"",
" surface",
" text \" investigate \"",
" surface",
" text \"search for …\"",
" surface",
" text \"search …\"",
" surface",
" text \"look up …\"",
" surface",
" text \"lookup …\"",
" surface",
" text \"research …\"",
" surface",
" text \"investigate …\"",
" lexeme ru",
" surface",
" text \" поищи \"",
" surface",
" text \" поиск \"",
" surface",
" text \" поискать \"",
" surface",
" text \" ищи \"",
" surface",
" text \"поищи …\"",
" surface",
" text \"поискать …\"",
" surface",
" text \"ищи …\"",
" lexeme hi",
" surface",
" text खोज",
" surface",
" text ढूंढ",
" surface",
" text ढूँढ",
" surface",
" text \"खोजो …\"",
" surface",
" text \"खोजें …\"",
" surface",
" text \"खोजिए …\"",
" surface",
" text \"ढूंढो …\"",
" surface",
" text \"ढूँढो …\"",
" surface",
" text \"ढूंढें …\"",
" surface",
" text \"ढूँढें …\"",
" lexeme zh",
" surface",
" text 搜索",
" surface",
" text 查找",
" surface",
" text 查询",
" surface",
" text 檢索",
" surface",
" text 检索",
" surface",
" text 搜一下",
" surface",
" text 查一下",
" surface",
" text 搜索…",
" surface",
" text 查找…",
" surface",
" text 查询…",
" surface",
" text 檢索…",
" surface",
" text 检索…",
" surface",
" text 搜一下…",
" surface",
" text 查一下…",
" act_find_weak",
" defined-by inquiry",
" defined-by action",
" role web_search_action",
" role web_search_imperative_lead",
" lexeme en",
" surface",
" text \" find \"",
" surface",
" text \"find …\"",
" lexeme ru",
" surface",
" text \" найди \"",
" surface",
" text \" найти \"",
" surface",
" text \" разыщи \"",
" surface",
" text \" узнай \"",
" surface",
" text \"найди …\"",
" surface",
" text \"найти …\"",
" surface",
" text \"разыщи …\"",
" surface",
" text \"узнай …\"",
" lexeme hi",
" surface",
" text \"पता लगाओ\"",
" surface",
" text \"पता लगाओ …\"",
" lexeme zh",
" surface",
" text 找到",
" surface",
" text 找到…",
" web_search_mention",
" defined-by web_search",
" role web_search_history_signal",
" lexeme en",
" surface",
" text duckduckgo",
" surface",
" text \"web search\"",
" surface",
" text \"search the internet\"",
" lexeme ru",
" surface",
" text веб-поиск",
" surface",
" text \"веб поиск\"",
" surface",
" text интернет",
" lexeme hi",
" surface",
" text \"वेब खोज\"",
" surface",
" text \"इंटरनेट पर खोज\"",
" surface",
" text इंटरनेट",
" lexeme zh",
" surface",
" text 网络搜索",
" surface",
" text 在网上搜索",
" surface",
" text 互联网",
" surface",
" text 網路",
"meanings",
" explicit_web_search_command",
" defined-by web_search",
" defined-by action",
" role web_search_explicit_prefix",
" lexeme en",
" surface",
" text \"search the web for …\"",
" surface",
" text \"search web for …\"",
" surface",
" text \"search the internet for …\"",
" surface",
" text \"search internet for …\"",
" surface",
" text \"search online for …\"",
" surface",
" text \"search wikipedia for …\"",
" surface",
" text \"search wikidata for …\"",
" surface",
" text \"search wiktionary for …\"",
" surface",
" text \"search for information about …\"",
" surface",
" text \"search for information on …\"",
" surface",
" text \"web search for …\"",
" surface",
" text \"find on the internet …\"",
" surface",
" text \"find online …\"",
" surface",
" text \"find information about …\"",
" surface",
" text \"find information on …\"",
" surface",
" text \"find detailed information about …\"",
" surface",
" text \"find detailed information on …\"",
" surface",
" text \"find info about …\"",
" surface",
" text \"find info on …\"",
" surface",
" text \"look up information about …\"",
" surface",
" text \"look up information on …\"",
" surface",
" text \"look up info about …\"",
" surface",
" text \"look up info on …\"",
" surface",
" text \"look up online …\"",
" lexeme ru",
" surface",
" text \"найди в интернете …\"",
" surface",
" text \"поищи в интернете …\"",
" surface",
" text \"поиск в интернете …\"",
" surface",
" text \"найди онлайн …\"",
" surface",
" text \"поищи онлайн …\"",
" surface",
" text \"найди в сети …\"",
" surface",
" text \"поищи в сети …\"",
" surface",
" text \"найди информацию в интернете о …\"",
" surface",
" text \"найди информацию в интернете об …\"",
" surface",
" text \"поищи информацию в интернете о …\"",
" surface",
" text \"поищи информацию в интернете об …\"",
" surface",
" text \"найди информацию о …\"",
" surface",
" text \"найди информацию об …\"",
" surface",
" text \"найди информацию про …\"",
" surface",
" text \"найди информацию по …\"",
" surface",
" text \"найти информацию о …\"",
" surface",
" text \"найти информацию об …\"",
" surface",
" text \"поищи информацию о …\"",
" surface",
" text \"поищи информацию об …\"",
" surface",
" text \"поищи информацию про …\"",
" surface",
" text \"поищи информацию по …\"",
" surface",
" text \"найди инфу о …\"",
" surface",
" text \"найди инфу об …\"",
" surface",
" text \"поищи инфу о …\"",
" surface",
" text \"поищи инфу об …\"",
" surface",
" text \"найди сведения о …\"",
" surface",
" text \"найди сведения об …\"",
" surface",
" text \"поищи сведения о …\"",
" surface",
" text \"поищи сведения об …\"",
" surface",
" text \"найди материалы о …\"",
" surface",
" text \"найди материалы об …\"",
" surface",
" text \"поищи материалы о …\"",
" surface",
" text \"поищи материалы об …\"",
" lexeme hi",
" surface",
" text \"इंटरनेट पर खोजें …\"",
" surface",
" text \"वेब पर खोजें …\"",
" surface",
" text \"ऑनलाइन खोजें …\"",
" surface",
" text \"विकिपीडिया पर खोजें …\"",
" lexeme zh",
" surface",
" text 在网上搜索…",
" surface",
" text 在互联网上搜索…",
" surface",
" text 在维基百科上搜索…",
" surface",
" text 上网查找…",
" topic_connective",
" defined-by relation",
" role web_search_topic_marker",
" lexeme en",
" surface",
" text \" about …\"",
" surface",
" text \" on …\"",
" surface",
" text \" regarding …\"",
" surface",
" text \" concerning …\"",
" surface",
" text \" for …\"",
" lexeme ru",
" surface",
" text \" о …\"",
" surface",
" text \" об …\"",
" surface",
" text \" про …\"",
" surface",
" text \" по …\"",
" surface",
" text \" насчет …\"",
" surface",
" text \" относительно …\"",
" lexeme hi",
" surface",
" text \"… के बारे में\"",
" surface",
" text \"… के विषय में\"",
" surface",
" text \"… से संबंधित\"",
" surface",
" text \"… पर\"",
" surface",
" text \"… की जानकारी\"",
" surface",
" text \"… की सूचना\"",
" lexeme zh",
" surface",
" text 关于…",
" surface",
" text 關於…",
" surface",
" text 有关…",
" surface",
" text 有關…",
" query_leading_noise",
" defined-by concept",
" role web_search_query_leading_noise",
" lexeme en",
" surface",
" text \"please …\"",
" surface",
" text \"can you …\"",
" surface",
" text \"could you …\"",
" surface",
" text \"would you …\"",
" surface",
" text \"me …\"",
" surface",
" text \"the …\"",
" surface",
" text \"some …\"",
" surface",
" text \"detailed …\"",
" surface",
" text \"more …\"",
" surface",
" text \"current …\"",
" surface",
" text \"latest …\"",
" surface",
" text \"information about …\"",
" surface",
" text \"information on …\"",
" surface",
" text \"info about …\"",
" surface",
" text \"info on …\"",
" surface",
" text \"details about …\"",
" surface",
" text \"details on …\"",
" surface",
" text \"data about …\"",
" surface",
" text \"data on …\"",
" lexeme ru",
" surface",
" text \"подробные …\"",
" surface",
" text \"информацию о …\"",
" surface",
" text \"информацию об …\"",
" surface",
" text \"инфу о …\"",
" surface",
" text \"инфу об …\"",
" surface",
" text \"сведения о …\"",
" surface",
" text \"сведения об …\"",
" surface",
" text \"материалы о …\"",
" surface",
" text \"материалы об …\"",
" surface",
" text \"материалы по …\"",
" surface",
" text \"данные о …\"",
" surface",
" text \"данные об …\"",
" surface",
" text \"о …\"",
" surface",
" text \"об …\"",
" surface",
" text \"про …\"",
" surface",
" text \"по …\"",
" lexeme hi",
" surface",
" text \"कृपया …\"",
" surface",
" text \"जानकारी …\"",
" surface",
" text \"सूचना …\"",
" surface",
" text \"विवरण …\"",
" surface",
" text \"सामग्री …\"",
" lexeme zh",
" surface",
" text 关于…",
" surface",
" text 關於…",
" surface",
" text 有关…",
" surface",
" text 有關…",
" query_trailing_noise",
" defined-by concept",
" role web_search_query_trailing_noise",
" lexeme en",
" surface",
" text \"… online\"",
" surface",
" text \"… on the internet\"",
" surface",
" text \"… on the web\"",
" surface",
" text \"… on wikipedia\"",
" surface",
" text \"… in wikipedia\"",
" surface",
" text \"… from wikipedia\"",
" surface",
" text \"… information\"",
" surface",
" text \"… info\"",
" surface",
" text \"… details\"",
" surface",
" text \"… data\"",
" surface",
" text \"… material\"",
" surface",
" text \"… materials\"",
" surface",
" text \"… resources\"",
" surface",
" text \"… sources\"",
" surface",
" text \"… articles\"",
" surface",
" text \"… facts\"",
" lexeme ru",
" surface",
" text \"… в интернете\"",
" surface",
" text \"… онлайн\"",
" surface",
" text \"… в сети\"",
" surface",
" text \"… в википедии\"",
" surface",
" text \"… википедии\"",
" surface",
" text \"… информация\"",
" surface",
" text \"… сведения\"",
" surface",
" text \"… материалы\"",
" surface",
" text \"… данные\"",
" lexeme hi",
" surface",
" text \"… के बारे में\"",
" surface",
" text \"… के विषय में\"",
" surface",
" text \"… से संबंधित\"",
" surface",
" text \"… पर\"",
" surface",
" text \"… की जानकारी\"",
" surface",
" text \"… की सूचना\"",
" surface",
" text \"… जानकारी\"",
" surface",
" text \"… सूचना\"",
" surface",
" text \"… विवरण\"",
" surface",
" text \"… सामग्री\"",
" surface",
" text \"… स्रोत\"",
" surface",
" text \"… विकिपीडिया में\"",
" surface",
" text \"… ऑनलाइन\"",
" surface",
" text \"… इंटरनेट पर\"",
" surface",
" text \"… खोजो\"",
" surface",
" text \"… खोजें\"",
" surface",
" text \"… खोजिए\"",
" surface",
" text \"… ढूंढो\"",
" surface",
" text \"… ढूँढो\"",
" surface",
" text \"… ढूंढें\"",
" surface",
" text \"… ढूँढें\"",
" lexeme zh",
" surface",
" text …的信息",
" surface",
" text …的資料",
" surface",
" text …的资料",
" surface",
" text …信息",
" surface",
" text …資料",
" surface",
" text …资料",
" surface",
" text …内容",
" surface",
" text …文章",
" surface",
" text …在维基百科上",
" surface",
" text …在維基百科上",
" surface",
" text …维基百科",
" surface",
" text …維基百科",
" surface",
" text …网上",
" surface",
" text …網上",
" surface",
" text …在线",
" surface",
" text …在線",
" surface",
" text …搜索",
" surface",
" text …查找",
" surface",
" text …查一下",
" surface",
" text …搜一下",
"meanings",
" research_question_opener",
" defined-by inquiry",
" role research_question_opener",
" lexeme en",
" surface",
" text \"what is the …\"",
" surface",
" text \"what is a …\"",
" surface",
" text \"what is an …\"",
" surface",
" text \"what is …\"",
" surface",
" text \"what are the …\"",
" surface",
" text \"what are …\"",
" surface",
" text \"what s the …\"",
" surface",
" text \"what s a …\"",
" surface",
" text \"what s an …\"",
" surface",
" text \"what s …\"",
" surface",
" text \"which is the …\"",
" surface",
" text \"which is a …\"",
" surface",
" text \"which is an …\"",
" surface",
" text \"which are the …\"",
" surface",
" text \"which are …\"",
" surface",
" text \"which …\"",
" surface",
" text \"who is the …\"",
" surface",
" text \"who are the …\"",
" surface",
" text \"who …\"",
" surface",
" text \"where is the …\"",
" surface",
" text \"where are the …\"",
" surface",
" text \"where …\"",
" surface",
" text \"when is the …\"",
" surface",
" text \"when are the …\"",
" surface",
" text \"when …\"",
" surface",
" text \"why is the …\"",
" surface",
" text \"why are the …\"",
" surface",
" text \"why …\"",
" surface",
" text \"how is the …\"",
" surface",
" text \"how are the …\"",
" surface",
" text \"how …\"",
" surface",
" text \"can you tell me …\"",
" surface",
" text \"could you tell me …\"",
" surface",
" text \"do you know …\"",
" lexeme ru",
" surface",
" text \"что такое …\"",
" surface",
" text \"что за …\"",
" surface",
" text \"какие …\"",
" surface",
" text \"какой …\"",
" surface",
" text \"какая …\"",
" surface",
" text \"какое …\"",
" surface",
" text \"кто такой …\"",
" surface",
" text \"кто такая …\"",
" surface",
" text \"кто …\"",
" surface",
" text \"где …\"",
" surface",
" text \"когда …\"",
" surface",
" text \"почему …\"",
" surface",
" text \"зачем …\"",
" surface",
" text \"как …\"",
" surface",
" text \"расскажи мне …\"",
" surface",
" text \"ты знаешь …\"",
" lexeme hi",
" surface",
" text \"क्या होता है …\"",
" surface",
" text \"क्या है …\"",
" surface",
" text \"कौन है …\"",
" surface",
" text \"कौन …\"",
" surface",
" text \"कहाँ है …\"",
" surface",
" text \"कहाँ …\"",
" surface",
" text \"कब …\"",
" surface",
" text \"क्यों …\"",
" surface",
" text \"कैसे …\"",
" surface",
" text \"बताओ …\"",
" lexeme zh",
" surface",
" text 什么是…",
" surface",
" text 谁是…",
" surface",
" text 哪里是…",
" surface",
" text 哪里…",
" surface",
" text 哪个…",
" surface",
" text 什么时候…",
" surface",
" text 为什么…",
" surface",
" text 怎么…",
" surface",
" text 如何…",
" surface",
" text 告诉我…",
" surface",
" text 你知道…",
" research_superlative_modifier",
" defined-by property",
" role research_superlative_modifier",
" lexeme en",
" surface",
" text \" most \"",
" surface",
" text \" best \"",
" surface",
" text \" top \"",
" surface",
" text \" leading \"",
" surface",
" text \" standard \"",
" surface",
" text \" de facto \"",
" surface",
" text \" widely used \"",
" surface",
" text \" commonly used \"",
" surface",
" text \" popular \"",
" surface",
" text \" recommended \"",
" surface",
" text \" current \"",
" surface",
" text \" latest \"",
" surface",
" text \" recent \"",
" surface",
" text \" state of the art \"",
" surface",
" text \" sota \"",
" surface",
" text \" should i use \"",
" surface",
" text \" should we use \"",
" surface",
" text \" should be used \"",
" lexeme ru",
" surface",
" text \" лучший \"",
" surface",
" text \" лучшая \"",
" surface",
" text \" лучшие \"",
" surface",
" text \" самый \"",
" surface",
" text \" самые \"",
" surface",
" text \" топ \"",
" surface",
" text \" ведущий \"",
" surface",
" text \" популярный \"",
" surface",
" text \" стандартный \"",
" surface",
" text \" рекомендуемый \"",
" surface",
" text \" текущий \"",
" surface",
" text \" последний \"",
" surface",
" text \" недавний \"",
" lexeme hi",
" surface",
" text \" सबसे अच्छा \"",
" surface",
" text \" सर्वश्रेष्ठ \"",
" surface",
" text \" शीर्ष \"",
" surface",
" text \" प्रमुख \"",
" surface",
" text \" मानक \"",
" surface",
" text \" लोकप्रिय \"",
" surface",
" text \" अनुशंसित \"",
" surface",
" text \" वर्तमान \"",
" surface",
" text \" नवीनतम \"",
" surface",
" text \" हाल ही का \"",
" lexeme zh",
" surface",
" text 最好",
" surface",
" text 最佳",
" surface",
" text 顶级",
" surface",
" text 领先",
" surface",
" text 标准",
" surface",
" text 流行",
" surface",
" text 推荐",
" surface",
" text 当前",
" surface",
" text 最新",
" surface",
" text 最近",
" research_evidence_domain",
" defined-by entity",
" role research_evidence_domain",
" lexeme en",
" surface",
" text \" dataset \"",
" surface",
" text \" datasets \"",
" surface",
" text \" benchmark \"",
" surface",
" text \" benchmarks \"",
" surface",
" text \" corpus \"",
" surface",
" text \" corpora \"",
" surface",
" text \" metric \"",
" surface",
" text \" metrics \"",
" surface",
" text \" framework \"",
" surface",
" text \" frameworks \"",
" surface",
" text \" paper \"",
" surface",
" text \" papers \"",
" surface",
" text \" study \"",
" surface",
" text \" studies \"",
" lexeme ru",
" surface",
" text \" датасет \"",
" surface",
" text \" набор данных \"",
" surface",
" text \" бенчмарк \"",
" surface",
" text \" корпус \"",
" surface",
" text \" метрика \"",
" surface",
" text \" фреймворк \"",
" surface",
" text \" статья \"",
" surface",
" text \" исследование \"",
" lexeme hi",
" surface",
" text \" डेटासेट \"",
" surface",
" text \" बेंचमार्क \"",
" surface",
" text \" कॉर्पस \"",
" surface",
" text \" मीट्रिक \"",
" surface",
" text \" फ्रेमवर्क \"",
" surface",
" text \" पेपर \"",
" surface",
" text \" अध्ययन \"",
" lexeme zh",
" surface",
" text 数据集",
" surface",
" text 基准",
" surface",
" text 语料库",
" surface",
" text 指标",
" surface",
" text 框架",
" surface",
" text 论文",
" surface",
" text 研究",
" research_evaluation_domain",
" defined-by concept",
" role research_evaluation_domain",
" lexeme en",
" surface",
" text \" evaluation \"",
" surface",
" text \" evaluate \"",
" surface",
" text \" validation \"",
" surface",
" text \" validate \"",
" surface",
" text \" quality \"",
" surface",
" text \" translation \"",
" surface",
" text \" compare \"",
" surface",
" text \" comparison \"",
" lexeme ru",
" surface",
" text \" оценка \"",
" surface",
" text \" оценить \"",
" surface",
" text \" валидация \"",
" surface",
" text \" проверка \"",
" surface",
" text \" качество \"",
" surface",
" text \" перевод \"",
" surface",
" text \" сравнить \"",
" surface",
" text \" сравнение \"",
" lexeme hi",
" surface",
" text \" मूल्यांकन \"",
" surface",
" text \" सत्यापन \"",
" surface",
" text \" गुणवत्ता \"",
" surface",
" text \" अनुवाद \"",
" surface",
" text \" तुलना \"",
" lexeme zh",
" surface",
" text 评估",
" surface",
" text 评价",
" surface",
" text 验证",
" surface",
" text 质量",
" surface",
" text 翻译",
" surface",
" text 比较",
" enumeration_request_opener",
" defined-by inquiry",
" defined-by action",
" role enumeration_request_opener",
" lexeme en",
" surface",
" text \"list all …\"",
" surface",
" text \"list every …\"",
" surface",
" text \"list the …\"",
" surface",
" text \"show all …\"",
" surface",
" text \"show me all …\"",
" surface",
" text \"show me the …\"",
" surface",
" text \"give me all …\"",
" surface",
" text \"name all …\"",
" surface",
" text \"enumerate all …\"",
" lexeme ru",
" surface",
" text \"перечисли всех …\"",
" surface",
" text \"перечисли все …\"",
" surface",
" text \"список всех …\"",
" surface",
" text \"назови всех …\"",
" lexeme hi",
" surface",
" text \"सभी …\"",
" surface",
" text \"हर …\"",
" lexeme zh",
" surface",
" text \"列出所有 …\"",
" surface",
" text \"列出全部 …\"",
" surface",
" text \"显示所有 …\"",
" surface",
" text \"枚举所有 …\"",
" enumeration_constraint",
" defined-by relation",
" role enumeration_constraint",
" lexeme en",
" surface",
" text \" with \"",
" surface",
" text \" that \"",
" surface",
" text \" who \"",
" surface",
" text \" whose \"",
" surface",
" text \" where \"",
" surface",
" text \" which \"",
" surface",
" text \" having \"",
" surface",
" text \" have \"",
" surface",
" text \" has \"",
" surface",
" text \" featuring \"",
" surface",
" text \" capable of \"",
" surface",
" text \" can \"",
" surface",
" text \" for \"",
" surface",
" text \" by \"",
" surface",
" text \" in \"",
" lexeme ru",
" surface",
" text \" с \"",
" surface",
" text \" у которых \"",
" surface",
" text \" которые \"",
" surface",
" text \" имеющие \"",
" surface",
" text \" имеющих \"",
" surface",
" text \" для \"",
" surface",
" text \" в \"",
" lexeme hi",
" surface",
" text \" जिनके \"",
" surface",
" text \" जिनमें \"",
" surface",
" text \" जिसमें \"",
" surface",
" text \" वाले \"",
" surface",
" text \" के साथ \"",
" surface",
" text \" के लिए \"",
" surface",
" text \" में \"",
" lexeme zh",
" surface",
" text \" 具有 \"",
" surface",
" text \" 有 \"",
" surface",
" text \" 带有 \"",
" surface",
" text \" 可以 \"",
" surface",
" text \" 能 \"",
" surface",
" text \" 在 \"",
" surface",
" text \" 用于 \"",
"meanings",
" followup_instruction_verb",
" defined-by action",
" role followup_instruction_verb",
" lexeme en",
" surface",
" text compare",
" surface",
" text summarize",
" surface",
" text summarise",
" surface",
" text explain",
" surface",
" text describe",
" surface",
" text show",
" surface",
" text tell",
" lexeme ru",
" surface",
" text сравни",
" surface",
" text обобщи",
" surface",
" text резюмируй",
" surface",
" text объясни",
" surface",
" text опиши",
" surface",
" text покажи",
" surface",
" text расскажи",
" lexeme hi",
" surface",
" text \"तुलना करो\"",
" surface",
" text \"सारांश दो\"",
" surface",
" text समझाओ",
" surface",
" text \"वर्णन करो\"",
" surface",
" text दिखाओ",
" surface",
" text बताओ",
" lexeme zh",
" surface",
" text 比较",
" surface",
" text 总结",
" surface",
" text 概括",
" surface",
" text 解释",
" surface",
" text 描述",
" surface",
" text 显示",
" surface",
" text 告诉",
" clause_continuation_marker",
" defined-by relation",
" role clause_continuation_marker",
" lexeme en",
" surface",
" text and",
" surface",
" text then",
" lexeme ru",
" surface",
" text и",
" surface",
" text затем",
" surface",
" text потом",
" lexeme hi",
" surface",
" text और",
" surface",
" text फिर",
" surface",
" text तब",
" lexeme zh",
" surface",
" text 并",
" surface",
" text 然后",
" surface",
" text 接着",
"meanings",
" human_language",
" grounded-in Q33742",
" defined-by concept",
" role translation_language_genus",
" lexeme en",
" surface",
" text language",
" lexeme ru",
" surface",
" text язык",
" lexeme hi",
" surface",
" text भाषा",
" lexeme zh",
" surface",
" text 语言",
" language_english",
" grounded-in Q1860",
" defined-by human_language",
" role translation_language",
" lexeme en",
" surface",
" text english",
" lexeme ru",
" surface",
" text английский",
" lexeme hi",
" surface",
" text अंग्रेज़ी",
" surface",
" text अंग्रेजी",
" lexeme zh",
" surface",
" text 英语",
" surface",
" text 英文",
" language_russian",
" grounded-in Q7737",
" defined-by human_language",
" role translation_language",
" lexeme en",
" surface",
" text russian",
" lexeme ru",
" surface",
" text русский",
" lexeme hi",
" surface",
" text रूसी",
" lexeme zh",
" surface",
" text 俄语",
" language_hindi",
" grounded-in Q1568",
" defined-by human_language",
" role translation_language",
" lexeme en",
" surface",
" text hindi",
" lexeme ru",
" surface",
" text хинди",
" lexeme hi",
" surface",
" text हिंदी",
" surface",
" text हिन्दी",
" lexeme zh",
" surface",
" text 印地语",
" language_chinese",
" grounded-in Q7850",
" defined-by human_language",
" role translation_language",
" lexeme en",
" surface",
" text chinese",
" lexeme ru",
" surface",
" text китайский",
" lexeme hi",
" surface",
" text चीनी",
" lexeme zh",
" surface",
" text 中文",
" surface",
" text 汉语",
" surface",
" text 漢語",
" translate",
" grounded-in Q7553",
" defined-by action",
" role translation_action",
" lexeme en",
" surface",
" text translate",
" lexeme ru",
" surface",
" text перевести",
" surface",
" text переведи",
" surface",
" text опиши",
" lexeme hi",
" surface",
" text अनुवाद",
" lexeme zh",
" surface",
" text 翻译",
" surface",
" text 翻譯",
" translation_direction_source",
" defined-by relation",
" role translation_source_direction",
" lexeme en",
" surface",
" text from",
" lexeme ru",
" surface",
" text с",
" lexeme hi",
" surface",
" text से",
" lexeme zh",
" surface",
" text 从",
" translation_direction_target",
" defined-by relation",
" role translation_target_direction",
" lexeme en",
" surface",
" text to",
" lexeme ru",
" surface",
" text на",
" lexeme hi",
" surface",
" text में",
" lexeme zh",
" surface",
" text 成",
" surface",
" text 为",
" surface",
" text 為",
" surface",
" text 到",
" translate_from_english",
" defined-by language_english",
" defined-by translation_direction_source",
" role translation_source_marker",
" lexeme en",
" surface",
" text \"from english\"",
" lexeme ru",
" surface",
" text \"с английского\"",
" lexeme hi",
" surface",
" text \"अंग्रेजी से\"",
" surface",
" text \"अंग्रेज़ी से\"",
" lexeme zh",
" surface",
" text 从英语",
" surface",
" text 从英文",
" translate_from_russian",
" defined-by language_russian",
" defined-by translation_direction_source",
" role translation_source_marker",
" lexeme en",
" surface",
" text \"from russian\"",
" lexeme ru",
" surface",
" text \"с русского\"",
" lexeme hi",
" surface",
" text \"रूसी से\"",
" lexeme zh",
" surface",
" text 从俄语",
" translate_from_hindi",
" defined-by language_hindi",
" defined-by translation_direction_source",
" role translation_source_marker",
" lexeme en",
" surface",
" text \"from hindi\"",
" lexeme ru",
" surface",
" text \"с хинди\"",
" lexeme hi",
" surface",
" text \"हिंदी से\"",
" surface",
" text \"हिन्दी से\"",
" lexeme zh",
" surface",
" text 从印地语",
" surface",
" text 从印地文",
" translate_from_chinese",
" defined-by language_chinese",
" defined-by translation_direction_source",
" role translation_source_marker",
" lexeme en",
" surface",
" text \"from chinese\"",
" lexeme ru",
" surface",
" text \"с китайского\"",
" lexeme hi",
" surface",
" text \"चीनी से\"",
" lexeme zh",
" surface",
" text 从中文",
" surface",
" text 从汉语",
" surface",
" text 从漢語",
" translate_into_english",
" defined-by language_english",
" defined-by translation_direction_target",
" role translation_target_marker",
" lexeme en",
" surface",
" text \"to english\"",
" lexeme ru",
" surface",
" text \"на английский\"",
" surface",
" text \"на английском\"",
" lexeme hi",
" surface",
" text \"अंग्रेजी में\"",
" surface",
" text \"अंग्रेज़ी में\"",
" lexeme zh",
" surface",
" text 成英文",
" surface",
" text 成英语",
" surface",
" text 为英文",
" surface",
" text 为英语",
" surface",
" text 為英文",
" surface",
" text 為英语",
" surface",
" text 到英文",
" surface",
" text 到英语",
" translate_into_russian",
" defined-by language_russian",
" defined-by translation_direction_target",
" role translation_target_marker",
" lexeme en",
" surface",
" text \"to russian\"",
" lexeme ru",
" surface",
" text \"на русский\"",
" lexeme hi",
" surface",
" text \"रूसी में\"",
" lexeme zh",
" surface",
" text 成俄语",
" surface",
" text 成俄語",
" surface",
" text 为俄语",
" surface",
" text 为俄語",
" surface",
" text 為俄语",
" surface",
" text 為俄語",
" surface",
" text 到俄语",
" surface",
" text 到俄語",
" translate_into_hindi",
" defined-by language_hindi",
" defined-by translation_direction_target",
" role translation_target_marker",
" lexeme en",
" surface",
" text \"to hindi\"",
" lexeme ru",
" surface",
" text \"на хинди\"",
" lexeme hi",
" surface",
" text \"हिंदी में\"",
" surface",
" text \"हिन्दी में\"",
" lexeme zh",
" surface",
" text 成印地语",
" surface",
" text 成印地文",
" surface",
" text 为印地语",
" surface",
" text 为印地文",
" surface",
" text 為印地语",
" surface",
" text 為印地文",
" surface",
" text 到印地语",
" surface",
" text 到印地文",
" translate_into_chinese",
" defined-by language_chinese",
" defined-by translation_direction_target",
" role translation_target_marker",
" lexeme en",
" surface",
" text \"to chinese\"",
" lexeme ru",
" surface",
" text \"на китайский\"",
" lexeme hi",
" surface",
" text \"चीनी में\"",
" lexeme zh",
" surface",
" text 成中文",
" surface",
" text 成汉语",
" surface",
" text 成漢語",
" surface",
" text 为中文",
" surface",
" text 为汉语",
" surface",
" text 为漢語",
" surface",
" text 為中文",
" surface",
" text 為汉语",
" surface",
" text 為漢語",
" surface",
" text 到中文",
" surface",
" text 到汉语",
" surface",
" text 到漢語",
" translation_unquoted_frame",
" defined-by translate",
" defined-by translation_direction_target",
" role translation_unquoted_frame",
" lexeme en",
" surface",
" text \"translate … to \"",
" lexeme ru",
" surface",
" text \"переведи … на \"",
" lexeme hi",
" surface",
" text अनुवाद",
" lexeme zh",
" surface",
" text 翻译",
" surface",
" text 翻譯",
" translation_into_marker",
" defined-by translate",
" defined-by translation_direction_target",
" role translation_into_marker",
" lexeme en",
" surface",
" text \"translate into\"",
" lexeme ru",
" surface",
" text \"перевести на\"",
" lexeme hi",
" surface",
" text \" में अनुवाद\"",
" surface",
" text \" मे अनुवाद\"",
" lexeme zh",
" surface",
" text 翻译成",
" surface",
" text 翻译为",
" surface",
" text 翻译到",
" surface",
" text 翻譯成",
" surface",
" text 翻譯為",
" surface",
" text 翻譯到",
" translation_object_marker",
" defined-by relation",
" role translation_object_marker",
" lexeme en",
" surface",
" text of",
" lexeme ru",
" surface",
" text слова",
" lexeme hi",
" surface",
" text \" का \"",
" surface",
" text \" को \"",
" lexeme zh",
" surface",
" text 把",
" surface",
" text 将",
" definition_command",
" defined-by action",
" role definition_command",
" lexeme en",
" surface",
" text define",
" lexeme ru",
" surface",
" text определи",
" lexeme hi",
" surface",
" text \"परिभाषित करें\"",
" lexeme zh",
" surface",
" text 定义",
" links_notation_format",
" defined-by concept",
" role links_notation_format",
" lexeme en",
" surface",
" text \"links notation\"",
" lexeme ru",
" surface",
" text \"в links\"",
" lexeme hi",
" surface",
" text \"लिंक्स नोटेशन\"",
" lexeme zh",
" surface",
" text 链接表示法",
" apple",
" grounded-in Q89",
" defined-by entity",
" role compositional_lemma",
" source-lexeme L3257 # wikidata english source lexeme",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" form L3257-F1 # wikidata form apple",
" feature Q110786 # wikidata grammatical feature",
" form L3257-F2 # wikidata form apples",
" feature Q146786 # wikidata grammatical feature",
" sense L3257-S1 # wikidata grounded sense",
" surface L3257-F1 # wikidata english surface",
" text apple",
" language en",
" sense L3257-S1 # wikidata grounded sense",
" lexeme ru",
" surface",
" text яблоко",
" surface",
" text яблока",
" surface",
" text яблоку",
" surface",
" text яблоком",
" surface",
" text яблоке",
" surface",
" text яблоки",
" surface",
" text яблок",
" surface",
" text яблокам",
" surface",
" text яблоками",
" surface",
" text яблоках",
" lexeme hi",
" surface",
" text सेब",
" lexeme zh",
" surface",
" text 苹果",
" greeting_hello",
" grounded-in Q98815142",
" defined-by entity",
" role compositional_lemma",
" lexeme en",
" surface",
" text hello",
" lexeme ru",
" surface",
" text привет",
" surface",
" text здравствуйте",
" lexeme hi",
" surface",
" text नमस्ते",
" lexeme zh",
" surface",
" text 你好",
" gratitude_thank_you",
" grounded-in Q2728730",
" defined-by entity",
" role compositional_lemma",
" lexeme en",
" surface",
" text \"thank you\"",
" surface",
" text thanks",
" lexeme ru",
" surface",
" text спасибо",
" lexeme hi",
" surface",
" text धन्यवाद",
" lexeme zh",
" surface",
" text 谢谢",
" surface",
" text 謝謝",
" affirmation_yes",
" grounded-in Q6452715",
" defined-by entity",
" role compositional_lemma",
" lexeme en",
" surface",
" text yes",
" lexeme ru",
" surface",
" text да",
" lexeme hi",
" surface",
" text हाँ",
" lexeme zh",
" surface",
" text 是",
" negation_no",
" defined-by entity",
" role compositional_lemma",
" lexeme en",
" surface",
" text no",
" lexeme ru",
" surface",
" text нет",
" lexeme hi",
" surface",
" text नहीं",
" lexeme zh",
" surface",
" text 不",
" tomato",
" grounded-in Q23501",
" defined-by entity",
" role compositional_lemma",
" source-lexeme L7993 # wikidata english source lexeme",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" form L7993-F1 # wikidata form tomato",
" feature Q110786 # wikidata grammatical feature",
" form L7993-F2 # wikidata form tomatoes",
" feature Q146786 # wikidata grammatical feature",
" sense L7993-S1 # wikidata grounded sense",
" surface L7993-F1 # wikidata english surface",
" text tomato",
" language en",
" sense L7993-S1 # wikidata grounded sense",
" lexeme ru",
" surface",
" text помидор",
" surface",
" text томат",
" lexeme hi",
" surface",
" text टमाटर",
" lexeme zh",
" surface",
" text 番茄",
" surface",
" text 西红柿",
" cucumber",
" grounded-in Q2735883",
" defined-by entity",
" role compositional_lemma",
" lexeme en",
" surface",
" text cucumber",
" lexeme ru",
" surface",
" text огурец",
" lexeme hi",
" surface",
" text खीरा",
" lexeme zh",
" surface",
" text 黄瓜",
" potato",
" grounded-in Q10998",
" defined-by entity",
" role compositional_lemma",
" source-lexeme L3784 # wikidata english source lexeme",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" form L3784-F1 # wikidata form potato",
" feature Q110786 # wikidata grammatical feature",
" form L3784-F2 # wikidata form potatoes",
" feature Q146786 # wikidata grammatical feature",
" sense L3784-S1 # wikidata grounded sense",
" surface L3784-F1 # wikidata english surface",
" text potato",
" language en",
" sense L3784-S1 # wikidata grounded sense",
" lexeme ru",
" surface",
" text картофель",
" surface",
" text картошка",
" lexeme hi",
" surface",
" text आलू",
" lexeme zh",
" surface",
" text 土豆",
" surface",
" text 马铃薯",
" carrot",
" grounded-in Q81",
" defined-by entity",
" role compositional_lemma",
" lexeme en",
" surface",
" text carrot",
" lexeme ru",
" surface",
" text морковь",
" lexeme hi",
" surface",
" text गाजर",
" lexeme zh",
" surface",
" text 胡萝卜",
" bread",
" grounded-in Q7802",
" defined-by entity",
" role compositional_lemma",
" source-lexeme L3865 # wikidata english source lexeme",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" form L3865-F1 # wikidata form bread",
" feature Q110786 # wikidata grammatical feature",
" form L3865-F2 # wikidata form breads",
" feature Q146786 # wikidata grammatical feature",
" sense L3865-S1 # wikidata grounded sense",
" surface L3865-F1 # wikidata english surface",
" text bread",
" language en",
" sense L3865-S1 # wikidata grounded sense",
" lexeme ru",
" surface",
" text хлеб",
" lexeme hi",
" surface",
" text रोटी",
" lexeme zh",
" surface",
" text 面包",
" water",
" grounded-in Q283",
" defined-by entity",
" role compositional_lemma",
" source-lexeme L3302 # wikidata english source lexeme",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" form L3302-F1 # wikidata form water",
" feature Q110786 # wikidata grammatical feature",
" form L3302-F2 # wikidata form waters",
" feature Q146786 # wikidata grammatical feature",
" sense L3302-S1 # wikidata grounded sense",
" surface L3302-F1 # wikidata english surface",
" text water",
" language en",
" sense L3302-S1 # wikidata grounded sense",
" lexeme ru",
" surface",
" text вода",
" lexeme hi",
" surface",
" text पानी",
" lexeme zh",
" surface",
" text 水",
" good",
" defined-by property",
" role compositional_lemma",
" lexeme en",
" surface",
" text good",
" lexeme ru",
" surface",
" text доброе",
" surface",
" text добрый",
" surface",
" text добрая",
" surface",
" text добрые",
" surface",
" text доброго",
" surface",
" text добрую",
" surface",
" text добрым",
" surface",
" text хорошее",
" surface",
" text хороший",
" surface",
" text хорошая",
" surface",
" text хорошие",
" surface",
" text хорошего",
" surface",
" text хорошую",
" surface",
" text хорошим",
" lexeme hi",
" surface",
" text अच्छा",
" lexeme zh",
" surface",
" text 好",
" find",
" defined-by action",
" role compositional_lemma",
" lexeme en",
" surface",
" text find",
" lexeme ru",
" surface",
" text найди",
" surface",
" text найдите",
" surface",
" text найти",
" lexeme hi",
" surface",
" text खोजें",
" lexeme zh",
" surface",
" text 查找",
" synonym",
" grounded-in Q42106",
" defined-by concept",
" role compositional_lemma",
" role compositional_genitive_head",
" lexeme en",
" surface",
" text synonyms",
" lexeme ru",
" surface",
" text синоним",
" surface",
" text синонимы",
" surface",
" text синонимов",
" lexeme hi",
" surface",
" text पर्यायवाची",
" lexeme zh",
" surface",
" text 同义词",
" example",
" grounded-in Q14944328",
" defined-by concept",
" role compositional_lemma",
" role compositional_genitive_head",
" lexeme en",
" surface",
" text examples",
" lexeme ru",
" surface",
" text пример",
" surface",
" text примеры",
" surface",
" text примеров",
" lexeme hi",
" surface",
" text उदाहरण",
" lexeme zh",
" surface",
" text 例子",
" agreement",
" defined-by concept",
" role compositional_lemma",
" lexeme en",
" surface",
" text agreement",
" lexeme ru",
" surface",
" text согласование",
" surface",
" text согласования",
" action genitive",
" surface",
" text согласованию",
" surface",
" text согласованием",
" surface",
" text согласовании",
" lexeme hi",
" surface",
" text सहमति",
" lexeme zh",
" surface",
" text 一致",
" conjunction_or",
" grounded-in Q1651704",
" defined-by relation",
" role compositional_lemma",
" lexeme en",
" surface",
" text or",
" lexeme ru",
" surface",
" text или",
" lexeme hi",
" surface",
" text या",
" lexeme zh",
" surface",
" text 或",
" who_are_you",
" defined-by concept",
" role compositional_phrase",
" lexeme en",
" surface",
" text \"Who are you?\"",
" lexeme ru",
" surface",
" text \"кто ты\"",
" surface",
" text \"кто ты такой\"",
" surface",
" text \"кто ты такая\"",
" surface",
" text \"кто вы\"",
" surface",
" text \"кто вы такой\"",
" surface",
" text \"кто вы такая\"",
" lexeme hi",
" surface",
" text \"आप कौन हैं\"",
" lexeme zh",
" surface",
" text 你是谁",
" what_is_this",
" defined-by concept",
" role compositional_phrase",
" lexeme en",
" surface",
" text \"What is this?\"",
" lexeme ru",
" surface",
" text \"что это\"",
" surface",
" text \"что это такое\"",
" lexeme hi",
" surface",
" text \"यह क्या है\"",
" lexeme zh",
" surface",
" text 这是什么",
" how_are_you",
" defined-by concept",
" role compositional_phrase",
" lexeme en",
" surface",
" text \"how are you\"",
" lexeme ru",
" surface",
" text \"как дела\"",
" lexeme hi",
" surface",
" text \"आप कैसे हैं\"",
" lexeme zh",
" surface",
" text 你好吗",
"meanings",
" link",
" defined-by link",
" defined-by link-action",
" defined-by any-of-reference",
" role ontology_root",
" notation links_notation_format",
" annotation semantic_gloss",
" denotation relation",
" connotation concept",
" lexeme en",
" surface",
" text link",
" surface",
" text connection",
" lexeme ru",
" surface",
" text связь",
" surface",
" text ссылка",
" lexeme hi",
" surface",
" text लिंक",
" surface",
" text कड़ी",
" lexeme zh",
" surface",
" text 链接",
" surface",
" text 连接",
" type",
" defined-by link",
" defined-by type",
" role ontology_type",
" self-equation type",
" lexeme en",
" surface",
" text type",
" surface",
" text kind",
" lexeme ru",
" surface",
" text тип",
" surface",
" text вид",
" lexeme hi",
" surface",
" text प्रकार",
" surface",
" text क़िस्म",
" lexeme zh",
" surface",
" text 类型",
" surface",
" text 种类",
" entity",
" defined-by type",
" role ontology_category",
" lexeme en",
" surface",
" text entity",
" surface",
" text thing",
" lexeme ru",
" surface",
" text сущность",
" surface",
" text объект",
" lexeme hi",
" surface",
" text इकाई",
" surface",
" text वस्तु",
" lexeme zh",
" surface",
" text 实体",
" surface",
" text 事物",
" concept",
" defined-by type",
" role ontology_category",
" lexeme en",
" surface",
" text concept",
" surface",
" text notion",
" lexeme ru",
" surface",
" text понятие",
" surface",
" text концепция",
" lexeme hi",
" surface",
" text अवधारणा",
" surface",
" text संकल्पना",
" lexeme zh",
" surface",
" text 概念",
" surface",
" text 观念",
" relation",
" defined-by link",
" source-lexeme L3743 # wikidata english noun relation",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" form L3743-F1 # wikidata singular form relation",
" feature Q110786 # wikidata singular",
" form L3743-F2 # wikidata plural form relations",
" feature Q146786 # wikidata plural",
" sense L3743-S1 # wikidata relation sense",
" source-lexeme L3744 # wikidata english noun relationship",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" form L3744-F1 # wikidata singular form relationship",
" feature Q110786 # wikidata singular",
" form L3744-F2 # wikidata plural form relationships",
" feature Q146786 # wikidata plural",
" sense L3744-S1 # wikidata relationship sense",
" related-lexeme L5848 # wikidata english noun object",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" sense L5848-S1 # wikidata thing sense",
" role ontology_category",
" surface L3743-F1 # wikidata english singular surface",
" text relation",
" language en",
" sense L3743-S1 # wikidata relation sense",
" surface L3744-F1 # wikidata english singular surface",
" text relationship",
" language en",
" sense L3744-S1 # wikidata relationship sense",
" lexeme en",
" surface",
" text relation",
" surface",
" text relationship",
" lexeme ru",
" surface",
" text отношение",
" surface",
" text соотношение",
" lexeme hi",
" surface",
" text संबंध",
" surface",
" text रिश्ता",
" lexeme zh",
" surface",
" text 关系",
" surface",
" text 联系",
" action",
" defined-by concept",
" role ontology_category",
" lexeme en",
" surface",
" text action",
" surface",
" text act",
" lexeme ru",
" surface",
" text действие",
" surface",
" text операция",
" lexeme hi",
" surface",
" text क्रिया",
" surface",
" text कार्य",
" lexeme zh",
" surface",
" text 动作",
" surface",
" text 操作",
" property",
" defined-by concept",
" role ontology_category",
" lexeme en",
" surface",
" text property",
" surface",
" text attribute",
" lexeme ru",
" surface",
" text свойство",
" surface",
" text атрибут",
" lexeme hi",
" surface",
" text गुण",
" surface",
" text विशेषता",
" lexeme zh",
" surface",
" text 属性",
" surface",
" text 特性",
"meanings",
" semantic_facet",
" defined-by relation",
" role ontology_category",
" notation links_notation_format",
" annotation semantic_gloss",
" denotation relation",
" connotation concept",
" lexeme en",
" surface",
" text \"semantic facet\"",
" notation word_surface",
" denotation semantic_facet",
" part_of_speech noun_phrase",
" lexeme ru",
" surface",
" text \"семантический аспект\"",
" notation word_surface",
" denotation semantic_facet",
" part_of_speech noun_phrase",
" lexeme hi",
" surface",
" text \"अर्थ पक्ष\"",
" notation word_surface",
" denotation semantic_facet",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 语义方面",
" notation word_surface",
" denotation semantic_facet",
" part_of_speech noun_phrase",
" notation",
" defined-by semantic_facet",
" role semantic_facet_kind",
" notation links_notation_format",
" annotation semantic_gloss",
" denotation semantic_facet",
" connotation links_notation_format",
" lexeme en",
" surface",
" text notation",
" notation word_surface",
" denotation notation",
" part_of_speech noun",
" lexeme ru",
" surface",
" text нотация",
" notation word_surface",
" denotation notation",
" part_of_speech noun",
" lexeme hi",
" surface",
" text संकेतन",
" notation word_surface",
" denotation notation",
" part_of_speech noun",
" lexeme zh",
" surface",
" text 记法",
" notation word_surface",
" denotation notation",
" part_of_speech noun",
" annotation",
" defined-by semantic_facet",
" role semantic_facet_kind",
" notation links_notation_format",
" annotation semantic_gloss",
" denotation semantic_facet",
" connotation semantic_gloss",
" lexeme en",
" surface",
" text annotation",
" notation word_surface",
" denotation annotation",
" part_of_speech noun",
" lexeme ru",
" surface",
" text аннотация",
" notation word_surface",
" denotation annotation",
" part_of_speech noun",
" lexeme hi",
" surface",
" text टिप्पणी",
" notation word_surface",
" denotation annotation",
" part_of_speech noun",
" lexeme zh",
" surface",
" text 注释",
" notation word_surface",
" denotation annotation",
" part_of_speech noun",
" denotation",
" defined-by semantic_facet",
" role semantic_facet_kind",
" notation links_notation_format",
" annotation semantic_gloss",
" denotation semantic_facet",
" connotation relation",
" lexeme en",
" surface",
" text denotation",
" notation word_surface",
" denotation denotation",
" part_of_speech noun",
" lexeme ru",
" surface",
" text денотация",
" notation word_surface",
" denotation denotation",
" part_of_speech noun",
" lexeme hi",
" surface",
" text \"निरूपित अर्थ\"",
" notation word_surface",
" denotation denotation",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 外延",
" notation word_surface",
" denotation denotation",
" part_of_speech noun",
" connotation",
" defined-by semantic_facet",
" role semantic_facet_kind",
" notation links_notation_format",
" annotation semantic_gloss",
" denotation semantic_facet",
" connotation concept",
" lexeme en",
" surface",
" text connotation",
" notation word_surface",
" denotation connotation",
" part_of_speech noun",
" lexeme ru",
" surface",
" text коннотация",
" notation word_surface",
" denotation connotation",
" part_of_speech noun",
" lexeme hi",
" surface",
" text व्यंजना",
" notation word_surface",
" denotation connotation",
" part_of_speech noun",
" lexeme zh",
" surface",
" text 内涵",
" notation word_surface",
" denotation connotation",
" part_of_speech noun",
" semantic_gloss",
" defined-by annotation",
" role semantic_annotation_artifact",
" lexeme en",
" surface",
" text gloss",
" notation word_surface",
" denotation semantic_gloss",
" part_of_speech noun",
" lexeme ru",
" surface",
" text глосса",
" notation word_surface",
" denotation semantic_gloss",
" part_of_speech noun",
" lexeme hi",
" surface",
" text ग्लॉस",
" notation word_surface",
" denotation semantic_gloss",
" part_of_speech noun",
" lexeme zh",
" surface",
" text 释义",
" notation word_surface",
" denotation semantic_gloss",
" part_of_speech noun",
" external_knowledge_source",
" defined-by entity",
" defined-by annotation",
" role semantic_grounding_artifact",
" lexeme en",
" surface",
" text \"external source\"",
" notation word_surface",
" denotation external_knowledge_source",
" part_of_speech noun_phrase",
" lexeme ru",
" surface",
" text \"внешний источник\"",
" notation word_surface",
" denotation external_knowledge_source",
" part_of_speech noun_phrase",
" lexeme hi",
" surface",
" text \"बाहरी स्रोत\"",
" notation word_surface",
" denotation external_knowledge_source",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 外部来源",
" notation word_surface",
" denotation external_knowledge_source",
" part_of_speech noun_phrase",
" cached_source_response",
" defined-by external_knowledge_source",
" defined-by annotation",
" role semantic_grounding_artifact",
" lexeme en",
" surface",
" text \"cached source response\"",
" notation word_surface",
" denotation cached_source_response",
" part_of_speech noun_phrase",
" lexeme ru",
" surface",
" text \"кэшированный ответ источника\"",
" notation word_surface",
" denotation cached_source_response",
" part_of_speech noun_phrase",
" lexeme hi",
" surface",
" text \"कैश स्रोत उत्तर\"",
" notation word_surface",
" denotation cached_source_response",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 缓存来源响应",
" notation word_surface",
" denotation cached_source_response",
" part_of_speech noun_phrase",
"meanings",
" lexical_form",
" grounded-in Q4147654",
" defined-by notation",
" defined-by word_surface",
" role lexical_meta",
" notation word_surface",
" annotation semantic_gloss",
" denotation lexical_sense",
" connotation part_of_speech",
" lexeme en",
" surface",
" text \"lexical form\"",
" notation word_surface",
" denotation lexical_form",
" part_of_speech noun_phrase",
" lexeme ru",
" surface",
" text \"лексическая форма\"",
" notation word_surface",
" denotation lexical_form",
" part_of_speech noun_phrase",
" lexeme hi",
" surface",
" text \"शाब्दिक रूप\"",
" notation word_surface",
" denotation lexical_form",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 词形",
" notation word_surface",
" denotation lexical_form",
" part_of_speech noun",
" word_surface",
" defined-by lexical_form",
" defined-by notation",
" role lexical_meta",
" notation links_notation_format",
" annotation semantic_gloss",
" denotation lexical_form",
" connotation lexical_sense",
" lexeme en",
" surface",
" text \"word surface\"",
" notation word_surface",
" denotation word_surface",
" part_of_speech noun_phrase",
" lexeme ru",
" surface",
" text \"словесная форма\"",
" notation word_surface",
" denotation word_surface",
" part_of_speech noun_phrase",
" lexeme hi",
" surface",
" text \"शब्द सतह\"",
" notation word_surface",
" denotation word_surface",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 词面",
" notation word_surface",
" denotation word_surface",
" part_of_speech noun",
" lexical_sense",
" grounded-in Q1570700",
" defined-by denotation",
" defined-by concept",
" role lexical_meta",
" notation word_surface",
" annotation semantic_gloss",
" denotation concept",
" connotation lexical_form",
" lexeme en",
" surface",
" text \"lexical sense\"",
" notation word_surface",
" denotation lexical_sense",
" part_of_speech noun_phrase",
" lexeme ru",
" surface",
" text \"лексическое значение\"",
" notation word_surface",
" denotation lexical_sense",
" part_of_speech noun_phrase",
" lexeme hi",
" surface",
" text \"शाब्दिक अर्थ\"",
" notation word_surface",
" denotation lexical_sense",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 词义",
" notation word_surface",
" denotation lexical_sense",
" part_of_speech noun",
" part_of_speech",
" grounded-in Q82042",
" defined-by semantic_facet",
" defined-by lexical_form",
" role semantic_facet_kind",
" role lexical_meta",
" notation word_surface",
" annotation semantic_gloss",
" denotation semantic_facet",
" connotation lexical_form",
" lexeme en",
" surface",
" text \"part of speech\"",
" notation word_surface",
" denotation part_of_speech",
" part_of_speech noun_phrase",
" lexeme ru",
" surface",
" text \"часть речи\"",
" notation word_surface",
" denotation part_of_speech",
" part_of_speech noun_phrase",
" lexeme hi",
" surface",
" text \"शब्द भेद\"",
" notation word_surface",
" denotation part_of_speech",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 词类",
" notation word_surface",
" denotation part_of_speech",
" part_of_speech noun",
" noun",
" grounded-in Q1084",
" defined-by part_of_speech",
" role lexical_meta",
" notation word_surface",
" annotation semantic_gloss",
" denotation part_of_speech",
" connotation lexical_form",
" lexeme en",
" surface",
" text noun",
" notation word_surface",
" denotation noun",
" part_of_speech noun",
" lexeme ru",
" surface",
" text существительное",
" notation word_surface",
" denotation noun",
" part_of_speech noun",
" lexeme hi",
" surface",
" text संज्ञा",
" notation word_surface",
" denotation noun",
" part_of_speech noun",
" lexeme zh",
" surface",
" text 名词",
" notation word_surface",
" denotation noun",
" part_of_speech noun",
" noun_phrase",
" grounded-in Q1401131",
" defined-by noun",
" defined-by lexical_form",
" role lexical_meta",
" notation word_surface",
" annotation semantic_gloss",
" denotation part_of_speech",
" connotation noun",
" lexeme en",
" surface",
" text \"noun phrase\"",
" notation word_surface",
" denotation noun_phrase",
" part_of_speech noun_phrase",
" lexeme ru",
" surface",
" text \"именная группа\"",
" notation word_surface",
" denotation noun_phrase",
" part_of_speech noun_phrase",
" lexeme hi",
" surface",
" text \"संज्ञा पद\"",
" notation word_surface",
" denotation noun_phrase",
" part_of_speech noun_phrase",
" lexeme zh",
" surface",
" text 名词短语",
" notation word_surface",
" denotation noun_phrase",
" part_of_speech noun_phrase",
"meanings",
" reference: reference-action link # concept reference",
" grounded-in Q121769 # wikidata concept reference",
" wiktionary WT-en-reference # wiktionary lexical source reference",
" source-lexeme L5785 # wikidata english noun reference",
" language Q1860 # wikidata language english",
" lexical-category Q1084 # wikidata category noun",
" form L5785-F1 # wikidata singular form reference",
" feature Q110786 # wikidata singular",
" form L5785-F2 # wikidata plural form references",
" feature Q146786 # wikidata plural",
" sense L5785-S4 # wikidata designation relation sense",
" source-lexeme L166084 # wikidata russian lexeme ssylka",
" lexical-category Q1084 # wikidata category noun",
" source-lexeme L166085 # wikidata russian lexeme ssylka",
" lexical-category Q1084 # wikidata category noun",
" role links_root",
" surface L5785-F1 # wikidata english singular surface",
" text reference",
" language en",
" sense L5785-S4 # wikidata designation relation sense",
" surface L166084-F1 # wikidata russian singular surface",
" text ссылка",
" language ru",
" source-lexeme L166084 # wikidata russian lexeme",
" surface L166085-F1 # wikidata russian singular surface",
" text ссылка",
" language ru",
" source-lexeme L166085 # wikidata russian lexeme",
" lexeme hi",
" surface",
" text संदर्भ",
" lexeme zh",
" surface",
" text 引用",
" reference-action: makes reference point-at link # concept reference-action",
" source-lexeme L41576 # wikidata english verb reference",
" language Q1860 # wikidata language english",
" lexical-category Q24905 # wikidata category verb",
" form L41576-F1 # wikidata citation form reference",
" sense L41576-S1 # wikidata refer to sense",
" role links_root",
" surface L41576-F1 # wikidata english verb surface",
" text reference",
" language en",
" sense L41576-S1 # wikidata refer to sense",
" lexeme ru",
" surface",
" text \"действие ссылки\"",
" lexeme hi",
" surface",
" text \"संदर्भ क्रिया\"",
" lexeme zh",
" surface",
" text 引用动作",
" link-action: makes link from any-of-reference # concept link-action",
" role links_root",
" lexeme en",
" surface",
" text \"link action\"",
" lexeme ru",
" surface",
" text \"действие связи\"",
" lexeme hi",
" surface",
" text \"लिंक क्रिया\"",
" lexeme zh",
" surface",
" text 链接动作",
" any-of-reference: reference repeatable-from-zero # concept any-of-reference",
" role links_root",
" lexeme en",
" surface",
" text \"any of reference\"",
" lexeme ru",
" surface",
" text \"любые ссылки\"",
" lexeme hi",
" surface",
" text \"कोई भी संदर्भ\"",
" lexeme zh",
" surface",
" text 任意引用",
" any-of-link: link repeatable-from-zero # concept any-of-link",
" role links_root",
" lexeme en",
" surface",
" text \"any of link\"",
" lexeme ru",
" surface",
" text \"любые связи\"",
" lexeme hi",
" surface",
" text \"कोई भी लिंक\"",
" lexeme zh",
" surface",
" text 任意链接",
" repeatable-from-zero: same link-action zero-or-more # concept repeatable-from-zero",
" role links_root",
" lexeme en",
" surface",
" text \"repeatable from zero\"",
" lexeme ru",
" surface",
" text \"повторяемое с нуля\"",
" lexeme hi",
" surface",
" text \"शून्य से दोहरनीय\"",
" lexeme zh",
" surface",
" text 从零可重复",
" zero-or-more: zero or-else more # concept zero-or-more",
" role links_root",
" lexeme en",
" surface",
" text \"zero or more\"",
" lexeme ru",
" surface",
" text \"ноль или больше\"",
" lexeme hi",
" surface",
" text \"शून्य या अधिक\"",
" lexeme zh",
" surface",
" text 零个或多个",
" makes: link-action action # concept makes",
" role links_root",
" lexeme en",
" surface",
" text makes",
" lexeme ru",
" surface",
" text создает",
" lexeme hi",
" surface",
" text \"बनाता है\"",
" lexeme zh",
" surface",
" text 生成",
" point-at: reference-action to link # concept point-at",
" role links_root",
" lexeme en",
" surface",
" text \"point at\"",
" lexeme ru",
" surface",
" text \"указывать на\"",
" lexeme hi",
" surface",
" text \"इशारा करना\"",
" lexeme zh",
" surface",
" text 指向",
" of: belonging from part to whole # concept of",
" role links_root",
" lexeme en",
" surface",
" text of",
" lexeme ru",
" surface",
" text от",
" lexeme hi",
" surface",
" text का",
" lexeme zh",
" surface",
" text 的",
" from: source end direction # concept from",
" role links_root",
" lexeme en",
" surface",
" text from",
" lexeme ru",
" surface",
" text из",
" lexeme hi",
" surface",
" text से",
" lexeme zh",
" surface",
" text 从",
" to: target end direction # concept to",
" role links_root",
" lexeme en",
" surface",
" text to",
" lexeme ru",
" surface",
" text к",
" lexeme hi",
" surface",
" text को",
" lexeme zh",
" surface",
" text 到",
" and: together-with # concept and",
" role links_root",
" lexeme en",
" surface",
" text and",
" lexeme ru",
" surface",
" text и",
" lexeme hi",
" surface",
" text और",
" lexeme zh",
" surface",
" text 和",
" not: not (not not) # concept not",
" self-equation not",
" role links_root",
" lexeme en",
" surface",
" text not",
" lexeme ru",
" surface",
" text не",
" lexeme hi",
" surface",
" text नहीं",
" lexeme zh",
" surface",
" text 非",
" same: not (not same) link # concept same",
" self-equation same",
" role links_root",
" lexeme en",
" surface",
" text same",
" lexeme ru",
" surface",
" text \"тот же\"",
" lexeme hi",
" surface",
" text समान",
" lexeme zh",
" surface",
" text 相同",
" other: not same # concept other",
" role links_root",
" lexeme en",
" surface",
" text other",
" lexeme ru",
" surface",
" text другой",
" lexeme hi",
" surface",
" text अन्य",
" lexeme zh",
" surface",
" text 其他",
" single: not other # concept single",
" role links_root",
" lexeme en",
" surface",
" text single",
" lexeme ru",
" surface",
" text одиночный",
" lexeme hi",
" surface",
" text एकल",
" lexeme zh",
" surface",
" text 单一",
" or-else: choice between other # concept or-else",
" role links_root",
" lexeme en",
" surface",
" text \"or else\"",
" lexeme ru",
" surface",
" text \"или иначе\"",
" lexeme hi",
" surface",
" text \"या अन्य\"",
" lexeme zh",
" surface",
" text 或者",
" more: one and one # concept more",
" role links_root",
" lexeme en",
" surface",
" text more",
" lexeme ru",
" surface",
" text больше",
" lexeme hi",
" surface",
" text अधिक",
" lexeme zh",
" surface",
" text 更多",
" is-identity: same # concept is-identity",
" role links_root",
" lexeme en",
" surface",
" text \"is identity\"",
" lexeme ru",
" surface",
" text \"есть тождество\"",
" lexeme hi",
" surface",
" text \"पहचान है\"",
" lexeme zh",
" surface",
" text 是同一",
" is-a-kind-of: subtype supertype direction # concept is-a-kind-of",
" role links_root",
" lexeme en",
" surface",
" text \"is a kind of\"",
" lexeme ru",
" surface",
" text \"является видом\"",
" lexeme hi",
" surface",
" text \"का प्रकार है\"",
" lexeme zh",
" surface",
" text 是一种",
" subtype: is-a-kind-of type # concept subtype",
" role links_root",
" lexeme en",
" surface",
" text subtype",
" lexeme ru",
" surface",
" text подтип",
" lexeme hi",
" surface",
" text उपप्रकार",
" lexeme zh",
" surface",
" text 子类型",
" supertype: type type # concept supertype",
" role links_root",
" lexeme en",
" surface",
" text supertype",
" lexeme ru",
" surface",
" text супертип",
" lexeme hi",
" surface",
" text अधिप्रकार",
" lexeme zh",
" surface",
" text 超类型",
" held-by: from property to entity # concept held-by",
" role links_root",
" lexeme en",
" surface",
" text \"held by\"",
" lexeme ru",
" surface",
" text удерживается",
" lexeme hi",
" surface",
" text \"द्वारा धारित\"",
" lexeme zh",
" surface",
" text 持有于",
" amount: count any-of-link # concept amount",
" role links_root",
" lexeme en",
" surface",
" text amount",
" lexeme ru",
" surface",
" text количество",
" lexeme hi",
" surface",
" text परिमाण",
" lexeme zh",
" surface",
" text 数额",
" size: extent single link # concept size",
" role links_root",
" lexeme en",
" surface",
" text size",
" lexeme ru",
" surface",
" text размер",
" lexeme hi",
" surface",
" text आकार",
" lexeme zh",
" surface",
" text 大小",
" count: amount distinct reference # concept count",
" role links_root",
" lexeme en",
" surface",
" text count",
" lexeme ru",
" surface",
" text счет",
" lexeme hi",
" surface",
" text गिनती",
" lexeme zh",
" surface",
" text 计数",
" extent: size single reference # concept extent",
" role links_root",
" lexeme en",
" surface",
" text extent",
" lexeme ru",
" surface",
" text протяженность",
" lexeme hi",
" surface",
" text विस्तार",
" lexeme zh",
" surface",
" text 范围",
" distinct: other # concept distinct",
" role links_root",
" lexeme en",
" surface",
" text distinct",
" lexeme ru",
" surface",
" text различный",
" lexeme hi",
" surface",
" text भिन्न",
" lexeme zh",
" surface",
" text 不同",
" any: quantity not fixed # concept any",
" role links_root",
" lexeme en",
" surface",
" text any",
" lexeme ru",
" surface",
" text любой",
" lexeme hi",
" surface",
" text \"कोई भी\"",
" lexeme zh",
" surface",
" text 任意",
" fixed: same repeatable-from-zero # concept fixed",
" role links_root",
" lexeme en",
" surface",
" text fixed",
" lexeme ru",
" surface",
" text фиксированный",
" lexeme hi",
" surface",
" text निश्चित",
" lexeme zh",
" surface",
" text 固定",
" empty: any-of-reference zero # concept empty",
" role links_root",
" lexeme en",
" surface",
" text empty",
" lexeme ru",
" surface",
" text пустой",
" lexeme hi",
" surface",
" text खाली",
" lexeme zh",
" surface",
" text 空",
" source: relation direction # concept source",
" role links_root",
" lexeme en",
" surface",
" text source",
" lexeme ru",
" surface",
" text источник",
" lexeme hi",
" surface",
" text स्रोत",
" lexeme zh",
" surface",
" text 来源",
" target: relation direction # concept target",
" role links_root",
" lexeme en",
" surface",
" text target",
" lexeme ru",
" surface",
" text цель",
" lexeme hi",
" surface",
" text लक्ष्य",
" lexeme zh",
" surface",
" text 目标",
" end: relation direction # concept end",
" role links_root",
" lexeme en",
" surface",
" text end",
" lexeme ru",
" surface",
" text конец",
" lexeme hi",
" surface",
" text सिरा",
" lexeme zh",
" surface",
" text 端点",
" direction: relation source target # concept direction",
" role links_root",
" lexeme en",
" surface",
" text direction",
" lexeme ru",
" surface",
" text направление",
" lexeme hi",
" surface",
" text दिशा",
" lexeme zh",
" surface",
" text 方向",
" belonging: relation part whole # concept belonging",
" role links_root",
" lexeme en",
" surface",
" text belonging",
" lexeme ru",
" surface",
" text принадлежность",
" lexeme hi",
" surface",
" text संबद्धता",
" lexeme zh",
" surface",
" text 归属",
" part: link whole # concept part",
" role links_root",
" lexeme en",
" surface",
" text part",
" lexeme ru",
" surface",
" text часть",
" lexeme hi",
" surface",
" text भाग",
" lexeme zh",
" surface",
" text 部分",
" whole: link part # concept whole",
" role links_root",
" lexeme en",
" surface",
" text whole",
" lexeme ru",
" surface",
" text целое",
" lexeme hi",
" surface",
" text संपूर्ण",
" lexeme zh",
" surface",
" text 整体",
" together-with: relation and # concept together-with",
" role links_root",
" lexeme en",
" surface",
" text \"together with\"",
" lexeme ru",
" surface",
" text \"вместе с\"",
" lexeme hi",
" surface",
" text \"साथ में\"",
" lexeme zh",
" surface",
" text 一起",
" choice: relation # concept choice",
" role links_root",
" lexeme en",
" surface",
" text choice",
" lexeme ru",
" surface",
" text выбор",
" lexeme hi",
" surface",
" text चयन",
" lexeme zh",
" surface",
" text 选择",
" between: relation # concept between",
" source-lexeme L3412 # wikidata english adposition between",
" language Q1860 # wikidata language english",
" form L3412-F1 # wikidata surface between",
" sense L3412-S1 # wikidata spatial relation sense",
" role links_root",
" surface L3412-F1 # wikidata english surface between",
" text between",
" language en",
" sense L3412-S1 # wikidata spatial relation sense",
" lexeme ru",
" surface",
" text между",
" lexeme hi",
" surface",
" text \"बीच में\"",
" lexeme zh",
" surface",
" text 之间",
" semantic-facet: relation # concept semantic-facet",
" role links_root",
" lexeme en",
" surface",
" text \"semantic facet\"",
" lexical-sense: denotation concept # concept lexical-sense",
" role links_root",
" lexeme en",
" surface",
" text \"lexical sense\"",
" self-equation: semantic-facet same # concept self-equation",
" role semantic_facet_kind",
" role links_root",
" lexeme en",
" surface",
" text \"self equation\"",
" lexeme ru",
" surface",
" text самоуравнение",
" lexeme hi",
" surface",
" text स्व-समीकरण",
" lexeme zh",
" surface",
" text 自方程",
" symbol: reference notation # concept symbol",
" role links_root",
" lexeme en",
" surface",
" text symbol",
" lexeme ru",
" surface",
" text символ",
" lexeme hi",
" surface",
" text प्रतीक",
" lexeme zh",
" surface",
" text 符号",
" one-symbol-one-meaning: symbol single lexical-sense # concept one-symbol-one-meaning",
" role links_root",
" lexeme en",
" surface",
" text \"one symbol one meaning\"",
" lexeme ru",
" surface",
" text \"один символ одно значение\"",
" lexeme hi",
" surface",
" text \"एक प्रतीक एक अर्थ\"",
" lexeme zh",
" surface",
" text 一符一义",
" sense-split: lexical-sense other symbol # concept sense-split",
" role links_root",
" lexeme en",
" surface",
" text \"sense split\"",
" lexeme ru",
" surface",
" text \"разделение значений\"",
" lexeme hi",
" surface",
" text \"अर्थ विभाजन\"",
" lexeme zh",
" surface",
" text 义项拆分",
" bank-river: sense-split part whole # concept bank-river",
" role links_root",
" lexeme en",
" surface",
" text \"river bank\"",
" lexeme ru",
" surface",
" text \"речной берег\"",
" lexeme hi",
" surface",
" text \"नदी तट\"",
" lexeme zh",
" surface",
" text 河岸",
" bank-money: sense-split entity property # concept bank-money",
" role links_root",
" lexeme en",
" surface",
" text \"financial bank\"",
" lexeme ru",
" surface",
" text \"финансовый банк\"",
" lexeme hi",
" surface",
" text \"वित्तीय बैंक\"",
" lexeme zh",
" surface",
" text 银行",
"meanings",
" wikidata_item_apple",
" grounded-in Q89",
" defined-by entity",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text apple",
" surface",
" text apples",
" lexeme ru",
" surface",
" text яблоко",
" surface",
" text яблоки",
" lexeme hi",
" surface",
" text सेब",
" lexeme zh",
" surface",
" text 苹果",
" surface",
" text 蘋果",
" wikidata_item_fruit",
" grounded-in Q3314483",
" defined-by concept",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text fruit",
" surface",
" text fruits",
" surface",
" text \"edible fruit\"",
" lexeme ru",
" surface",
" text фрукт",
" surface",
" text фрукты",
" surface",
" text плод",
" lexeme hi",
" surface",
" text फल",
" lexeme zh",
" surface",
" text 水果",
" surface",
" text 水果类",
" wikidata_item_sorting_algorithm",
" grounded-in Q181593",
" defined-by concept",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text \"sorting algorithm\"",
" surface",
" text \"sorting algorithms\"",
" surface",
" text \"sort algorithm\"",
" lexeme ru",
" surface",
" text \"алгоритм сортировки\"",
" lexeme hi",
" surface",
" text \"सॉर्टिंग एल्गोरिदम\"",
" lexeme zh",
" surface",
" text 排序算法",
" wikidata_item_water",
" grounded-in Q283",
" defined-by entity",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text water",
" lexeme ru",
" surface",
" text вода",
" lexeme hi",
" surface",
" text पानी",
" lexeme zh",
" surface",
" text 水",
" wikidata_item_bread",
" grounded-in Q7802",
" defined-by entity",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text bread",
" lexeme ru",
" surface",
" text хлеб",
" lexeme hi",
" surface",
" text रोटी",
" lexeme zh",
" surface",
" text 面包",
" surface",
" text 麵包",
" wikidata_item_carrot",
" grounded-in Q81",
" defined-by entity",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text carrot",
" surface",
" text carrots",
" lexeme ru",
" surface",
" text морковь",
" lexeme hi",
" surface",
" text गाजर",
" lexeme zh",
" surface",
" text 胡萝卜",
" surface",
" text 胡蘿蔔",
" wikidata_item_wikidata",
" grounded-in Q2013",
" defined-by entity",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text Wikidata",
" lexeme ru",
" surface",
" text викидата",
" lexeme hi",
" surface",
" text विकिडेटा",
" lexeme zh",
" surface",
" text 维基数据",
" surface",
" text 維基數據",
" wikidata_item_wikipedia",
" grounded-in Q52",
" defined-by entity",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text Wikipedia",
" lexeme ru",
" surface",
" text википедия",
" lexeme hi",
" surface",
" text विकिपीडिया",
" lexeme zh",
" surface",
" text 维基百科",
" surface",
" text 維基百科",
" wikidata_item_wiktionary",
" grounded-in Q151",
" defined-by entity",
" role wikidata_entity_anchor",
" lexeme en",
" surface",
" text Wiktionary",
" lexeme ru",
" surface",
" text викисловарь",
" lexeme hi",
" surface",
" text विक्षनरी",
" lexeme zh",
" surface",
" text 维基词典",
" surface",
" text 維基辭典",
" wikidata_property_subclass_of",
" grounded-in P279",
" defined-by relation",
" role binary_relation_property",
" lexeme en",
" surface",
" text \"subclass of\"",
" surface",
" text \"is a kind of\"",
" surface",
" text \"is a type of\"",
" surface",
" text \"is subclass of\"",
" surface",
" text \"kind of\"",
" surface",
" text \"type of\"",
" lexeme ru",
" surface",
" text род",
" surface",
" text тип",
" lexeme hi",
" surface",
" text उपवर्ग",
" lexeme zh",
" surface",
" text 子类",
" wikidata_property_instance_of",
" grounded-in P31",
" defined-by relation",
" role binary_relation_property",
" lexeme en",
" surface",
" text \"instance of\"",
" surface",
" text \"is a\"",
" action wikidata_property_subclass_of",
" surface",
" text \"is an\"",
" action wikidata_property_subclass_of",
" surface",
" text \"is the\"",
" surface",
" text \"are a\"",
" action wikidata_property_subclass_of",
" surface",
" text \"are an\"",
" action wikidata_property_subclass_of",
" lexeme ru",
" surface",
" text является",
" surface",
" text это",
" lexeme hi",
" surface",
" text है",
" lexeme zh",
" surface",
" text 是",
" wikidata_property_part_of",
" grounded-in P361",
" defined-by relation",
" role binary_relation_property",
" lexeme en",
" surface",
" text \"part of\"",
" surface",
" text \"is part of\"",
" surface",
" text \"belongs to\"",
" lexeme ru",
" surface",
" text \"является частью\"",
" surface",
" text часть",
" lexeme hi",
" surface",
" text \"का हिस्सा\"",
" lexeme zh",
" surface",
" text 属于",
" wikidata_property_has_part",
" grounded-in P527",
" defined-by relation",
" role binary_relation_property",
" lexeme en",
" surface",
" text \"has part\"",
" surface",
" text \"has a part\"",
" surface",
" text contains",
" surface",
" text includes",
" lexeme ru",
" surface",
" text содержит",
" lexeme hi",
" surface",
" text \"शामिल है\"",
" lexeme zh",
" surface",
" text 包含",
" wikidata_property_capital",
" grounded-in P36",
" defined-by relation",
" role binary_relation_property",
" lexeme en",
" surface",
" text capital",
" surface",
" text \"is the capital of\"",
" surface",
" text \"is capital of\"",
" surface",
" text \"capital of\"",
" lexeme ru",
" surface",
" text столица",
" lexeme hi",
" surface",
" text राजधानी",
" lexeme zh",
" surface",
" text 首都",
" wikidata_property_named_after",
" grounded-in P138",
" defined-by relation",
" role binary_relation_property",
" lexeme en",
" surface",
" text \"named after\"",
" surface",
" text \"is named after\"",
" lexeme ru",
" surface",
" text \"назван в честь\"",
" lexeme hi",
" surface",
" text \"के नाम पर\"",
" lexeme zh",
" surface",
" text 命名自",
" wikidata_property_translation",
" grounded-in P5972",
" defined-by relation",
" role translation_property",
" lexeme en",
" surface",
" text translation",
" surface",
" text translate",
" surface",
" text translates",
" lexeme ru",
" surface",
" text переведи",
" surface",
" text перевести",
" lexeme hi",
" surface",
" text अनुवाद",
" lexeme zh",
" surface",
" text 翻译",
" surface",
" text 翻譯",
" wikidata_property_item_for_this_sense",
" grounded-in P5137",
" defined-by relation",
" role binary_relation_property",
" lexeme en",
" surface",
" text \"item for this sense\"",
" surface",
" text means",
" surface",
" text \"meaning of\"",
" surface",
" text \"meaning item\"",
" surface",
" text \"sense item\"",
" lexeme ru",
" surface",
" text означает",
" lexeme hi",
" surface",
" text अर्थ",
" lexeme zh",
" surface",
" text 意思",
"meanings",
" behavior_rule",
" defined-by relation",
" defined-by knowledge",
" role rule_listing_subject",
" lexeme en",
" surface",
" text rules",
" surface",
" text \"rule list\"",
" surface",
" text \"rules list\"",
" lexeme ru",
" surface",
" text правил",
" surface",
" text правила",
" lexeme hi",
" surface",
" text नियम",
" surface",
" text नियमों",
" lexeme zh",
" surface",
" text 规则",
" surface",
" text 規則",
" rule_enumeration_request",
" defined-by action",
" defined-by inquiry",
" role rule_listing_request",
" lexeme en",
" surface",
" text list",
" surface",
" text show",
" surface",
" text what",
" surface",
" text which",
" lexeme ru",
" surface",
" text список",
" surface",
" text перечисли",
" surface",
" text покажи",
" surface",
" text какие",
" lexeme hi",
" surface",
" text सूची",
" surface",
" text सूचीबद्ध",
" surface",
" text दिखाओ",
" surface",
" text दिखाएं",
" surface",
" text बताओ",
" surface",
" text गिनाओ",
" surface",
" text कौन",
" lexeme zh",
" surface",
" text 列出",
" surface",
" text 显示",
" surface",
" text 顯示",
" surface",
" text 展示",
" surface",
" text 哪些",
" surface",
" text 什么",
" behavior_domain",
" defined-by self_reference",
" defined-by concept",
" role rule_listing_scope",
" lexeme en",
" surface",
" text behavior",
" lexeme ru",
" surface",
" text поведения",
" lexeme hi",
" surface",
" text व्यवहार",
" lexeme zh",
" surface",
" text 行为",
" surface",
" text 行為",
" assistant_own_ruleset",
" defined-by self_reference",
" defined-by knowledge",
" role rule_listing_scope",
" lexeme en",
" surface",
" text your",
" surface",
" text own",
" surface",
" text current",
" surface",
" text existing",
" lexeme ru",
" surface",
" text свои",
" surface",
" text своих",
" surface",
" text твои",
" surface",
" text твоих",
" surface",
" text собственные",
" surface",
" text \"список правил\"",
" lexeme hi",
" surface",
" text अपने",
" surface",
" text तुम्हारे",
" surface",
" text आपके",
" surface",
" text \"नियमों की सूची\"",
" lexeme zh",
" surface",
" text 你的",
" surface",
" text 您的",
" surface",
" text 自己",
" surface",
" text 规则列表",
" surface",
" text 規則列表",
" behavior_rule_set_phrase",
" defined-by inquiry",
" defined-by knowledge",
" role rule_listing_phrase",
" lexeme en",
" surface",
" text \"list behavior rules\"",
" surface",
" text \"list all behavior rules\"",
" surface",
" text \"show behavior rules\"",
" surface",
" text \"show all behavior rules\"",
" surface",
" text \"what behavior rules\"",
" surface",
" text \"existing behavior rules\"",
" lexeme ru",
" surface",
" text \"список правил поведения\"",
" surface",
" text \"покажи правила поведения\"",
" surface",
" text \"какие правила поведения\"",
" lexeme hi",
" surface",
" text \"व्यवहार के नियम\"",
" surface",
" text \"व्यवहार नियम सूचीबद्ध करें\"",
" lexeme zh",
" surface",
" text 行为规则",
" surface",
" text 列出行为规则",
"meanings",
" prove",
" defined-by inquiry",
" defined-by action",
" role proof_directive",
" role proof_claim_scaffold",
" lexeme en",
" surface",
" text prove",
" surface",
" text proof",
" surface",
" text \"prove that …\"",
" surface",
" text \"prove …\"",
" surface",
" text \"proof of …\"",
" surface",
" text \"proof that …\"",
" surface",
" text \"show that …\"",
" surface",
" text \"demonstrate that …\"",
" surface",
" text \"demonstrate …\"",
" surface",
" text \"can you prove that …\"",
" surface",
" text \"can you prove …\"",
" surface",
" text \"could you prove that …\"",
" surface",
" text \"could you prove …\"",
" surface",
" text \"please prove that …\"",
" surface",
" text \"please prove …\"",
" surface",
" text \"give me a proof of …\"",
" surface",
" text \"give me a proof that …\"",
" surface",
" text \"give a proof of …\"",
" surface",
" text \"give a proof that …\"",
" lexeme ru",
" surface",
" text докажи",
" surface",
" text докажите",
" surface",
" text доказать",
" surface",
" text доказательство",
" surface",
" text \"докажи что …\"",
" surface",
" text \"докажите что …\"",
" surface",
" text \"доказать что …\"",
" surface",
" text \"докажите …\"",
" surface",
" text \"докажи …\"",
" surface",
" text \"доказать …\"",
" surface",
" text \"доказательство …\"",
" lexeme hi",
" surface",
" text \"साबित करो कि …\"",
" surface",
" text \"साबित कीजिए कि …\"",
" surface",
" text \"साबित कर …\"",
" surface",
" text \"सिद्ध कीजिए कि …\"",
" surface",
" text \"सिद्ध करो कि …\"",
" lexeme zh",
" surface",
" text 证明…",
" surface",
" text 證明…",
" proof_request_frame",
" defined-by inquiry",
" defined-by prove",
" role proof_request_lead",
" lexeme en",
" surface",
" text \"can you prove…\"",
" surface",
" text \"could you prove…\"",
" surface",
" text \"please prove…\"",
" surface",
" text \"give me a proof…\"",
" surface",
" text \"give a proof…\"",
" surface",
" text \"show that …\"",
" surface",
" text \"demonstrate that …\"",
" lexeme ru",
" surface",
" text \"можешь доказать…\"",
" surface",
" text \"можете доказать…\"",
" surface",
" text \"сможешь доказать…\"",
" surface",
" text \"пожалуйста докажи…\"",
" surface",
" text \"пожалуйста докажите…\"",
" lexeme hi",
" surface",
" text \"क्या आप साबित कर सकते हैं…\"",
" surface",
" text \"क्या आप सिद्ध कर सकते हैं…\"",
" surface",
" text \"कृपया सिद्ध कीजिए…\"",
" surface",
" text \"कृपया साबित कीजिए…\"",
" lexeme zh",
" surface",
" text 你能证明…",
" surface",
" text 你能證明…",
" surface",
" text 请证明…",
" surface",
" text 請證明…",
" proof_assertion",
" defined-by inquiry",
" defined-by prove",
" role proof_marker",
" lexeme en",
" surface",
" text \" prove that \"",
" surface",
" text \" proof of \"",
" lexeme ru",
" surface",
" text \" докажи \"",
" surface",
" text \" докажите \"",
" surface",
" text \" доказать \"",
" surface",
" text \" доказательство \"",
" lexeme hi",
" surface",
" text \"साबित कर\"",
" surface",
" text \"साबित कीजिए\"",
" surface",
" text \"साबित कीजिये\"",
" surface",
" text \"सिद्ध कर\"",
" surface",
" text \"सिद्ध कीजिए\"",
" surface",
" text \"सिद्ध कीजिये\"",
" surface",
" text प्रमाण",
" lexeme zh",
" surface",
" text 证明",
" surface",
" text 證明",
" godel",
" defined-by concept",
" role proof_concept_godel",
" lexeme en",
" surface",
" text godel",
" surface",
" text gödel",
" lexeme ru",
" surface",
" text гёдел",
" surface",
" text гёделя",
" surface",
" text гедел",
" lexeme hi",
" surface",
" text गोडेल",
" lexeme zh",
" surface",
" text 哥德尔",
" determinism",
" defined-by concept",
" role proof_concept_determinism",
" lexeme en",
" surface",
" text determinism",
" surface",
" text deterministic",
" lexeme ru",
" surface",
" text детерминизм",
" lexeme hi",
" surface",
" text निर्धारणवाद",
" lexeme zh",
" surface",
" text 决定论",
"meanings",
" physical_action_query",
" defined-by inquiry",
" defined-by action",
" role physical_action_trigger",
" lexeme en",
" surface",
" text \"did you suck\"",
" lexeme ru",
" surface",
" text сосал",
" surface",
" text сосала",
" surface",
" text сосёшь",
" surface",
" text соси",
" surface",
" text сосать",
" lexeme hi",
" surface",
" text \"क्या तुमने चूसा\"",
" lexeme zh",
" surface",
" text 你吸了吗",
" circular_joke_idiom",
" defined-by inquiry",
" defined-by concept",
" role circular_joke_phrase",
" lexeme en",
" surface",
" text \"buy an elephant\"",
" lexeme ru",
" surface",
" text \"купи слона\"",
" lexeme hi",
" surface",
" text \"हाथी खरीदो\"",
" lexeme zh",
" surface",
" text 买大象",
" vulgar_content",
" defined-by concept",
" role vulgar_content_marker",
" lexeme en",
" surface",
" text \"fuck you\"",
" surface",
" text fuckyou",
" surface",
" text \"suck my\"",
" surface",
" text \"suck my dick\"",
" surface",
" text \"suck my cock\"",
" surface",
" text \"you suck\"",
" surface",
" text \"eat shit\"",
" surface",
" text \"go to hell\"",
" surface",
" text asshole",
" surface",
" text motherfucker",
" surface",
" text \"you fucking\"",
" surface",
" text \"piece of shit\"",
" lexeme ru",
" surface",
" text ебать",
" surface",
" text ебёт",
" surface",
" text ебал",
" surface",
" text ёб",
" surface",
" text еблан",
" surface",
" text пизда",
" surface",
" text пиздец",
" surface",
" text пиздёж",
" surface",
" text хуй",
" surface",
" text хуёв",
" surface",
" text хуйня",
" surface",
" text блядь",
" surface",
" text блядство",
" surface",
" text залупа",
" surface",
" text мудак",
" surface",
" text мудила",
" surface",
" text шлюха",
" surface",
" text проститутка",
" surface",
" text ублюдок",
" surface",
" text сука",
" surface",
" text пидор",
" surface",
" text пидорас",
" lexeme hi",
" surface",
" text मादरचोद",
" surface",
" text बहनचोद",
" lexeme zh",
" surface",
" text 操你妈",
" surface",
" text 傻逼",
"meanings",
" explanation_request",
" defined-by inquiry",
" defined-by action",
" role explanation_request_lead",
" lexeme en",
" surface",
" text \"how …\"",
" surface",
" text \" how \"",
" surface",
" text \"explain …\"",
" surface",
" text \"describe …\"",
" surface",
" text \"what does …\"",
" surface",
" text \"what is …\"",
" surface",
" text \"tell me about …\"",
" surface",
" text \"how to use …\"",
" lexeme ru",
" surface",
" text \"как …\"",
" surface",
" text \" как \"",
" surface",
" text \"объясни …\"",
" surface",
" text \"расскажи …\"",
" surface",
" text \"что такое …\"",
" lexeme hi",
" surface",
" text \"कैसे काम\"",
" surface",
" text समझाओ…",
" surface",
" text \"क्या है …\"",
" lexeme zh",
" surface",
" text 如何工作",
" surface",
" text 怎么工作",
" surface",
" text 解释…",
" surface",
" text 是什么",
" code_method",
" defined-by action",
" defined-by concept",
" role code_method_noun",
" lexeme en",
" surface",
" text method",
" lexeme ru",
" surface",
" text метод",
" lexeme hi",
" surface",
" text विधि",
" lexeme zh",
" surface",
" text 方法",
"meanings",
" skill_teaching_trigger",
" defined-by relation",
" defined-by inquiry",
" role skill_teaching_trigger_lead",
" lexeme en",
" surface",
" text \"when i say\"",
" surface",
" text \"when the user says\"",
" surface",
" text \"when the user asks\"",
" surface",
" text \"if i ask\"",
" lexeme ru",
" surface",
" text \"когда я скажу\"",
" surface",
" text \"если я спрошу\"",
" lexeme hi",
" surface",
" text \"जब मैं कहूँ\"",
" surface",
" text \"अगर मैं पूछूँ\"",
" lexeme zh",
" surface",
" text 当我说",
" surface",
" text 当用户说",
" surface",
" text 当用户问",
" skill_teaching_response",
" defined-by action",
" defined-by answer",
" role skill_teaching_response_verb",
" lexeme en",
" surface",
" text answer",
" surface",
" text reply",
" surface",
" text respond",
" lexeme ru",
" surface",
" text ответ",
" lexeme hi",
" surface",
" text उत्तर",
" surface",
" text जवाब",
" lexeme zh",
" surface",
" text 回答",
" surface",
" text 回复",
" behavior_rule_edit",
" defined-by action",
" defined-by knowledge",
" role behavior_rule_edit_directive",
" lexeme en",
" surface",
" text \"add behavior rule\"",
" surface",
" text \"update behavior rule\"",
" lexeme ru",
" surface",
" text \"добавь правило поведения\"",
" surface",
" text \"обнови правило поведения\"",
" lexeme hi",
" surface",
" text \"व्यवहार नियम जोड़ो\"",
" surface",
" text \"व्यवहार नियम अपडेट करो\"",
" lexeme zh",
" surface",
" text 添加行为规则",
" surface",
" text 更新行为规则",
" skill_when_then",
" defined-by relation",
" defined-by concept",
" role skill_when_then_pair",
" lexeme en",
" surface",
" text \"when … then \"",
" surface",
" text \"when … do \"",
" lexeme ru",
" surface",
" text \"когда … тогда \"",
" surface",
" text \"когда … делай \"",
" surface",
" text \"когда … сделай \"",
" surface",
" text \"когда … отвечай \"",
" surface",
" text \"когда … отвечать \"",
" surface",
" text \"если … то \"",
" lexeme hi",
" surface",
" text \"जब … तब \"",
" surface",
" text \"जब … तो \"",
" lexeme zh",
" surface",
" text \"当 … 时 \"",
" surface",
" text \"当 … 则 \"",
" surface",
" text \"当 … 回答 \"",
" surface",
" text \"当 …时回答 \"",
" surface",
" text \"当 …则回答 \"",
" nondeterministic_step",
" defined-by property",
" defined-by concept",
" role nondeterministic_marker",
" lexeme en",
" surface",
" text random",
" surface",
" text nondeterministic",
" surface",
" text non-deterministic",
" surface",
" text \"arbitrary code\"",
" lexeme ru",
" surface",
" text случайный",
" surface",
" text недетерминированный",
" lexeme hi",
" surface",
" text यादृच्छिक",
" surface",
" text अनिश्चित",
" lexeme zh",
" surface",
" text 随机",
" surface",
" text 不确定",
" shell_capability_need",
" defined-by capability",
" defined-by action",
" role shell_capability_cue",
" lexeme en",
" surface",
" text local_shell",
" surface",
" text shell",
" surface",
" text filesystem",
" surface",
" text \"file system\"",
" surface",
" text \"list files\"",
" surface",
" text \"write file\"",
" surface",
" text \"delete file\"",
" lexeme ru",
" surface",
" text оболочка",
" surface",
" text \"файловая система\"",
" surface",
" text \"список файлов\"",
" lexeme hi",
" surface",
" text शेल",
" surface",
" text \"फाइल सिस्टम\"",
" surface",
" text \"फाइलें सूचीबद्ध\"",
" lexeme zh",
" surface",
" text 外壳",
" surface",
" text 文件系统",
" surface",
" text 列出文件",
" network_capability_need",
" defined-by capability",
" defined-by action",
" role network_capability_cue",
" lexeme en",
" surface",
" text http",
" surface",
" text network",
" surface",
" text fetch",
" surface",
" text \"web request\"",
" lexeme ru",
" surface",
" text сеть",
" surface",
" text веб-запрос",
" surface",
" text \"сетевой запрос\"",
" lexeme hi",
" surface",
" text नेटवर्क",
" surface",
" text \"वेब अनुरोध\"",
" lexeme zh",
" surface",
" text 网络",
" surface",
" text 网络请求",
" surface",
" text 获取",
"meanings",
" investment",
" grounded-in Q4290",
" defined-by money",
" defined-by action",
" role investment_cue",
" lexeme en",
" surface",
" text invest",
" surface",
" text investment",
" lexeme ru",
" surface",
" text инвестир",
" surface",
" text инвестиц",
" lexeme hi",
" surface",
" text निवेश",
" lexeme zh",
" surface",
" text 投资",
" interest_finance",
" grounded-in Q170924",
" defined-by money",
" role interest_cue",
" lexeme en",
" surface",
" text interest",
" lexeme ru",
" surface",
" text процент",
" lexeme hi",
" surface",
" text ब्याज",
" lexeme zh",
" surface",
" text 利息",
" compounding",
" grounded-in Q959606",
" defined-by action",
" defined-by money",
" role compounding_action_cue",
" lexeme en",
" surface",
" text compound",
" lexeme ru",
" surface",
" text \"сложный процент\"",
" surface",
" text капитализаци",
" lexeme hi",
" surface",
" text चक्रवृद्धि",
" lexeme zh",
" surface",
" text 复利",
" compounding_monthly",
" defined-by compounding",
" role compounding_frequency_cue",
" lexeme en",
" surface",
" text monthly",
" lexeme ru",
" surface",
" text ежемесячн",
" lexeme hi",
" surface",
" text मासिक",
" lexeme zh",
" surface",
" text 每月",
" compounding_quarterly",
" defined-by compounding",
" role compounding_frequency_cue",
" lexeme en",
" surface",
" text quarterly",
" lexeme ru",
" surface",
" text ежеквартальн",
" lexeme hi",
" surface",
" text त्रैमासिक",
" lexeme zh",
" surface",
" text 每季度",
" compounding_weekly",
" defined-by compounding",
" role compounding_frequency_cue",
" lexeme en",
" surface",
" text weekly",
" lexeme ru",
" surface",
" text еженедельн",
" lexeme hi",
" surface",
" text साप्ताहिक",
" lexeme zh",
" surface",
" text 每周",
" compounding_daily",
" defined-by compounding",
" role compounding_frequency_cue",
" lexeme en",
" surface",
" text daily",
" lexeme ru",
" surface",
" text ежедневн",
" lexeme hi",
" surface",
" text दैनिक",
" lexeme zh",
" surface",
" text 每日",
" compounding_annual",
" defined-by compounding",
" role compounding_frequency_cue",
" lexeme en",
" surface",
" text annually",
" surface",
" text yearly",
" lexeme ru",
" surface",
" text ежегодн",
" lexeme hi",
" surface",
" text वार्षिक",
" lexeme zh",
" surface",
" text 每年",
" live_rate_freshness",
" defined-by exchange_rate",
" defined-by concept",
" role live_rate_freshness_cue",
" lexeme en",
" surface",
" text web",
" surface",
" text \"current exchange\"",
" surface",
" text \"current rate\"",
" surface",
" text \"exchange rate\"",
" lexeme ru",
" surface",
" text \"текущий курс\"",
" surface",
" text \"актуальный курс\"",
" lexeme hi",
" surface",
" text \"वर्तमान दर\"",
" lexeme zh",
" surface",
" text 实时汇率",
" year_period",
" grounded-in Q577",
" defined-by concept",
" role year_unit_cue",
" lexeme en",
" surface",
" text year",
" lexeme ru",
" surface",
" text год",
" surface",
" text лет",
" lexeme hi",
" surface",
" text वर्ष",
" lexeme zh",
" surface",
" text 年",
" conversion_action",
" defined-by action",
" defined-by money",
" role conversion_action_cue",
" lexeme en",
" surface",
" text convert",
" lexeme ru",
" surface",
" text конвертир",
" lexeme hi",
" surface",
" text परिवर्तित",
" lexeme zh",
" surface",
" text 转换",
" final_amount",
" defined-by money",
" role final_amount_reference",
" lexeme en",
" surface",
" text \"final amount\"",
" lexeme ru",
" surface",
" text \"итоговая сумма\"",
" lexeme hi",
" surface",
" text \"अंतिम राशि\"",
" lexeme zh",
" surface",
" text 最终金额",
"meanings",
" definition_merge_action",
" defined-by action",
" role definition_merge_action",
" lexeme en",
" surface",
" text merge",
" surface",
" text merged",
" surface",
" text combine",
" surface",
" text combined",
" surface",
" text fuse",
" surface",
" text fusion",
" lexeme ru",
" surface",
" text объедин",
" surface",
" text слия",
" lexeme hi",
" surface",
" text विलय",
" surface",
" text संयोजन",
" lexeme zh",
" surface",
" text 合并",
" surface",
" text 融合",
" definition_artifact_request",
" defined-by inquiry",
" role definition_artifact_request",
" lexeme en",
" surface",
" text definition",
" surface",
" text definitions",
" surface",
" text translation",
" surface",
" text translations",
" surface",
" text translated",
" surface",
" text wikipedia",
" lexeme ru",
" surface",
" text определени",
" surface",
" text перевод",
" surface",
" text википеди",
" lexeme hi",
" surface",
" text परिभाषा",
" surface",
" text अनुवाद",
" lexeme zh",
" surface",
" text 定义",
" surface",
" text 翻译",
" definition_merge_marker",
" defined-by inquiry",
" role definition_merge_marker",
" lexeme en",
" surface",
" text \"translated definitions for …\"",
" surface",
" text \"translated definitions of …\"",
" surface",
" text \"wikipedia definitions for …\"",
" surface",
" text \"wikipedia definitions of …\"",
" surface",
" text \"definitions for …\"",
" surface",
" text \"definitions of …\"",
" surface",
" text \"definition for …\"",
" surface",
" text \"definition of …\"",
" surface",
" text \"translations for …\"",
" surface",
" text \"translations of …\"",
" surface",
" text \"translation for …\"",
" surface",
" text \"translation of …\"",
" lexeme ru",
" surface",
" text \"переведенные определения для …\"",
" surface",
" text \"определения для …\"",
" surface",
" text \"определение …\"",
" surface",
" text \"перевод …\"",
" lexeme hi",
" surface",
" text \"की परिभाषाएँ …\"",
" surface",
" text \"का अनुवाद …\"",
" lexeme zh",
" surface",
" text \"的定义 …\"",
" surface",
" text \"的翻译 …\"",
" definition_merge_tail_boundary",
" defined-by relation",
" role definition_merge_tail_boundary",
" lexeme en",
" surface",
" text from",
" surface",
" text using",
" surface",
" text with",
" surface",
" text by",
" surface",
" text into",
" surface",
" text across",
" lexeme ru",
" surface",
" text из",
" surface",
" text с",
" surface",
" text по",
" surface",
" text в",
" lexeme hi",
" surface",
" text से",
" surface",
" text \"के साथ\"",
" lexeme zh",
" surface",
" text 从",
" surface",
" text 使用",
"meanings",
" tool_invocation_cue",
" defined-by action",
" role tool_invocation_cue",
" lexeme en",
" surface",
" text call",
" surface",
" text invoke",
" surface",
" text run",
" surface",
" text api",
" surface",
" text tool",
" lexeme ru",
" surface",
" text вызови",
" surface",
" text вызвать",
" surface",
" text запусти",
" surface",
" text инструмент",
" lexeme hi",
" surface",
" text कॉल",
" surface",
" text चलाओ",
" surface",
" text उपकरण",
" lexeme zh",
" surface",
" text 调用",
" surface",
" text 运行",
" surface",
" text 工具",
" calculator_tool",
" defined-by entity",
" role calculator_tool_name",
" lexeme en",
" surface",
" text calculator",
" lexeme ru",
" surface",
" text калькулятор",
" lexeme hi",
" surface",
" text कैलकुलेटर",
" lexeme zh",
" surface",
" text 计算器",
" web_search_tool",
" defined-by entity",
" role web_search_tool_name",
" lexeme en",
" surface",
" text web_search",
" surface",
" text \"web search\"",
" surface",
" text web-search",
" lexeme ru",
" surface",
" text веб-поиск",
" surface",
" text \"поиск в интернете\"",
" lexeme hi",
" surface",
" text \"वेब खोज\"",
" surface",
" text \"इंटरनेट खोज\"",
" lexeme zh",
" surface",
" text 网络搜索",
" surface",
" text 网页搜索",
" local_shell_tool",
" defined-by entity",
" role local_shell_request_cue",
" lexeme en",
" surface",
" text local_shell",
" surface",
" text \"local shell tool\"",
" surface",
" text \"local shell api\"",
" surface",
" text \"call the shell tool\"",
" surface",
" text \"invoke the shell tool\"",
" lexeme ru",
" surface",
" text \"локальная оболочка\"",
" surface",
" text \"вызови оболочку\"",
" lexeme hi",
" surface",
" text \"लोकल शेल\"",
" surface",
" text \"शेल टूल चलाओ\"",
" lexeme zh",
" surface",
" text 本地外壳",
" surface",
" text 调用外壳工具",
" tool_argument_marker",
" defined-by relation",
" role tool_argument_marker",
" lexeme en",
" surface",
" text \"with query\"",
" surface",
" text query",
" surface",
" text with",
" surface",
" text for",
" lexeme ru",
" surface",
" text \"с запросом\"",
" surface",
" text для",
" lexeme hi",
" surface",
" text \"क्वेरी के साथ\"",
" surface",
" text \"के लिए\"",
" lexeme zh",
" surface",
" text 查询",
" surface",
" text 用于",
"meanings",
" feature_capability_web_search",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text \"web search\"",
" surface",
" text \"internet search\"",
" surface",
" text \"search engines\"",
" surface",
" text \"can you search the internet\"",
" surface",
" text \"can you search internet\"",
" surface",
" text \"can you search the web\"",
" surface",
" text \"can you search web\"",
" surface",
" text \"can you search online\"",
" surface",
" text \"do you have internet search\"",
" surface",
" text \"do you have web search\"",
" surface",
" text \"do you have internet access\"",
" surface",
" text \"are you connected to search engines\"",
" surface",
" text \"can you use search engines\"",
" surface",
" text \"can you browse the web\"",
" lexeme ru",
" surface",
" text веб-поиск",
" surface",
" text \"веб поиск\"",
" surface",
" text \"поиск в интернете\"",
" surface",
" text поисковик",
" surface",
" text \"поисковые системы\"",
" surface",
" text \"можешь искать в интернете\"",
" surface",
" text \"можешь искать интернет\"",
" surface",
" text \"умеешь искать в интернете\"",
" surface",
" text \"умеешь искать интернет\"",
" surface",
" text \"можешь искать онлайн\"",
" surface",
" text \"умеешь искать онлайн\"",
" surface",
" text \"у тебя есть веб-поиск\"",
" surface",
" text \"у тебя есть веб поиск\"",
" surface",
" text \"у тебя есть поиск в интернете\"",
" surface",
" text \"есть доступ к интернету\"",
" surface",
" text \"подключен к поисковикам\"",
" surface",
" text \"подключена к поисковикам\"",
" surface",
" text \"подключен к поисковым системам\"",
" surface",
" text \"можешь пользоваться интернетом\"",
" lexeme hi",
" surface",
" text \"web search\"",
" surface",
" text \"internet search\"",
" surface",
" text \"search engine\"",
" surface",
" text \"इंटरनेट पर खोज सकते\"",
" surface",
" text \"ऑनलाइन खोज सकते\"",
" surface",
" text \"इंटरनेट खोज है\"",
" surface",
" text \"वेब खोज है\"",
" surface",
" text \"सर्च इंजन से जुड़े\"",
" surface",
" text \"खोज इंजन से जुड़े\"",
" lexeme zh",
" surface",
" text \"web search\"",
" surface",
" text 搜索引擎",
" surface",
" text 上网搜索",
" surface",
" text 搜索互联网",
" surface",
" text 搜索网络",
" surface",
" text 联网搜索",
" surface",
" text 用搜索引擎",
" surface",
" text 使用搜索引擎",
" surface",
" text 网络搜索",
" feature_capability_diagnostics",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text diagnostics",
" surface",
" text diagnostic",
" surface",
" text trace",
" surface",
" text \"reasoning trace\"",
" surface",
" text \"show diagnostics\"",
" lexeme ru",
" surface",
" text диагностика",
" surface",
" text диагност",
" surface",
" text трассировка",
" surface",
" text trace",
" lexeme hi",
" surface",
" text diagnostics",
" surface",
" text निदान",
" surface",
" text trace",
" lexeme zh",
" surface",
" text 诊断",
" surface",
" text trace",
" surface",
" text 推理跟踪",
" feature_capability_agent_mode",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text \"agent mode\"",
" surface",
" text agent",
" surface",
" text multi-step",
" surface",
" text autonomous",
" lexeme ru",
" surface",
" text \"agent mode\"",
" surface",
" text агент",
" surface",
" text многошаг",
" surface",
" text автоном",
" lexeme hi",
" surface",
" text \"agent mode\"",
" surface",
" text एजेंट",
" surface",
" text multi-step",
" lexeme zh",
" surface",
" text \"agent mode\"",
" surface",
" text 代理",
" surface",
" text 多步骤",
" feature_capability_definition_fusion",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text \"definition fusion\"",
" surface",
" text \"merge definitions\"",
" surface",
" text \"automatic definition\"",
" lexeme ru",
" surface",
" text \"слияние определений\"",
" surface",
" text \"объединение определений\"",
" lexeme hi",
" surface",
" text \"definition fusion\"",
" surface",
" text \"merge definitions\"",
" surface",
" text \"परिभाषा विलय\"",
" lexeme zh",
" surface",
" text \"definition fusion\"",
" surface",
" text 合并定义",
" feature_capability_configuration",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text settings",
" surface",
" text configuration",
" surface",
" text \"configure settings\"",
" surface",
" text theme",
" surface",
" text language",
" surface",
" text \"chat style\"",
" surface",
" text configure",
" surface",
" text preferences",
" surface",
" text \"composer style\"",
" surface",
" text \"ui skin\"",
" lexeme ru",
" surface",
" text настройки",
" surface",
" text настройка",
" surface",
" text конфигурация",
" surface",
" text тема",
" surface",
" text язык",
" surface",
" text \"стиль чата\"",
" surface",
" text оформление",
" surface",
" text настрой",
" surface",
" text конфигурац",
" surface",
" text параметр",
" lexeme hi",
" surface",
" text settings",
" surface",
" text configuration",
" surface",
" text theme",
" surface",
" text language",
" surface",
" text सेटिंग",
" lexeme zh",
" surface",
" text 设置",
" surface",
" text 配置",
" surface",
" text 主题",
" surface",
" text 语言",
" surface",
" text 聊天样式",
" feature_capability_memory_actions",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text \"export memory\"",
" surface",
" text \"import memory\"",
" surface",
" text \"memory export\"",
" surface",
" text \"memory import\"",
" lexeme ru",
" surface",
" text \"экспорт памяти\"",
" surface",
" text \"импорт памяти\"",
" surface",
" text \"память экспорт\"",
" surface",
" text \"память импорт\"",
" lexeme hi",
" surface",
" text \"memory export\"",
" surface",
" text \"memory import\"",
" surface",
" text \"स्मृति निर्यात\"",
" surface",
" text \"स्मृति आयात\"",
" lexeme zh",
" surface",
" text 导出记忆",
" surface",
" text 导入记忆",
" surface",
" text \"memory export\"",
" surface",
" text \"memory import\"",
" feature_capability_greeting",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text greeting",
" surface",
" text greetings",
" surface",
" text \"say hello\"",
" surface",
" text \"respond to hello\"",
" lexeme ru",
" surface",
" text приветствие",
" surface",
" text приветствия",
" surface",
" text здороваться",
" surface",
" text привет",
" lexeme hi",
" surface",
" text अभिवादन",
" surface",
" text नमस्ते",
" surface",
" text hello",
" lexeme zh",
" surface",
" text 问候",
" surface",
" text 打招呼",
" surface",
" text 你好",
" feature_capability_write_program",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text \"hello world\"",
" surface",
" text \"write code\"",
" surface",
" text \"generate code\"",
" surface",
" text \"write program\"",
" surface",
" text program",
" lexeme ru",
" surface",
" text \"hello world\"",
" surface",
" text код",
" surface",
" text программу",
" surface",
" text программа",
" lexeme hi",
" surface",
" text \"hello world\"",
" surface",
" text code",
" surface",
" text program",
" surface",
" text प्रोग्राम",
" lexeme zh",
" surface",
" text \"hello world\"",
" surface",
" text 代码",
" surface",
" text 程序",
" feature_capability_concept_lookup",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text \"concept lookup\"",
" surface",
" text concept",
" surface",
" text \"wikipedia lookup\"",
" lexeme ru",
" surface",
" text \"поиск понятий\"",
" surface",
" text понятие",
" lexeme hi",
" surface",
" text concept",
" surface",
" text अवधारणा",
" lexeme zh",
" surface",
" text 概念",
" feature_capability_arithmetic",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text arithmetic",
" surface",
" text calculate",
" surface",
" text math",
" surface",
" text \"2 + 2\"",
" lexeme ru",
" surface",
" text арифмет",
" surface",
" text считать",
" surface",
" text посчитать",
" surface",
" text \"2 + 2\"",
" lexeme hi",
" surface",
" text अंकगणित",
" surface",
" text गणना",
" surface",
" text math",
" surface",
" text \"2 + 2\"",
" lexeme zh",
" surface",
" text 算术",
" surface",
" text 计算",
" surface",
" text 数学",
" surface",
" text \"2 + 2\"",
" feature_capability_translation",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text translation",
" surface",
" text translate",
" surface",
" text \"language translation\"",
" lexeme ru",
" surface",
" text перевод",
" surface",
" text переводить",
" surface",
" text перевести",
" lexeme hi",
" surface",
" text अनुवाद",
" surface",
" text translate",
" surface",
" text translation",
" lexeme zh",
" surface",
" text 翻译",
" surface",
" text translation",
" surface",
" text translate",
" feature_capability_memory",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text memory",
" surface",
" text remember",
" surface",
" text recall",
" surface",
" text \"conversation context\"",
" lexeme ru",
" surface",
" text память",
" surface",
" text помнить",
" surface",
" text запомнить",
" surface",
" text контекст",
" lexeme hi",
" surface",
" text स्मृति",
" surface",
" text याद",
" surface",
" text memory",
" surface",
" text context",
" lexeme zh",
" surface",
" text 记忆",
" surface",
" text 记住",
" surface",
" text 回忆",
" surface",
" text 上下文",
" feature_capability_demo_mode",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text \"demo mode\"",
" surface",
" text demo",
" surface",
" text \"scripted demo\"",
" lexeme ru",
" surface",
" text демо",
" surface",
" text демо-режим",
" surface",
" text \"сценарный демо\"",
" lexeme hi",
" surface",
" text demo",
" surface",
" text डेमो",
" lexeme zh",
" surface",
" text demo",
" surface",
" text 演示",
" feature_capability_http_url",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text \"http fetch\"",
" surface",
" text \"fetch url\"",
" surface",
" text \"open url\"",
" surface",
" text \"navigate to url\"",
" surface",
" text \"visit url\"",
" surface",
" text \"url navigation\"",
" lexeme ru",
" surface",
" text \"http запрос\"",
" surface",
" text \"открыть url\"",
" surface",
" text \"перейти на\"",
" surface",
" text \"сделать запрос\"",
" surface",
" text url-навигация",
" surface",
" text \"url навигация\"",
" lexeme hi",
" surface",
" text \"http fetch\"",
" surface",
" text url",
" surface",
" text navigate",
" surface",
" text लिंक",
" surface",
" text यूआरएल",
" lexeme zh",
" surface",
" text \"http fetch\"",
" surface",
" text url",
" surface",
" text 打开链接",
" surface",
" text 访问链接",
" feature_capability_javascript_execution",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text javascript",
" surface",
" text \"run javascript\"",
" surface",
" text \"execute javascript\"",
" surface",
" text js",
" lexeme ru",
" surface",
" text javascript",
" surface",
" text js",
" surface",
" text \"выполнить javascript\"",
" lexeme hi",
" surface",
" text javascript",
" surface",
" text js",
" surface",
" text स्क्रिप्ट",
" lexeme zh",
" surface",
" text javascript",
" surface",
" text js",
" surface",
" text 脚本执行",
" surface",
" text 执行脚本",
" feature_capability_planning",
" defined-by concept",
" role feature_capability_alias",
" lexeme en",
" surface",
" text summarize",
" surface",
" text brainstorm",
" surface",
" text roleplay",
" surface",
" text \"software project\"",
" surface",
" text \"project plan\"",
" lexeme ru",
" surface",
" text резюмировать",
" surface",
" text брейншторм",
" surface",
" text роль",
" surface",
" text проект",
" surface",
" text \"план проекта\"",
" lexeme hi",
" surface",
" text summary",
" surface",
" text brainstorm",
" surface",
" text roleplay",
" surface",
" text \"project plan\"",
" surface",
" text \"परियोजना योजना\"",
" lexeme zh",
" surface",
" text 总结",
" surface",
" text 头脑风暴",
" surface",
" text 角色扮演",
" surface",
" text 项目计划",
" feature_capability_question",
" defined-by action",
" role feature_capability_question",
" lexeme en",
" surface",
" text \"can you\"",
" surface",
" text \"can formal-ai\"",
" surface",
" text \"are you able\"",
" surface",
" text \"are you connected\"",
" surface",
" text \"do you support\"",
" surface",
" text \"do you have\"",
" surface",
" text \"can i\"",
" lexeme ru",
" surface",
" text можешь",
" surface",
" text умеешь",
" surface",
" text поддерживаешь",
" surface",
" text \"у тебя есть\"",
" surface",
" text \"есть ли\"",
" surface",
" text доступен",
" surface",
" text доступна",
" surface",
" text включен",
" surface",
" text включена",
" surface",
" text подключен",
" surface",
" text подключена",
" surface",
" text \"можно ли\"",
" lexeme hi",
" surface",
" text क्या",
" surface",
" text सकते",
" surface",
" text सकती",
" surface",
" text समर्थन",
" surface",
" text उपलब्ध",
" lexeme zh",
" surface",
" text 能",
" surface",
" text 可以",
" surface",
" text 支持",
" surface",
" text 你有",
" surface",
" text 您有",
" surface",
" text 有没有",
" surface",
" text 是否有",
" surface",
" text 启用",
" surface",
" text 可用",
" feature_action_arithmetic",
" defined-by action",
" role feature_action_arithmetic",
" lexeme en",
" surface",
" text \"can you calculate\"",
" surface",
" text \"can you compute\"",
" lexeme ru",
" surface",
" text \"можешь вычислить\"",
" surface",
" text \"можешь посчитать\"",
" lexeme hi",
" surface",
" text \"क्या आप गणना कर सकते\"",
" lexeme zh",
" surface",
" text 你能计算",
" feature_action_planning",
" defined-by action",
" role feature_action_planning",
" lexeme en",
" surface",
" text \"can you summarize\"",
" surface",
" text \"can you brainstorm\"",
" surface",
" text \"can you roleplay\"",
" lexeme ru",
" surface",
" text \"можешь резюмировать\"",
" surface",
" text \"можешь провести мозговой штурм\"",
" surface",
" text \"можешь сыграть роль\"",
" lexeme hi",
" surface",
" text \"क्या आप सारांश कर सकते\"",
" lexeme zh",
" surface",
" text 你能总结",
"meanings",
" playwright",
" defined-by entity",
" role playwright_tool_name",
" lexeme en",
" surface",
" text playwright",
" surface",
" text playright",
" action playwright",
" lexeme ru",
" surface",
" text плейврайт",
" surface",
" text плейрайт",
" lexeme hi",
" surface",
" text प्लेराइट",
" lexeme zh",
" surface",
" text 普莱赖特",
" playwright_script_request_cue",
" defined-by concept",
" role playwright_script_cue",
" lexeme en",
" surface",
" text script",
" surface",
" text test",
" surface",
" text spec",
" surface",
" text code",
" surface",
" text write",
" surface",
" text create",
" surface",
" text generate",
" surface",
" text make",
" surface",
" text build",
" surface",
" text \"can you\"",
" surface",
" text \"could you\"",
" lexeme ru",
" surface",
" text скрипт",
" surface",
" text сценар",
" surface",
" text тест",
" surface",
" text код",
" surface",
" text напиши",
" surface",
" text написать",
" surface",
" text можешь",
" surface",
" text сделай",
" surface",
" text создай",
" lexeme hi",
" surface",
" text स्क्रिप्ट",
" surface",
" text टेस्ट",
" surface",
" text कोड",
" surface",
" text लिखो",
" surface",
" text बनाओ",
" lexeme zh",
" surface",
" text 脚本",
" surface",
" text 测试",
" surface",
" text 代码",
" surface",
" text 写",
" surface",
" text 生成",
"meanings",
" compare",
" defined-by action",
" role comparison_table_trigger",
" lexeme en",
" surface",
" text \"comparison table\"",
" surface",
" text compare",
" surface",
" text comparing",
" lexeme ru",
" surface",
" text \"сравнительная таблица\"",
" surface",
" text сравнить",
" surface",
" text сравнение",
" lexeme hi",
" surface",
" text \"तुलना तालिका\"",
" surface",
" text तुलना",
" lexeme zh",
" surface",
" text 比较表",
" surface",
" text 比较",
" surface",
" text 对比",
" table",
" defined-by concept",
" role comparison_table_noun",
" lexeme en",
" surface",
" text table",
" lexeme ru",
" surface",
" text таблица",
" lexeme hi",
" surface",
" text तालिका",
" lexeme zh",
" surface",
" text 表格",
" differences",
" defined-by relation",
" role comparison_difference_cue",
" lexeme en",
" surface",
" text differences",
" lexeme ru",
" surface",
" text различия",
" surface",
" text отличия",
" lexeme hi",
" surface",
" text अंतर",
" lexeme zh",
" surface",
" text 差异",
" surface",
" text 区别",
" research_prompt_signal",
" defined-by concept",
" role research_prompt_signal",
" lexeme en",
" surface",
" text \"search …\"",
" surface",
" text \"find information …\"",
" surface",
" text \"look up information …\"",
" surface",
" text \"search for information\"",
" surface",
" text \"web search\"",
" surface",
" text research",
" lexeme ru",
" surface",
" text поиск",
" surface",
" text исследование",
" surface",
" text веб-поиск",
" lexeme hi",
" surface",
" text खोज",
" surface",
" text अनुसंधान",
" lexeme zh",
" surface",
" text 搜索",
" surface",
" text 研究",
" key_differences",
" defined-by relation",
" role research_criterion",
" lexeme en",
" surface",
" text \"key difference\"",
" surface",
" text difference",
" lexeme ru",
" surface",
" text \"ключевые различия\"",
" surface",
" text различия",
" lexeme hi",
" surface",
" text \"मुख्य अंतर\"",
" surface",
" text अंतर",
" lexeme zh",
" surface",
" text 主要区别",
" surface",
" text 区别",
" use_cases",
" defined-by concept",
" role research_criterion",
" lexeme en",
" surface",
" text \"use case\"",
" surface",
" text application",
" lexeme ru",
" surface",
" text \"сценарии использования\"",
" surface",
" text применение",
" lexeme hi",
" surface",
" text \"उपयोग के मामले\"",
" surface",
" text अनुप्रयोग",
" lexeme zh",
" surface",
" text 用例",
" surface",
" text 应用",
" advantages",
" defined-by property",
" role research_criterion",
" lexeme en",
" surface",
" text advantage",
" surface",
" text \"pro \"",
" lexeme ru",
" surface",
" text преимущества",
" surface",
" text плюсы",
" lexeme hi",
" surface",
" text फायदे",
" surface",
" text लाभ",
" lexeme zh",
" surface",
" text 优点",
" surface",
" text 优势",
" disadvantages",
" defined-by property",
" role research_criterion",
" lexeme en",
" surface",
" text disadvantage",
" surface",
" text \" con \"",
" lexeme ru",
" surface",
" text недостатки",
" surface",
" text минусы",
" lexeme hi",
" surface",
" text नुकसान",
" surface",
" text हानि",
" lexeme zh",
" surface",
" text 缺点",
" surface",
" text 劣势",
"meanings",
" conversation_topic_opener",
" defined-by inquiry",
" defined-by action",
" role conversation_topic_opener",
" lexeme en",
" surface",
" text \"let's talk about …\"",
" surface",
" text \"lets talk about …\"",
" surface",
" text \"can we talk about …\"",
" surface",
" text \"talk about …\"",
" lexeme ru",
" surface",
" text \"давай поговорим о …\"",
" surface",
" text \"давай поговорим об …\"",
" surface",
" text \"давайте поговорим о …\"",
" surface",
" text \"давайте поговорим об …\"",
" surface",
" text \"поговорим о …\"",
" action scan",
" surface",
" text \"поговорим об …\"",
" surface",
" text \"обсудим …\"",
" lexeme hi",
" surface",
" text \"चलो बात करें …\"",
" surface",
" text \"बात करें …\"",
" lexeme zh",
" surface",
" text 聊聊…",
" surface",
" text 谈谈…",
"meanings",
" summary_statement_kind",
" defined-by concept",
" role summary_statement_kind",
" lexeme en",
" surface",
" text \"statement kind\"",
" surface",
" text \"sentence kind\"",
" lexeme ru",
" surface",
" text \"вид утверждения\"",
" surface",
" text \"тип предложения\"",
" lexeme hi",
" surface",
" text \"कथन का प्रकार\"",
" surface",
" text \"वाक्य का प्रकार\"",
" lexeme zh",
" surface",
" text 陈述类型",
" surface",
" text 句子类型",
" summary_kind_install",
" defined-by summary_statement_kind",
" role summary_classification_cue",
" lexeme en",
" surface",
" text \"to install\"",
" surface",
" text \"install with\"",
" surface",
" text installation",
" surface",
" text \"npm install\"",
" surface",
" text \"cargo install\"",
" surface",
" text \"pip install\"",
" lexeme ru",
" surface",
" text установить",
" surface",
" text установка",
" surface",
" text установите",
" lexeme hi",
" surface",
" text \"इंस्टॉल करें\"",
" surface",
" text स्थापना",
" lexeme zh",
" surface",
" text 安装",
" surface",
" text 安装方法",
" summary_kind_example",
" defined-by summary_statement_kind",
" role summary_classification_cue",
" lexeme en",
" surface",
" text \"for example\"",
" surface",
" text \"example:\"",
" surface",
" text e.g.",
" surface",
" text \"run --\"",
" surface",
" text \"$ \"",
" surface",
" text \"```\"",
" lexeme ru",
" surface",
" text выполни",
" surface",
" text например",
" lexeme hi",
" surface",
" text \"उदाहरण के लिए\"",
" surface",
" text \"उदाहरण:\"",
" lexeme zh",
" surface",
" text 例如",
" surface",
" text 比如",
" summary_kind_language",
" defined-by summary_statement_kind",
" role summary_classification_cue",
" lexeme en",
" surface",
" text \"written in \"",
" surface",
" text \"language:\"",
" surface",
" text \"is a \"",
" surface",
" text \"build with \"",
" lexeme ru",
" surface",
" text \"написан на \"",
" surface",
" text \"на языке \"",
" lexeme hi",
" surface",
" text \"में लिखा\"",
" surface",
" text \"भाषा:\"",
" lexeme zh",
" surface",
" text 语言",
" surface",
" text 编写",
" summary_kind_stars",
" defined-by summary_statement_kind",
" role summary_classification_cue",
" lexeme en",
" surface",
" text \" stars\"",
" surface",
" text \"github stars\"",
" surface",
" text ★",
" surface",
" text stargazers",
" lexeme ru",
" surface",
" text звёзды",
" surface",
" text звезд",
" lexeme hi",
" surface",
" text सितारे",
" surface",
" text तारे",
" lexeme zh",
" surface",
" text 星标",
" surface",
" text 星数",
" summary_kind_purpose",
" defined-by summary_statement_kind",
" role summary_classification_cue",
" lexeme en",
" surface",
" text \"is for \"",
" surface",
" text \"is the ai\"",
" surface",
" text \"is an ai\"",
" surface",
" text \"is used to\"",
" surface",
" text \"is meant to\"",
" surface",
" text \"is designed to\"",
" surface",
" text \"helps you\"",
" surface",
" text \"lets you\"",
" lexeme ru",
" surface",
" text \"это ии\"",
" surface",
" text предназнач",
" lexeme hi",
" surface",
" text \"के लिए है\"",
" surface",
" text \"मदद करता है\"",
" lexeme zh",
" surface",
" text 用于",
" surface",
" text 旨在",
" surface",
" text 帮助你",
" summary_kind_use_case",
" defined-by summary_statement_kind",
" role summary_classification_cue",
" lexeme en",
" surface",
" text \"use it when\"",
" surface",
" text \"use this when\"",
" surface",
" text \"when you need\"",
" surface",
" text \"ideal for\"",
" surface",
" text \"useful when\"",
" lexeme ru",
" surface",
" text \"используйте когда\"",
" surface",
" text \"когда нужно\"",
" surface",
" text \"идеально для\"",
" lexeme hi",
" surface",
" text \"जब आपको चाहिए\"",
" surface",
" text \"के लिए आदर्श\"",
" lexeme zh",
" surface",
" text 当你需要",
" surface",
" text 适用于",
" summary_kind_feature",
" defined-by summary_statement_kind",
" role summary_classification_cue",
" lexeme en",
" surface",
" text \" supports \"",
" surface",
" text \" provides \"",
" surface",
" text \" offers \"",
" surface",
" text \" exposes \"",
" surface",
" text \" ships \"",
" surface",
" text \" orchestrates \"",
" lexeme ru",
" surface",
" text \" предоставляет \"",
" surface",
" text \" поддерживает \"",
" lexeme hi",
" surface",
" text \"समर्थन करता है\"",
" surface",
" text \"प्रदान करता है\"",
" lexeme zh",
" surface",
" text 支持",
" surface",
" text 提供",
"meanings",
" program_language",
" grounded-in Q9143",
" defined-by software_language",
" role program_language",
" lexeme en",
" surface",
" text \"programming language\"",
" lexeme ru",
" surface",
" text \"язык программирования\"",
" lexeme hi",
" surface",
" text \"प्रोग्रामिंग भाषा\"",
" lexeme zh",
" surface",
" text 编程语言",
" program_language_rust",
" grounded-in Q575650",
" defined-by program_language",
" defined-by language_rust",
" role program_language_alias",
" lexeme en",
" surface",
" text rust",
" surface",
" text rs",
" lexeme ru",
" surface",
" text раст",
" surface",
" text расте",
" lexeme hi",
" surface",
" text रस्ट",
" lexeme zh",
" surface",
" text rust",
" program_language_python",
" grounded-in Q28865",
" defined-by program_language",
" defined-by language_python",
" role program_language_alias",
" lexeme en",
" surface",
" text python",
" surface",
" text py",
" lexeme ru",
" surface",
" text питон",
" surface",
" text питоне",
" lexeme hi",
" surface",
" text पायथन",
" lexeme zh",
" surface",
" text python",
" program_language_javascript",
" grounded-in Q2005",
" defined-by program_language",
" defined-by language_javascript",
" role program_language_alias",
" lexeme en",
" surface",
" text javascript",
" surface",
" text js",
" surface",
" text node",
" lexeme ru",
" surface",
" text джаваскрипт",
" lexeme hi",
" surface",
" text जावास्क्रिप्ट",
" lexeme zh",
" surface",
" text javascript",
" program_language_typescript",
" grounded-in Q978185",
" defined-by program_language",
" role program_language_alias",
" lexeme en",
" surface",
" text typescript",
" surface",
" text ts",
" lexeme ru",
" surface",
" text тайпскрипт",
" lexeme hi",
" surface",
" text टाइपस्क्रिप्ट",
" lexeme zh",
" surface",
" text typescript",
" program_language_go",
" grounded-in Q37227",
" defined-by program_language",
" role program_language_alias",
" lexeme en",
" surface",
" text go",
" surface",
" text golang",
" lexeme ru",
" surface",
" text го",
" lexeme hi",
" surface",
" text गो",
" lexeme zh",
" surface",
" text go",
" program_language_c",
" grounded-in Q15777",
" defined-by program_language",
" role program_language_alias",
" lexeme en",
" surface",
" text c",
" lexeme ru",
" surface",
" text си",
" lexeme hi",
" surface",
" text सी",
" lexeme zh",
" surface",
" text c",
" program_language_cpp",
" grounded-in Q2407",
" defined-by program_language",
" role program_language_alias",
" lexeme en",
" surface",
" text cpp",
" surface",
" text c++",
" surface",
" text cplusplus",
" lexeme ru",
" surface",
" text сиплюсплюс",
" lexeme hi",
" surface",
" text सीपीपी",
" lexeme zh",
" surface",
" text c++",
" program_language_java",
" grounded-in Q251",
" defined-by program_language",
" role program_language_alias",
" lexeme en",
" surface",
" text java",
" lexeme ru",
" surface",
" text джава",
" lexeme hi",
" surface",
" text जावा",
" lexeme zh",
" surface",
" text java",
" program_language_csharp",
" grounded-in Q2370",
" defined-by program_language",
" role program_language_alias",
" lexeme en",
" surface",
" text csharp",
" surface",
" text \"c#\"",
" surface",
" text cs",
" surface",
" text dotnet",
" lexeme ru",
" surface",
" text сишарп",
" lexeme hi",
" surface",
" text सीशार्प",
" lexeme zh",
" surface",
" text \"c#\"",
" program_language_ruby",
" grounded-in Q161053",
" defined-by program_language",
" role program_language_alias",
" lexeme en",
" surface",
" text ruby",
" surface",
" text rb",
" lexeme ru",
" surface",
" text руби",
" lexeme hi",
" surface",
" text रूबी",
" lexeme zh",
" surface",
" text ruby",
" program_task",
" defined-by program",
" role program_task",
" lexeme en",
" surface",
" text \"coding task\"",
" lexeme ru",
" surface",
" text \"задача программирования\"",
" lexeme hi",
" surface",
" text \"प्रोग्रामिंग कार्य\"",
" lexeme zh",
" surface",
" text 编程任务",
" program_task_hello_world",
" defined-by program_task",
" defined-by hello_world",
" role program_task_alias",
" lexeme en",
" surface",
" text \"hello world\"",
" lexeme ru",
" surface",
" text \"хелло ворлд\"",
" lexeme hi",
" surface",
" text \"हैलो वर्ल्ड\"",
" lexeme zh",
" surface",
" text 你好世界",
" program_task_count_to_three",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"count to three\"",
" surface",
" text \"count to 3\"",
" surface",
" text \"counts to three\"",
" surface",
" text \"counts to 3\"",
" lexeme ru",
" surface",
" text \"посчитай до трёх\"",
" surface",
" text \"посчитай до 3\"",
" lexeme hi",
" surface",
" text \"तीन तक गिनें\"",
" surface",
" text \"3 तक गिनें\"",
" lexeme zh",
" surface",
" text 数到三",
" surface",
" text 数到3",
" program_task_list_files",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"list files in the current directory\"",
" surface",
" text \"list files in current directory\"",
" surface",
" text \"list files in the directory\"",
" surface",
" text \"list the files in the current directory\"",
" surface",
" text \"lists files in the current directory\"",
" surface",
" text \"lists the files in the current directory\"",
" surface",
" text \"list files in a directory\"",
" surface",
" text \"list directory files\"",
" surface",
" text \"list files\"",
" surface",
" text \"lists files\"",
" surface",
" text \"files in the current directory\"",
" surface",
" text \"files in current directory\"",
" lexeme ru",
" surface",
" text \"список файлов в текущей директории\"",
" surface",
" text \"список файлов в текущем каталоге\"",
" surface",
" text \"список файлов в директории\"",
" surface",
" text \"список файлов в каталоге\"",
" surface",
" text \"выдаёт список файлов\"",
" surface",
" text \"выдает список файлов\"",
" surface",
" text \"выводит список файлов\"",
" surface",
" text \"вывести список файлов\"",
" surface",
" text \"вывод списка файлов\"",
" surface",
" text \"список файлов\"",
" surface",
" text \"файлы в текущей директории\"",
" surface",
" text \"файлы в текущем каталоге\"",
" lexeme hi",
" surface",
" text \"फ़ाइलों की सूची\"",
" surface",
" text \"फाइलों की सूची\"",
" surface",
" text \"वर्तमान निर्देशिका की फ़ाइलें\"",
" surface",
" text \"वर्तमान निर्देशिका की फाइलें\"",
" surface",
" text \"निर्देशिका की फ़ाइलें\"",
" lexeme zh",
" surface",
" text 列出当前目录中的文件",
" surface",
" text 列出当前目录中文件",
" surface",
" text 列出当前目录的文件",
" surface",
" text 列出当前目录文件",
" surface",
" text 列出目录中的文件",
" surface",
" text 列出文件",
" program_task_list_files_arg",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"list files in the directory given as a path argument\"",
" surface",
" text \"list files in a directory given as an argument\"",
" surface",
" text \"list files in the directory passed as an argument\"",
" surface",
" text \"list files in a path argument\"",
" surface",
" text \"list files with a path argument\"",
" surface",
" text \"list files accepting a path argument\"",
" lexeme ru",
" surface",
" text \"список файлов в каталоге переданном как аргумент\"",
" surface",
" text \"список файлов в директории переданной как аргумент\"",
" surface",
" text \"список файлов по пути из аргумента\"",
" lexeme hi",
" surface",
" text \"पथ तर्क के रूप में दी गई निर्देशिका की फ़ाइलों की सूची\"",
" lexeme zh",
" surface",
" text 列出作为路径参数给出的目录中的文件",
" surface",
" text 列出路径参数指定目录中的文件",
" program_task_list_files_reverse_sort",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"list files in reverse order\"",
" surface",
" text \"list files sorted in reverse order\"",
" surface",
" text \"list files in descending order\"",
" surface",
" text \"reverse sorted list files\"",
" lexeme ru",
" surface",
" text \"список файлов в обратном порядке\"",
" surface",
" text \"список файлов с обратной сортировкой\"",
" lexeme hi",
" surface",
" text \"फ़ाइलों की सूची उल्टे क्रम में\"",
" surface",
" text \"फ़ाइलों को उल्टे क्रम में सूचीबद्ध करें\"",
" lexeme zh",
" surface",
" text 按相反顺序列出文件",
" surface",
" text 倒序列出文件",
" program_task_list_files_arg_reverse_sort",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"list files from a path argument in reverse order\"",
" surface",
" text \"list files with a path argument in reverse order\"",
" surface",
" text \"list files with a path argument sorted descending\"",
" surface",
" text \"reverse sorted list files with a path argument\"",
" lexeme ru",
" surface",
" text \"список файлов по пути из аргумента в обратном порядке\"",
" surface",
" text \"список файлов из аргумента с обратной сортировкой\"",
" lexeme hi",
" surface",
" text \"पथ तर्क से फ़ाइलों की सूची उल्टे क्रम में\"",
" surface",
" text \"पथ तर्क वाली फ़ाइलों को उल्टे क्रम में सूचीबद्ध करें\"",
" lexeme zh",
" surface",
" text 按相反顺序列出路径参数中的文件",
" surface",
" text 倒序列出路径参数指定目录中的文件",
" program_task_fizzbuzz",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text fizzbuzz",
" surface",
" text \"fizz buzz\"",
" lexeme ru",
" surface",
" text физзбазз",
" surface",
" text \"физз базз\"",
" surface",
" text физбаз",
" lexeme hi",
" surface",
" text फ़िज़बज़",
" surface",
" text फिज़बज़",
" lexeme zh",
" surface",
" text 菲茨巴兹",
" program_task_factorial",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"factorial of 5\"",
" surface",
" text \"factorial of five\"",
" surface",
" text \"5 factorial\"",
" surface",
" text \"five factorial\"",
" lexeme ru",
" surface",
" text \"факториал 5\"",
" surface",
" text \"факториал пяти\"",
" surface",
" text \"факториал числа 5\"",
" lexeme hi",
" surface",
" text \"5 का फैक्टोरियल\"",
" surface",
" text \"पाँच का फैक्टोरियल\"",
" lexeme zh",
" surface",
" text 5的阶乘",
" surface",
" text 五的阶乘",
" program_task_reverse_string",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"reverse a string\"",
" surface",
" text \"reverse the string hello\"",
" surface",
" text \"reverse hello\"",
" surface",
" text \"reverse string hello\"",
" surface",
" text \"reverse the word hello\"",
" lexeme ru",
" surface",
" text \"перевернуть строку\"",
" surface",
" text \"перевернуть строку hello\"",
" surface",
" text \"развернуть строку\"",
" lexeme hi",
" surface",
" text \"स्ट्रिंग को उलटें\"",
" surface",
" text \"स्ट्रिंग पलटें\"",
" lexeme zh",
" surface",
" text 反转字符串",
" surface",
" text 翻转字符串",
" surface",
" text 反转hello",
" program_task_sum_to_ten",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"sum of 1 to 10\"",
" surface",
" text \"sum from 1 to 10\"",
" surface",
" text \"sum the numbers 1 to 10\"",
" surface",
" text \"sum of numbers from 1 to 10\"",
" surface",
" text \"sum 1 to 10\"",
" surface",
" text \"sum to ten\"",
" lexeme ru",
" surface",
" text \"сумма от 1 до 10\"",
" surface",
" text \"сумма чисел от 1 до 10\"",
" surface",
" text \"сумма чисел от одного до десяти\"",
" lexeme hi",
" surface",
" text \"1 से 10 तक का योग\"",
" surface",
" text \"1 से 10 तक योग\"",
" lexeme zh",
" surface",
" text 1到10的和",
" surface",
" text 1到10求和",
" surface",
" text 求1到10的和",
" program_task_fibonacci",
" defined-by program_task",
" role program_task_alias",
" lexeme en",
" surface",
" text \"fibonacci sequence recursively\"",
" surface",
" text \"fibonacci sequence\"",
" surface",
" text \"recursive fibonacci\"",
" surface",
" text \"fibonacci recursively\"",
" surface",
" text \"fibonacci function\"",
" surface",
" text \"fibonacci numbers\"",
" surface",
" text \"fibonacci number\"",
" surface",
" text \"10th fibonacci number\"",
" surface",
" text \"the 10th fibonacci number\"",
" surface",
" text \"tenth fibonacci number\"",
" surface",
" text \"nth fibonacci number\"",
" lexeme ru",
" surface",
" text \"последовательность фибоначчи\"",
" surface",
" text \"числа фибоначчи\"",
" surface",
" text \"число фибоначчи\"",
" surface",
" text \"рекурсивный фибоначчи\"",
" surface",
" text \"фибоначчи рекурсивно\"",
" surface",
" text \"10-е число фибоначчи\"",
" surface",
" text \"десятое число фибоначчи\"",
" lexeme hi",
" surface",
" text \"फ़िबोनाची अनुक्रम\"",
" surface",
" text \"फिबोनाची अनुक्रम\"",
" surface",
" text \"फ़िबोनाची संख्या\"",
" surface",
" text \"फिबोनाची संख्या\"",
" lexeme zh",
" surface",
" text 斐波那契数列",
" surface",
" text 斐波那契序列",
" surface",
" text 斐波那契数",
" surface",
" text 递归斐波那契",
].join("\n");
// Semantic role: a thing a program produces that a later turn can refer back to
// (a result, an output, the program/script/code itself, an ordering).
const ROLE_PROGRAM_ARTIFACT = "program_artifact";
// Semantic role: an operation a follow-up turn can request against the active
// program (sort, reverse, cancel, change, …) — additive or subtractive.
const ROLE_PROGRAM_MODIFICATION = "program_modification";
// Semantic role: a kind of program artifact a user can ask to be authored
// (a program, a script, code, a function) — the noun side of "write a <kind>".
const ROLE_PROGRAM_KIND = "program_kind";
// Semantic role: a verb that requests a program artifact be produced (write,
// create, show, generate, make, build) — the verb side of "write a <kind>".
const ROLE_PROGRAM_REQUEST = "program_request";
// Issue #386 program-synthesis roles — mirror the ROLE_PROGRAM_SYNTHESIS_*
// consts in src/seed/meanings.rs. Their surface words live in
// data/seed/meanings-program-synthesis.lino (embedded in MEANINGS_LINO above).
// The subject/domain/action triple gates a synthesis request; signals
// distinguish one task from another; a task's slug is the Python function name.
const ROLE_PROGRAM_SYNTHESIS_SUBJECT = "program_synthesis_subject";
const ROLE_PROGRAM_SYNTHESIS_DOMAIN = "program_synthesis_domain";
const ROLE_PROGRAM_SYNTHESIS_ACTION = "program_synthesis_action";
const ROLE_PROGRAM_SYNTHESIS_SIGNAL = "program_synthesis_signal";
const ROLE_PROGRAM_SYNTHESIS_TASK = "program_synthesis_task";
// Issue #386 conversational-intent roles — mirror the ROLE_CLARIFICATION_REQUEST
// / ROLE_CAPABILITY_QUERY* / ROLE_SELF_FACT_QUERY / ROLE_SELF_INTRODUCTION_REQUEST
// consts in src/seed/meanings.rs. Their surface words live in
// data/seed/meanings-intent.lino (embedded in MEANINGS_LINO above); the
// recognizers below ask the lexicon by meaning instead of hardcoding phrases.
const ROLE_CLARIFICATION_REQUEST = "clarification_request";
const ROLE_CAPABILITY_QUERY = "capability_query";
const ROLE_CAPABILITY_QUERY_MORE = "capability_query_more";
const ROLE_SELF_FACT_QUERY = "self_fact_query";
const ROLE_SELF_INTRODUCTION_REQUEST = "self_introduction_request";
// Issue #386 known-facts inventory roles — mirror the ROLE_KNOWLEDGE_INVENTORY_*
// / ROLE_KNOWLEDGE_POSSESSION consts in src/seed/roles.rs. Their surface words
// live in data/seed/meanings-intent.lino (the shared `fact` noun plus the
// knowledge_inventory_probe / assistant_knowing / knowledge_inventory_query
// meanings, embedded in MEANINGS_LINO above); isKnownFactQuery composes these
// roles instead of hardcoding per-language phrase arrays.
const ROLE_KNOWLEDGE_INVENTORY_NOUN = "knowledge_inventory_noun";
const ROLE_KNOWLEDGE_INVENTORY_INTERROGATIVE = "knowledge_inventory_interrogative";
const ROLE_KNOWLEDGE_POSSESSION = "knowledge_possession";
const ROLE_KNOWLEDGE_INVENTORY_PHRASE = "knowledge_inventory_phrase";
// Issue #386 conversation-summary roles — mirror the
// ROLE_CONVERSATION_SUMMARY_DIRECTIVE / ROLE_CONVERSATION_REFERENCE /
// ROLE_CONVERSATION_SUMMARY_PHRASE / ROLE_CONVERSATION_SUMMARY_COURTESY consts
// in src/seed/roles.rs. Their per-language surface words live once in the
// embedded MEANINGS_LINO above (data/seed/meanings-intent.lino); the
// isSummarizePrompt recogniser composes these roles instead of hardcoding
// per-language phrase / regex arrays.
const ROLE_CONVERSATION_SUMMARY_DIRECTIVE = "conversation_summary_directive";
const ROLE_CONVERSATION_REFERENCE = "conversation_reference";
const ROLE_CONVERSATION_SUMMARY_PHRASE = "conversation_summary_phrase";
const ROLE_CONVERSATION_SUMMARY_COURTESY = "conversation_summary_courtesy";
// Issue #386 conversation-opener role — mirrors ROLE_CONVERSATION_TOPIC_OPENER
// in src/seed/roles.rs. Its slot-marked surface words live in
// data/seed/meanings-conversation.lino (embedded in MEANINGS_LINO above);
// conversationTopic asks the lexicon for these forms by meaning instead of
// hardcoding a per-language opener array.
const ROLE_CONVERSATION_TOPIC_OPENER = "conversation_topic_opener";
// Issue #386 how-cluster roles — mirror the ROLE_MECHANISM_INQUIRY /
// ROLE_PROCEDURAL_REQUEST consts in src/seed/meanings.rs. Their slot-marked
// surface words live in data/seed/meanings-how.lino (embedded in MEANINGS_LINO
// above); extractHowItWorksSubject / extractProceduralHowToTask ask the lexicon
// for these forms by meaning instead of hardcoding per-language phrase arrays.
const ROLE_MECHANISM_INQUIRY = "mechanism_inquiry";
const ROLE_PROCEDURAL_REQUEST = "procedural_request";
// Issue #386 procedural-cluster cleanup roles — mirror the
// ROLE_PROCEDURAL_TASK_MODIFIER / ROLE_COMMON_TYPO consts in src/seed/roles.rs.
// cleanProceduralFragment / correctCommonProceduralTypos ask the lexicon for
// these forms by meaning instead of hardcoding per-language modifier and typo
// arrays.
const ROLE_PROCEDURAL_TASK_MODIFIER = "procedural_task_modifier";
const ROLE_COMMON_TYPO = "common_typo";
// Issue #386 mechanism-subject cleanup roles — mirror the
// ROLE_MECHANISM_PREDICATE / ROLE_DETAIL_MODIFIER / ROLE_NON_REFERENTIAL_SUBJECT
// consts in src/seed/roles.rs. stripMechanismTail / cleanMechanismSubject ask
// the lexicon for these forms by meaning instead of hardcoding per-language
// predicate, modifier, and pronoun arrays.
const ROLE_MECHANISM_PREDICATE = "mechanism_predicate";
const ROLE_DETAIL_MODIFIER = "detail_modifier";
const ROLE_NON_REFERENTIAL_SUBJECT = "non_referential_subject";
// Slot marker (U+2026 …) carried inside a surface word's text to mark the open
// subject/task position. Mirrors the `split_once('…')` slot derivation on
// WordForm in src/seed/meanings.rs (issue #386).
const SLOT_MARKER = "…";
// Build a surface form { text, action, description, slot, before, after } from a
// raw surface word, its optional canonical action, and its self-describing note.
// The slot classification and the literal text on either side are derived from
// the position of the … marker, exactly as WordForm::slot/before_slot/after_slot
// do in src/seed/meanings.rs: no marker = "bare"; trailing marker = "prefix";
// leading marker = "suffix"; a marker with text on both sides = "circumfix".
function makeWordForm(text, action, description) {
const idx = text.indexOf(SLOT_MARKER);
if (idx === -1) {
return {
text,
action: action || "",
description: description || "",
slot: "bare",
before: text,
after: "",
};
}
const before = text.slice(0, idx);
const after = text.slice(idx + SLOT_MARKER.length);
let slot = "bare";
if (before && after) slot = "circumfix";
else if (before) slot = "prefix";
else if (after) slot = "suffix";
return {
text,
action: action || "",
description: description || "",
slot,
before,
after,
};
}
let cachedMeaningLexicon = null;
// Parse the embedded lexicon once. Each meaning keeps the semantic roles it
// plays, the surface words (across every language) that evidence it, and the
// richer per-form data (action + self-describing note + derived slot) in
// declaration order. Mirrors parse_lexicon in src/seed/meanings.rs.
function meaningLexicon() {
if (cachedMeaningLexicon) return cachedMeaningLexicon;
const root = parseLinoTree(MEANINGS_LINO);
// The lexicon is split across several files (program, units, …), each
// wrapping its records under a top-level `meanings` node. Concatenated, the
// document therefore holds one-or-more `meanings` containers; collect the
// records from every one. If none is present the records sit at the document
// root (kept for robustness). Mirrors parse_lexicon in src/seed/meanings.rs.
const containers = root.children.filter((child) => child.name === "meanings");
const sources = containers.length ? containers : [root];
const meanings = [];
for (const container of sources) {
for (const node of container.children) {
if (node.name === "meanings") continue;
const slug = meaningSlug(node);
if (!slug) continue;
const roles = [];
const definedBy = [];
const words = [];
const wordForms = [];
// Per-language word groups, so a handler can partition a role's vocabulary
// by language (e.g. head-initial vs. head-final translation verbs) without
// losing the language tag the flat `words` list drops. Mirrors the
// `lexemes` field on Meaning in src/seed/meanings.rs (issue #386).
const lexemes = [];
if (node.name !== "meaning" && node.value) {
definedBy.push(...node.value.split(/\s+/).filter(Boolean));
}
for (const child of node.children) {
if (child.name === "role") roles.push(child.value);
else if (child.name === "defined_by" || child.name === "defined-by") {
definedBy.push(child.value);
}
else if (child.name === "lexeme") {
const lexemeWords = [];
for (const lexWord of child.children) {
if (lexWord.name !== "word" && lexWord.name !== "surface") continue;
const text = surfaceText(lexWord);
words.push(text);
lexemeWords.push(text);
let action = "";
let description = "";
for (const attr of lexWord.children) {
if (attr.name === "action") action = attr.value;
else if (attr.name === "description") description = attr.value;
}
wordForms.push(
makeWordForm(text, action, description || generatedWordDescription(slug, text)),
);
}
lexemes.push({ language: lexemeLanguage(child), words: lexemeWords });
} else if (child.name === "surface") {
const text = surfaceText(child);
words.push(text);
let action = "";
let description = "";
for (const attr of child.children) {
if (attr.name === "action") action = attr.value;
else if (attr.name === "description") description = attr.value;
}
wordForms.push(
makeWordForm(text, action, description || generatedWordDescription(slug, text)),
);
lexemes.push({ language: childValue(child, "language"), words: [text] });
}
}
meanings.push({ slug, roles, definedBy, words, wordForms, lexemes });
}
}
cachedMeaningLexicon = meanings;
return cachedMeaningLexicon;
}
function childValue(node, name) {
const child = (node.children || []).find((candidate) => candidate.name === name);
return child ? child.value : "";
}
function meaningSlug(node) {
return node.name === "meaning" ? node.value : node.name;
}
function lexemeLanguage(node) {
return childValue(node, "language") || node.value || "";
}
function decodeCodepoints(raw) {
return String(raw || "")
.split(/\s+/)
.filter(Boolean)
.map((part) => {
const trimmed = part.trim();
const radix = /^0x/i.test(trimmed) ? 16 : 10;
const parsed = parseInt(radix === 16 ? trimmed.slice(2) : trimmed, radix);
return String.fromCodePoint(Number.isFinite(parsed) ? parsed : 0);
})
.join("");
}
function surfaceText(node) {
// Issue #398: surfaces now carry a readable `text` child; the legacy
// `codepoints` byte-dump is still decoded for backward compatibility.
const text = childValue(node, "text");
if (text) return text;
const codepoints = childValue(node, "codepoints");
return codepoints ? decodeCodepoints(codepoints) : node.value;
}
function generatedWordDescription(parentMeaning, text) {
return text ? `${text} denotes ${parentMeaning}` : parentMeaning;
}
// Does `expected` (a surface word or multi-word phrase) appear in `normalized`?
// CJK surfaces match as substrings; everything else matches on whitespace
// boundaries (whole token or whole phrase). Mirrors surface_present in
// src/seed/meanings.rs — stricter than a raw substring, so a short surface like
// "hp" never matches inside "php" and a phrase like "each step" matches only on
// word boundaries.
function surfacePresent(normalized, expected) {
if (!expected) return false;
const text = String(normalized || "");
if (containsCjk(expected)) return text.includes(expected);
return (
text === expected ||
text.startsWith(`${expected} `) ||
text.endsWith(` ${expected}`) ||
text.includes(` ${expected} `)
);
}
// Is any surface word (any language) of `meaning` evidenced in `normalized`?
// Mirrors Meaning::evidenced_in in src/seed/meanings.rs.
function meaningEvidencedIn(meaning, normalized) {
return meaning.words.some((word) => surfacePresent(normalized, word));
}
// Does `normalized` mention any surface word of any meaning carrying `role`?
// Mirrors Lexicon::mentions_role in src/seed/meanings.rs — the boundary-aware,
// phrase-capable surface_present contract (CJK substring vs. whitespace token).
function lexiconMentionsRole(role, normalized) {
return meaningsWithRole(role).some((meaning) => meaningEvidencedIn(meaning, normalized));
}
// Like lexiconMentionsRole but ignores a meaning's script-independent *value
// surfaces* — word forms with no alphabetic character, such as the operator
// symbol "+" or the numeral "10". Those forms exist so the arithmetic normalizer
// can read a meaning's machine value; they are not spelled words, so operator-
// *word* detection skips them and a bare "+" is recognised as an operator symbol
// by the symbol scan, not double-counted here. Mirrors Lexicon::mentions_role_spelled
// in src/seed/meanings.rs.
function lexiconMentionsRoleSpelled(role, normalized) {
return meaningsWithRole(role).some((meaning) =>
meaning.words
.filter((word) => /\p{Alphabetic}/u.test(word))
.some((word) => surfacePresent(normalized, word)),
);
}
// The first meaning (declaration order) carrying `role` that is evidenced in
// `normalized`, or null. Declaration order encodes priority. Mirrors
// Lexicon::first_role_match in src/seed/meanings.rs.
function firstRoleMatch(role, normalized) {
return meaningsWithRole(role).find((meaning) => meaningEvidencedIn(meaning, normalized)) || null;
}
// Issue #386 calendar roles — mirror the ROLE_CALENDAR_* consts in
// src/seed/meanings.rs. Their surface words live in
// data/seed/meanings-calendar.lino (embedded in MEANINGS_LINO above). The
// calendar handler uses its own boundary-aware matcher (containsCalendarTerm)
// rather than lexiconMentionsRole, so these come with dedicated accessors.
const ROLE_CALENDAR_WEEKDAY = "calendar_weekday";
const ROLE_CALENDAR_DIRECTION_NEXT = "calendar_direction_next";
const ROLE_CALENDAR_DIRECTION_PREVIOUS = "calendar_direction_previous";
const ROLE_CALENDAR_TODAY = "calendar_today";
const ROLE_CALENDAR_DAY_REFERENCE = "calendar_day_reference";
const ROLE_CALENDAR_QUESTION = "calendar_question";
// Every meaning carrying `role`, in lexicon (declaration) order. Mirrors
// Lexicon::meanings_with_role in src/seed/meanings.rs.
function meaningsWithRole(role) {
return meaningLexicon().filter((meaning) => meaning.roles.includes(role));
}
// Every slot-marked surface form carrying `role`, flattened across all meanings
// and languages in declaration order. Recognition code buckets the result by
// form.slot ("bare" / "prefix" / "suffix" / "circumfix") to derive its
// affix-matching strategy from the data. Mirrors Lexicon::role_word_forms in
// src/seed/meanings.rs (issue #386).
function roleWordForms(role) {
const forms = [];
for (const meaning of meaningsWithRole(role)) {
for (const form of meaning.wordForms) forms.push(form);
}
return forms;
}
// The meaning identified by `slug`, or null. Mirrors Lexicon::meaning in
// src/seed/meanings.rs.
function findMeaning(slug) {
return meaningLexicon().find((meaning) => meaning.slug === slug) || null;
}
// Distinct surface words (across all languages) carried by the meaning `slug`,
// or an empty array when no such meaning exists. The coding catalog reads each
// language's and task's alias surfaces from the `program_language_<slug>` /
// `program_task_<slug>` meanings by slug (issue #386), so the matchers name only
// the concept while the words stay self-describing seed data. Mirrors the Rust
// `coding::catalog::alias_surfaces` helper.
function wordsForMeaning(slug) {
return findMeaning(slug)?.words || [];
}
// Distinct surface words (across all languages) for `role`, declaration order.
// Mirrors Lexicon::words_for_role in src/seed/meanings.rs.
function wordsForRole(role) {
const seen = new Set();
const words = [];
for (const meaning of meaningsWithRole(role)) {
for (const word of meaning.words) {
if (!seen.has(word)) {
seen.add(word);
words.push(word);
}
}
}
return words;
}
// Issue #386 translation-command role — mirrors ROLE_TRANSLATION_ACTION in
// src/seed/roles.rs. The per-language command verbs (translate, переведи/
// перевести/опиши, अनुवाद, 翻译/翻譯) live once in the embedded MEANINGS_LINO
// (data/seed/meanings-translation.lino) under the `translate` meaning; the gate
// and source-inferencer reference the concept, not the raw words.
const ROLE_TRANSLATION_ACTION = "translation_action";
// Distinct surface words for `role` limited to `languages`, declaration order.
// Mirrors Lexicon::words_for_role_in_languages in src/seed/meanings.rs (#386).
function wordsForRoleInLanguages(role, languages) {
const out = [];
for (const meaning of meaningsWithRole(role)) {
for (const lexeme of meaning.lexemes) {
if (!languages.includes(lexeme.language)) continue;
for (const word of lexeme.words) {
if (!out.includes(word)) out.push(word);
}
}
}
return out;
}
// The first language in `priority` whose surface word for `role` appears in
// `normalized` (raw substring), or null when none is present. Answers "which
// language did the user issue this command in?". Mirrors
// Lexicon::first_role_language in src/seed/meanings.rs (#386).
function firstRoleLanguage(role, normalized, priority) {
for (const lang of priority) {
const present = meaningsWithRole(role).some((meaning) =>
meaning.lexemes.some(
(lexeme) =>
lexeme.language === lang &&
lexeme.words.some((word) => normalized.includes(word)),
),
);
if (present) return lang;
}
return null;
}
// The first surface word `meaning` lexicalises in `language`, or null. Mirrors
// Meaning::word_in in src/seed/meanings.rs (issue #386).
function wordIn(meaning, language) {
for (const lexeme of meaning.lexemes) {
if (lexeme.language !== language) continue;
for (const word of lexeme.words) {
if (word) return word;
}
}
return null;
}
// Translate `surface` from `source` to `target` through the meaning carrying
// `role` that lexicalises it: the first such meaning (declaration order) whose
// `source` lexeme lists `surface`, returning its first `target` form, or null.
// Mirrors Lexicon::role_surface_translation in src/seed/meanings.rs (issue #386).
function roleSurfaceTranslation(role, source, target, surface) {
for (const meaning of meaningsWithRole(role)) {
const lists = meaning.lexemes.some(
(lexeme) => lexeme.language === source && lexeme.words.includes(surface),
);
if (lists) return wordIn(meaning, target);
}
return null;
}
// Does any meaning carrying `role` lexicalise `surface` in `language`? Mirrors
// Lexicon::role_lists_surface in src/seed/meanings.rs (issue #386).
function roleListsSurface(role, language, surface) {
return meaningsWithRole(role).some((meaning) =>
meaning.lexemes.some(
(lexeme) => lexeme.language === language && lexeme.words.includes(surface),
),
);
}
// Like roleSurfaceTranslation but the `source` form must also carry the per-form
// grammatical `action` tag (e.g. "genitive"). The worker keeps per-form action on
// the flat wordForms array (no language) and raw strings on each lexeme, so a
// form qualifies when the source lexeme lists `surface` and some wordForm has the
// same text and `action`. Mirrors Lexicon::role_action_surface_translation in
// src/seed/meanings.rs (issue #386).
function roleActionSurfaceTranslation(role, action, source, target, surface) {
for (const meaning of meaningsWithRole(role)) {
const listsInSource = meaning.lexemes.some(
(lexeme) => lexeme.language === source && lexeme.words.includes(surface),
);
const tagged = meaning.wordForms.some(
(form) => form.text === surface && form.action === action,
);
if (listsInSource && tagged) return wordIn(meaning, target);
}
return null;
}
// Issue #386 software-project follow-up roles — mirror the
// ROLE_SOFTWARE_FOLLOWUP_* consts in src/seed/meanings.rs. Their surface words
// live in data/seed/meanings-software-project.lino (embedded in MEANINGS_LINO
// above). detectSoftwareFollowUp matches them as raw substrings (not whole
// tokens), so they come with a dedicated accessor.
const ROLE_SOFTWARE_FOLLOWUP_VERIFICATION = "software_followup_verification";
const ROLE_SOFTWARE_FOLLOWUP_EXECUTION = "software_followup_execution";
const ROLE_SOFTWARE_FOLLOWUP_DEMONSTRATION = "software_followup_demonstration";
// Issue #386 behavior-rules-list roles — mirror the ROLE_RULE_LISTING_* consts
// in src/seed/roles.rs. Their surface words live in
// data/seed/meanings-behavior-rules.lino (embedded in MEANINGS_LINO above).
// isBehaviorRulesList ANDs the three compositional dimensions within one
// language (subject + request + scope) and ORs the standalone phrase role,
// matching every surface as a raw substring exactly like the Rust recogniser.
const ROLE_RULE_LISTING_SUBJECT = "rule_listing_subject";
const ROLE_RULE_LISTING_REQUEST = "rule_listing_request";
const ROLE_RULE_LISTING_SCOPE = "rule_listing_scope";
const ROLE_RULE_LISTING_PHRASE = "rule_listing_phrase";
// Does `normalized` contain any surface word of any meaning carrying `role`,
// matched as a raw substring? Unlike lexiconMentionsRole (whole-token, via
// containsProgramToken), follow-up markers are multi-word phrases ("run the
// tests", "show me"), so a token-boundary match would never find them. Mirrors
// the raw `.contains()` in follow_up_kind
// (src/solver_handlers/software_project_followup.rs).
function lexiconMentionsRoleSubstring(role, normalized) {
return meaningLexicon().some(
(meaning) =>
meaning.roles.includes(role) &&
meaning.words.some((word) => word && normalized.includes(word)),
);
}
// Does any surface form `meaning` lexicalises in one of `languages` appear in
// `normalized` as a raw substring (String.includes)? The language-restricted,
// raw-substring sibling of meaningEvidencedIn. Mirrors
// Meaning::mentions_in_languages_raw in src/seed/meanings.rs (issue #386).
function meaningMentionsInLanguagesRaw(meaning, normalized, languages) {
return meaning.lexemes.some(
(lexeme) =>
languages.includes(lexeme.language) &&
lexeme.words.some((word) => word && normalized.includes(word)),
);
}
// The first meaning (declaration order) carrying `role` whose `languages`
// surface forms occur in `normalized` as a raw substring, or null. Declaration
// order encodes priority. Mirrors Lexicon::first_role_match_in_languages_raw in
// src/seed/meanings.rs (issue #386).
function firstRoleMatchInLanguagesRaw(role, normalized, languages) {
return (
meaningsWithRole(role).find((meaning) =>
meaningMentionsInLanguagesRaw(meaning, normalized, languages),
) || null
);
}
// Does any meaning carrying `role` mention one of its `languages` surface forms
// in `normalized` as a raw substring? Mirrors
// Lexicon::mentions_role_in_languages_raw in src/seed/meanings.rs (issue #386).
function mentionsRoleInLanguagesRaw(role, normalized, languages) {
return meaningsWithRole(role).some((meaning) =>
meaningMentionsInLanguagesRaw(meaning, normalized, languages),
);
}
function detectedProgramModifiers(normalized) {
const slugs = [];
const validModifiers = programModifierSlugs();
for (const operation of operationVocabulary()) {
if (validModifiers.has(operation.slug) && operationFormMatches(normalized, operation)) {
slugs.push(operation.slug);
}
}
return slugs;
}
// --- Substitution engine (mirror of src/substitution.rs) -------------------
function unescapeLinoValue(value) {
let out = "";
for (let index = 0; index < value.length; index += 1) {
const ch = value[index];
if (ch === "\\" && index + 1 < value.length) {
const next = value[index + 1];
if (next === "n") {
out += "\n";
index += 1;
continue;
}
if (next === '"' || next === "\\") {
out += next;
index += 1;
continue;
}
}
out += ch;
}
return out;
}
function unescapeSingleLinoValue(value) {
let out = "";
for (let index = 0; index < value.length; index += 1) {
const ch = value[index];
if (ch === "\\" && index + 1 < value.length) {
const next = value[index + 1];
if (next === "n") {
out += "\n";
index += 1;
continue;
}
if (next === "\\") {
out += "\\";
index += 1;
continue;
}
if (next === "x" && value.slice(index + 2, index + 4) === "27") {
out += "'";
index += 3;
continue;
}
}
out += ch;
}
return out;
}
function decodeRawReference(raw) {
const value = String(raw || "").trim();
if (value === "unformalized-raw" || value === "codepoints") return "";
if (value.startsWith("unformalized-raw ")) {
return decodeCodepoints(value.slice("unformalized-raw ".length));
}
if (value.startsWith("codepoints ")) {
return decodeCodepoints(value.slice("codepoints ".length));
}
return value;
}
function stripLinoComment(line) {
let inDoubleQuote = false;
let escaped = false;
let previousWasSpace = true;
for (let index = 0; index < line.length; index += 1) {
const ch = line[index];
if (inDoubleQuote) {
if (escaped) {
escaped = false;
} else if (ch === "\\") {
escaped = true;
} else if (ch === '"') {
inDoubleQuote = false;
}
continue;
}
if (ch === '"') {
inDoubleQuote = true;
previousWasSpace = false;
continue;
}
if (ch === "#" && previousWasSpace) return line.slice(0, index);
previousWasSpace = /\s/.test(ch);
}
return line;
}
function parseLinoValue(raw) {
const trimmed = raw.trim();
if (trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"')) {
return unescapeLinoValue(trimmed.slice(1, -1));
}
if (trimmed.length >= 2 && trimmed.startsWith("'") && trimmed.endsWith("'")) {
return unescapeSingleLinoValue(trimmed.slice(1, -1));
}
return decodeRawReference(trimmed);
}
function parseLinoTree(text) {
const root = { name: "", value: "", depth: -1, children: [] };
const stack = [root];
for (const line of text.split("\n")) {
const stripped = stripLinoComment(line);
if (!stripped.trim()) continue;
const indent = stripped.length - stripped.trimStart().length;
const depth = indent / 2;
const rest = stripped.trim();
const colonIndex = rest.indexOf(":");
const whitespaceIndex = rest.search(/\s/);
let name = "";
let value = "";
if (colonIndex !== -1 && (whitespaceIndex === -1 || colonIndex < whitespaceIndex)) {
name = rest.slice(0, colonIndex).trim();
value = parseLinoValue(rest.slice(colonIndex + 1));
} else if (whitespaceIndex === -1) {
name = rest;
} else {
name = rest.slice(0, whitespaceIndex);
value = parseLinoValue(rest.slice(whitespaceIndex + 1));
}
const node = { name, value, depth, children: [] };
while (stack.length && stack[stack.length - 1].depth >= depth) stack.pop();
stack[stack.length - 1].children.push(node);
stack.push(node);
}
return root;
}
function parsePatternNode(text) {
if (!text) throw new Error("pattern node is empty");
if (text.startsWith("$")) return { kind: "variable", variable: text.slice(1) };
const dollar = text.indexOf("$");
if (dollar !== -1) {
return { kind: "prefix", prefix: text.slice(0, dollar), variable: text.slice(dollar + 1) };
}
return { kind: "literal", value: text };
}
function parseLinkPattern(text) {
const index = text.indexOf("->");
if (index === -1) throw new Error(`expected \`from -> to\`, got \`${text}\``);
return {
from: parsePatternNode(text.slice(0, index).trim()),
to: parsePatternNode(text.slice(index + 2).trim()),
};
}
function parseCrudEvent(value) {
const map = {
manual: "manual",
apply: "manual",
create: "create",
created: "create",
read: "read",
select: "read",
query: "read",
update: "update",
updated: "update",
delete: "delete",
deleted: "delete",
};
const key = String(value).trim().toLowerCase();
if (!map[key]) throw new Error(`invalid CRUD event: ${value}`);
return map[key];
}
function parseSubstitutionRule(node) {
const rule = { id: node.value, order: 0, events: [], conditions: [], actions: [] };
for (const child of node.children) {
switch (child.name) {
case "order": {
const parsed = parseInt(child.value, 10);
rule.order = Number.isNaN(parsed) ? 0 : parsed;
break;
}
case "event":
rule.events.push(parseCrudEvent(child.value));
break;
case "when":
rule.conditions.push(parseLinkPattern(child.value));
break;
case "replace": {
const add = child.children
.filter((grandchild) => grandchild.name === "with")
.map((grandchild) => parseLinkPattern(grandchild.value));
rule.actions.push({ remove: parseLinkPattern(child.value), add });
break;
}
default:
break;
}
}
return rule;
}
function parseSubstitutionRules(text) {
const tree = parseLinoTree(text.trim());
const root = tree.children[0];
if (!root || root.name !== "substitution_rules") {
throw new Error("not a substitution_rules document");
}
const idNode = root.children.find((child) => child.name === "id");
const id = idNode ? idNode.value : "";
const rules = root.children
.filter((child) => child.name === "rule")
.map(parseSubstitutionRule);
rules.sort((left, right) =>
left.order - right.order ||
(left.id < right.id ? -1 : left.id > right.id ? 1 : 0),
);
return { id, rules };
}
const LINK_KEY_SEPARATOR = "