@burdenoff/fe-libs 2026.1009.2 → 2026.1010.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/shared/assistant/appControls/grammar.js +48 -41
  2. package/dist/shared/assistant/appControls/languages.d.ts +2 -0
  3. package/dist/shared/assistant/appControls/languages.js +8 -5
  4. package/dist/shared/assistant/index.d.ts +18 -0
  5. package/dist/shared/assistant/localUiCommands.d.ts +33 -0
  6. package/dist/shared/assistant/localUiCommands.js +159 -88
  7. package/dist/shared/assistant/localeCommands/active.d.ts +19 -0
  8. package/dist/shared/assistant/localeCommands/active.js +25 -0
  9. package/dist/shared/assistant/localeCommands/assent.d.ts +25 -0
  10. package/dist/shared/assistant/localeCommands/assent.js +78 -0
  11. package/dist/shared/assistant/localeCommands/engine.d.ts +89 -0
  12. package/dist/shared/assistant/localeCommands/engine.js +351 -0
  13. package/dist/shared/assistant/localeCommands/evaluate.d.ts +77 -0
  14. package/dist/shared/assistant/localeCommands/evaluate.js +180 -0
  15. package/dist/shared/assistant/localeCommands/fold.d.ts +17 -0
  16. package/dist/shared/assistant/localeCommands/fold.js +66 -0
  17. package/dist/shared/assistant/localeCommands/frames.d.ts +23 -0
  18. package/dist/shared/assistant/localeCommands/frames.js +150 -0
  19. package/dist/shared/assistant/localeCommands/gate.d.ts +37 -0
  20. package/dist/shared/assistant/localeCommands/gate.js +137 -0
  21. package/dist/shared/assistant/localeCommands/languageNames.d.ts +11 -0
  22. package/dist/shared/assistant/localeCommands/languageNames.js +53 -0
  23. package/dist/shared/assistant/localeCommands/nameForms.d.ts +21 -0
  24. package/dist/shared/assistant/localeCommands/nameForms.js +44 -0
  25. package/dist/shared/assistant/localeCommands/normalise.d.ts +49 -0
  26. package/dist/shared/assistant/localeCommands/normalise.js +87 -0
  27. package/dist/shared/assistant/localeCommands/symbols.d.ts +8 -0
  28. package/dist/shared/assistant/localeCommands/symbols.js +6 -0
  29. package/dist/shared/assistant/localeCommands/tables/ar.d.ts +10 -0
  30. package/dist/shared/assistant/localeCommands/tables/ar.js +343 -0
  31. package/dist/shared/assistant/localeCommands/tables/en.d.ts +12 -0
  32. package/dist/shared/assistant/localeCommands/tables/en.js +28 -0
  33. package/dist/shared/assistant/localeCommands/tables/es.d.ts +7 -0
  34. package/dist/shared/assistant/localeCommands/tables/es.js +356 -0
  35. package/dist/shared/assistant/localeCommands/tables/hi.d.ts +9 -0
  36. package/dist/shared/assistant/localeCommands/tables/hi.js +495 -0
  37. package/dist/shared/assistant/localeCommands/tables/ta.d.ts +10 -0
  38. package/dist/shared/assistant/localeCommands/tables/ta.js +429 -0
  39. package/dist/shared/assistant/localeCommands/tables/zh.d.ts +9 -0
  40. package/dist/shared/assistant/localeCommands/tables/zh.js +402 -0
  41. package/dist/shared/assistant/localeCommands/types.d.ts +172 -0
  42. package/dist/shared/assistant/localeCommands/types.js +19 -0
  43. package/dist/shared/assistant/ui/AssistantChatArea.js +1340 -1330
  44. package/dist/shared/assistant/uiActions/linkContext.d.ts +20 -1
  45. package/dist/shared/assistant/uiActions/linkContext.js +39 -21
  46. package/dist/shared/assistant/uiActions/offerYesOnSurface.d.ts +6 -0
  47. package/dist/shared/assistant/uiActions/offerYesOnSurface.js +1 -1
  48. package/dist/shared/assistant/writeReplyBinder.d.ts +1 -1
  49. package/dist/shared/assistant/writeReplySend.d.ts +16 -0
  50. package/dist/shared/assistant/writeReplySend.js +23 -1
  51. package/dist/shared/graphql/client.d.ts +20 -0
  52. package/dist/shared/graphql/client.js +196 -191
  53. package/dist/shared/graphql/retryPolicy.d.ts +27 -0
  54. package/dist/shared/graphql/retryPolicy.js +10 -0
  55. package/dist/shared/utils/apollo.d.ts +6 -2
  56. package/dist/shared/utils/apollo.js +26 -25
  57. package/dist/shared-assistant.js +55 -37
  58. package/package.json +2 -2
@@ -0,0 +1,23 @@
1
+ import { CommandLocale } from './types';
2
+ /** The words a closed slot may be: a map from the FOLDED word to its value. */
3
+ export type ClosedSlots = Readonly<Record<string, ReadonlyMap<string, string | number>>>;
4
+ /** What a template captured: slot name → folded text. */
5
+ export type Captures = Readonly<Record<string, string>>;
6
+ export interface CompiledFrame {
7
+ readonly template: string;
8
+ /** Lazy then greedy; identical when the template has no open slot. */
9
+ readonly expressions: readonly RegExp[];
10
+ readonly slots: readonly string[];
11
+ }
12
+ /** Compile a template for `locale`. Throws on a malformed template (a table bug). */
13
+ export declare function compileFrame(template: string, locale: CommandLocale | "en", closed?: ClosedSlots): CompiledFrame;
14
+ /** Every distinct parse of the whole folded utterance by this frame. */
15
+ export declare function matchFrame(frame: CompiledFrame, folded: string): Captures[];
16
+ /** A map from folded word to value, for a closed slot. */
17
+ export declare function closedSlot(entries: ReadonlyArray<readonly [readonly string[], string | number]>, locale: CommandLocale | "en"): ReadonlyMap<string, string | number>;
18
+ /**
19
+ * Every sentence a template can say, with each slot filled from `slots` (a
20
+ * closed slot may be left out: its table words are used). For the tests and the
21
+ * evaluation harness — the engine never enumerates, it matches.
22
+ */
23
+ export declare function expandTemplate(template: string, locale: CommandLocale | "en", slots: Readonly<Record<string, readonly string[]>>, closed?: ClosedSlots): string[];
@@ -0,0 +1,150 @@
1
+ import { foldText as e } from "./fold.js";
2
+ //#region src/shared/assistant/localeCommands/frames.ts
3
+ var t = new Set([
4
+ "x",
5
+ "q",
6
+ "lang"
7
+ ]), n = new Set([
8
+ "p",
9
+ "o",
10
+ "n",
11
+ "d"
12
+ ]), r = 6, i = 12, a = (e) => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13
+ function o(t, n) {
14
+ let r = 0, i = (a) => {
15
+ let o = [[]], s = "", c = () => {
16
+ if (!s) return;
17
+ let t = e(s, n);
18
+ if (s = "", !t) return;
19
+ let r = o[o.length - 1];
20
+ for (let e of t.split(" ")) r.push({
21
+ type: "lit",
22
+ text: e
23
+ });
24
+ };
25
+ for (; r < t.length;) {
26
+ let e = t.charAt(r);
27
+ if (a !== null && e === a) return c(), o;
28
+ let n = o[o.length - 1];
29
+ if (e === " ") c(), r += 1;
30
+ else if (e === "(") {
31
+ c(), r += 1;
32
+ let e = i(")");
33
+ if (r += 1, e.some((e) => e.length === 0)) throw Error(`empty alternative in template "${t}"`);
34
+ n.push({
35
+ type: "alt",
36
+ options: e
37
+ });
38
+ } else if (e === "[") {
39
+ c(), r += 1;
40
+ let e = i("]");
41
+ r += 1;
42
+ let [a, ...o] = e;
43
+ if (o.length > 0 || !a || a.length === 0) throw Error(`bad optional group in template "${t}"`);
44
+ n.push({
45
+ type: "opt",
46
+ seq: a
47
+ });
48
+ } else if (e === "{") {
49
+ c();
50
+ let e = t.indexOf("}", r);
51
+ if (e < 0) throw Error(`unclosed slot in template "${t}"`);
52
+ n.push({
53
+ type: "slot",
54
+ name: t.slice(r + 1, e)
55
+ }), r = e + 1;
56
+ } else if (e === "|" && a === ")") c(), r += 1, o.push([]);
57
+ else if (e === "|" && a === "]") throw Error(`alternatives inside [ ] in template "${t}": use [(a|b)]`);
58
+ else s += e, r += 1;
59
+ }
60
+ if (a !== null) throw Error(`unclosed group in template "${t}"`);
61
+ return c(), o;
62
+ }, [a] = i(null);
63
+ return a ?? [];
64
+ }
65
+ function s(e, o) {
66
+ if (o.slots.add(e), n.has(e)) {
67
+ let t = [...o.closed[e]?.keys() ?? []].sort((e, t) => t.length - e.length);
68
+ if (t.length === 0) throw Error(`no words for the closed slot {${e}}`);
69
+ return `(?<${e}>${t.map(a).join("|")})`;
70
+ }
71
+ if (!t.has(e)) throw Error(`unknown slot {${e}}`);
72
+ let s = o.lazy ? "?" : "";
73
+ return e === "q" ? `(?<q>.+${s})` : o.zh ? `(?<${e}>.{1,${i}}${s})` : `(?<${e}>\\S+(?: \\S+){0,${r - 1}}${s})`;
74
+ }
75
+ function c(e, t) {
76
+ let n = "", r = !1;
77
+ for (let i of e) {
78
+ if (i.type === "opt") {
79
+ let e = c(i.seq, t);
80
+ n += r ? `(?:${t.sep}${e})?` : `(?:${e}${t.sep})?`;
81
+ continue;
82
+ }
83
+ let e;
84
+ if (i.type === "lit") e = a(i.text);
85
+ else if (i.type === "slot") e = s(i.name, t);
86
+ else {
87
+ let n = i.options.map((e) => c(e, t));
88
+ n.sort((e, t) => t.length - e.length), e = `(?:${n.join("|")})`;
89
+ }
90
+ n += (r ? t.sep : "") + e, r = !0;
91
+ }
92
+ return n;
93
+ }
94
+ function l(e, n, r = {}) {
95
+ let i = o(e, n), a = n === "zh", s = /* @__PURE__ */ new Set(), l = (e) => RegExp(`^${c(i, {
96
+ sep: a ? "" : " ",
97
+ lazy: e,
98
+ closed: r,
99
+ zh: a,
100
+ slots: s
101
+ })}$`, "u"), u = l(!0), d = l(!1);
102
+ return {
103
+ template: e,
104
+ expressions: [...s].some((e) => t.has(e)) ? [u, d] : [u],
105
+ slots: [...s]
106
+ };
107
+ }
108
+ function u(e, t) {
109
+ let n = [], r = /* @__PURE__ */ new Set();
110
+ for (let i of e.expressions) {
111
+ let e = i.exec(t);
112
+ if (!e) continue;
113
+ let a = {};
114
+ for (let [t, n] of Object.entries(e.groups ?? {})) typeof n == "string" && (a[t] = n);
115
+ let o = JSON.stringify(a);
116
+ r.has(o) || (r.add(o), n.push(a));
117
+ }
118
+ return n;
119
+ }
120
+ function d(t, n) {
121
+ let r = /* @__PURE__ */ new Map();
122
+ for (let [i, a] of t) for (let t of i) {
123
+ let i = e(t, n);
124
+ i && r.set(i, a);
125
+ }
126
+ return r;
127
+ }
128
+ var f = 2e4;
129
+ function p(e, t, n, r = {}) {
130
+ let i = o(e, t), a = t === "zh" ? "" : " ", s = (t) => {
131
+ let i = n[t];
132
+ if (i) return i;
133
+ let a = r[t];
134
+ if (a) return [...a.keys()];
135
+ throw Error(`no values for slot {${t}} in "${e}"`);
136
+ }, c = (t) => {
137
+ let n = [""];
138
+ for (let r of t) {
139
+ let t;
140
+ t = r.type === "lit" ? [r.text] : r.type === "slot" ? [...s(r.name)] : r.type === "alt" ? r.options.flatMap((e) => c(e)) : ["", ...c(r.seq)];
141
+ let i = [];
142
+ for (let r of n) for (let n of t) if (i.push(r === "" ? n : n === "" ? r : `${r}${a}${n}`), i.length > f) throw Error(`too many expansions of "${e}"`);
143
+ n = i;
144
+ }
145
+ return n;
146
+ };
147
+ return c(i);
148
+ }
149
+ //#endregion
150
+ export { d as closedSlot, l as compileFrame, p as expandTemplate, u as matchFrame };
@@ -0,0 +1,37 @@
1
+ import { CommandFamily, CommandManifest, ManifestLocale } from './types';
2
+ /**
3
+ * What the shipped build turns on: only the cells that passed the round-3 blind
4
+ * gate (0 false matches, recall over the floor; evidence in `frozen/`, report
5
+ * `audit/platform/assistant/2026-10-09-multilingual-commands/eval/round3.md`).
6
+ */
7
+ export declare const COMMAND_MANIFEST: CommandManifest;
8
+ /** The families each manifest locale can carry. `en` carries only the English additions. */
9
+ export declare const MANIFEST_FAMILIES: Readonly<Record<ManifestLocale, readonly (CommandFamily | "frames")[]>>;
10
+ /** Design §3.2 G4: the recall an entry needs to be enabled for go-to, there and yes. */
11
+ export declare const RECALL_FLOOR = 0.6;
12
+ /**
13
+ * A manifest with every entry on. For the evaluation harness and the tests
14
+ * ONLY: it carries no evidence on purpose, so `validateManifest` rejects it —
15
+ * it can never be mistaken for a shippable one.
16
+ */
17
+ export declare function allOnManifest(): CommandManifest;
18
+ /** A manifest with only the listed `locale:family` pairs on (tests, evaluation of one family). */
19
+ export declare function manifestWith(on: readonly string[]): CommandManifest;
20
+ /**
21
+ * The manifest a matcher actually uses for a caller-supplied one. Absent: the
22
+ * shipped manifest. One the evaluation builders made (the harness, the tests):
23
+ * as given. Anything else: only the cells that are ON in BOTH it and the
24
+ * shipped manifest, with the shipped entry's evidence, so a product may narrow
25
+ * what shipped but never widen it past the evidence.
26
+ */
27
+ export declare function effectiveManifest(supplied: CommandManifest | undefined, shipped?: CommandManifest): CommandManifest;
28
+ /** Is `family` of `locale` on in this manifest? */
29
+ export declare function isEnabled(manifest: CommandManifest, locale: ManifestLocale, family: CommandFamily | "frames"): boolean;
30
+ /** Does this locale have any family on? (A table with nothing on is not active.) */
31
+ export declare function localeHasAnyOn(manifest: CommandManifest, locale: ManifestLocale): boolean;
32
+ /**
33
+ * The evidence rule, as a list of problems (empty: the manifest may ship).
34
+ * `corpusShaOf` reads the sha256 of the frozen blind file an entry names, or
35
+ * null when that file does not exist.
36
+ */
37
+ export declare function validateManifest(manifest: CommandManifest, corpusShaOf: (locale: ManifestLocale, family: CommandFamily | "frames") => string | null): string[];
@@ -0,0 +1,137 @@
1
+ import { COMMAND_FAMILIES as e, COMMAND_LOCALES as t } from "./types.js";
2
+ //#region src/shared/assistant/localeCommands/gate.ts
3
+ function n(e) {
4
+ return Object.freeze({
5
+ enabled: !0,
6
+ ...e
7
+ });
8
+ }
9
+ var r = Object.freeze({
10
+ en: Object.freeze({}),
11
+ hi: Object.freeze({ language: n({
12
+ corpusSha256: "c15bed2eabee0fece15456f96923bd12abf3e6f8884619a8a90d2e9a99c0acac",
13
+ positives: 8,
14
+ negatives: 195,
15
+ falseMatches: 0,
16
+ recall: 1
17
+ }) }),
18
+ ta: Object.freeze({}),
19
+ ar: Object.freeze({
20
+ goto: n({
21
+ corpusSha256: "9c39b609525a616bcba5992f6a30279af8023bd478d2c3f5692bba9b668893be",
22
+ positives: 26,
23
+ negatives: 188,
24
+ falseMatches: 0,
25
+ recall: .769
26
+ }),
27
+ language: n({
28
+ corpusSha256: "86a23a6e8920558576fd2f73cb3504a766103b8b58d0255ff4a4b27ccff46414",
29
+ positives: 8,
30
+ negatives: 188,
31
+ falseMatches: 0,
32
+ recall: .75
33
+ })
34
+ }),
35
+ es: Object.freeze({
36
+ goto: n({
37
+ corpusSha256: "5b67ba87a2a69dd6ec656c8f1d76c65a001043059fc7b0d4d971e641b52f9746",
38
+ positives: 26,
39
+ negatives: 188,
40
+ falseMatches: 0,
41
+ recall: .769
42
+ }),
43
+ language: n({
44
+ corpusSha256: "9bc791353a258d3e22bf28f505b9b34065d9b24c5c657a69fc8ce5625e846d7d",
45
+ positives: 8,
46
+ negatives: 188,
47
+ falseMatches: 0,
48
+ recall: .625
49
+ })
50
+ }),
51
+ zh: Object.freeze({
52
+ goto: n({
53
+ corpusSha256: "94f7b5b11bfd843695674cd86840cb6b59be291c7c9dcdaa83ec9b12d5bc0d16",
54
+ positives: 26,
55
+ negatives: 188,
56
+ falseMatches: 0,
57
+ recall: .769
58
+ }),
59
+ language: n({
60
+ corpusSha256: "edefba601427357299751db7a0cebd603d8945d981c61fb663b0e958f30e00bb",
61
+ positives: 8,
62
+ negatives: 188,
63
+ falseMatches: 0,
64
+ recall: .875
65
+ })
66
+ })
67
+ }), i = Object.freeze({
68
+ en: [
69
+ "frames",
70
+ "goto",
71
+ "language"
72
+ ],
73
+ hi: e,
74
+ ta: e,
75
+ ar: e,
76
+ es: e,
77
+ zh: e
78
+ }), a = .6, o = "0".repeat(64);
79
+ function s() {
80
+ let e = (e) => Object.fromEntries(e.map((e) => [e, { enabled: !0 }]));
81
+ return u({
82
+ en: e(i.en),
83
+ hi: e(i.hi),
84
+ ta: e(i.ta),
85
+ ar: e(i.ar),
86
+ es: e(i.es),
87
+ zh: e(i.zh)
88
+ });
89
+ }
90
+ function c(e) {
91
+ let t = new Set(e), n = (e) => Object.fromEntries(i[e].filter((n) => t.has(`${e}:${n}`)).map((e) => [e, { enabled: !0 }]));
92
+ return u({
93
+ en: n("en"),
94
+ hi: n("hi"),
95
+ ta: n("ta"),
96
+ ar: n("ar"),
97
+ es: n("es"),
98
+ zh: n("zh")
99
+ });
100
+ }
101
+ var l = /* @__PURE__ */ new WeakSet();
102
+ function u(e) {
103
+ let t = Object.freeze(e);
104
+ return l.add(t), t;
105
+ }
106
+ function d(e, t = r) {
107
+ if (!e || e === t) return t;
108
+ if (l.has(e)) return e;
109
+ let n = (n) => Object.fromEntries(Object.entries(t[n]).filter(([t, r]) => r?.enabled === !0 && e[n]?.[t]?.enabled === !0));
110
+ return Object.freeze({
111
+ en: Object.freeze(n("en")),
112
+ hi: Object.freeze(n("hi")),
113
+ ta: Object.freeze(n("ta")),
114
+ ar: Object.freeze(n("ar")),
115
+ es: Object.freeze(n("es")),
116
+ zh: Object.freeze(n("zh"))
117
+ });
118
+ }
119
+ function f(e, t, n) {
120
+ return e[t][n]?.enabled === !0;
121
+ }
122
+ function p(e, t) {
123
+ return Object.values(e[t]).some((e) => e?.enabled === !0);
124
+ }
125
+ function m(e, n) {
126
+ let r = [];
127
+ for (let s of ["en", ...t]) for (let [t, c] of Object.entries(e[s])) {
128
+ if (!c?.enabled) continue;
129
+ let e = `${s}:${t}`;
130
+ i[s].includes(t) || r.push(`${e}: not a family of ${s}`), (!c.corpusSha256 || !/^[0-9a-f]{64}$/.test(c.corpusSha256)) && r.push(`${e}: enabled with no corpus sha256`), c.corpusSha256 === o && r.push(`${e}: placeholder sha256`), (c.falseMatches === void 0 || c.falseMatches !== 0) && r.push(`${e}: enabled without a recorded 0 false matches`), (c.negatives === void 0 || c.negatives <= 0) && r.push(`${e}: enabled without a negative count`), (t === "goto" || t === "there" || t === "yes") && (c.recall === void 0 || c.recall < .6) && r.push(`${e}: recall is under the ${a} floor`);
131
+ let l = n(s, t);
132
+ l === null ? r.push(`${e}: its corpus file is missing`) : c.corpusSha256 && l !== c.corpusSha256 && r.push(`${e}: its corpus file has a different hash`);
133
+ }
134
+ return r;
135
+ }
136
+ //#endregion
137
+ export { r as COMMAND_MANIFEST, i as MANIFEST_FAMILIES, a as RECALL_FLOOR, s as allOnManifest, d as effectiveManifest, f as isEnabled, p as localeHasAnyOn, c as manifestWith, m as validateManifest };
@@ -0,0 +1,11 @@
1
+ import { UiLanguage } from '../appControls/types';
2
+ import { CommandLocale, LanguageCode, LocaleTable } from './types';
3
+ /**
4
+ * Folded name → the one language it names. `fold` is the fold of the table the
5
+ * sentence is read with; the names of every `tables` locale are added.
6
+ */
7
+ export declare function languageNameMap(input: {
8
+ tables: readonly LocaleTable[];
9
+ appLanguages: readonly UiLanguage[];
10
+ fold: CommandLocale | "en";
11
+ }): ReadonlyMap<string, LanguageCode>;
@@ -0,0 +1,53 @@
1
+ import { uiLanguageWords as e } from "../appControls/languages.js";
2
+ import { foldText as t } from "./fold.js";
3
+ import { UI_LANGUAGE_CODES as n } from "../appControls/types.js";
4
+ //#region src/shared/assistant/localeCommands/languageNames.ts
5
+ var r = {
6
+ en: "en",
7
+ ar: "ar",
8
+ es: "es",
9
+ hi: "hi",
10
+ ta: "ta",
11
+ zh: "zh"
12
+ }, i = /* @__PURE__ */ new Map();
13
+ function a(e) {
14
+ let t = i.get(e);
15
+ if (t) return t;
16
+ let a = [];
17
+ try {
18
+ let t = new Intl.DisplayNames([e], { type: "language" });
19
+ for (let e of n) {
20
+ let n = t.of(r[e]);
21
+ n && a.push(`${e}\u0000${n}`);
22
+ }
23
+ } catch {}
24
+ return i.set(e, a), a;
25
+ }
26
+ var o = (e) => n.includes(e);
27
+ function s(r) {
28
+ let i = /* @__PURE__ */ new Map(), s = (e, n) => {
29
+ let a = t(e, r.fold);
30
+ if (!a) return;
31
+ let o = i.get(a) ?? /* @__PURE__ */ new Set();
32
+ o.add(n), i.set(a, o);
33
+ };
34
+ for (let t of n) for (let n of e(t)) s(n, t);
35
+ for (let e of r.appLanguages) {
36
+ let t = e.code.toLowerCase().split(/[-_]/)[0] ?? "";
37
+ o(t) && s(e.label, t);
38
+ }
39
+ let c = ["en", ...r.tables.map((e) => e.locale)];
40
+ for (let e of c) for (let t of a(e)) {
41
+ let [e, n] = t.split("\0");
42
+ s(n, e);
43
+ }
44
+ for (let e of r.tables) for (let [t, n] of Object.entries(e.languageNames)) for (let e of n) s(e, t);
45
+ let l = /* @__PURE__ */ new Map();
46
+ for (let [e, t] of i) {
47
+ let [n] = [...t];
48
+ t.size === 1 && n && l.set(e, n);
49
+ }
50
+ return l;
51
+ }
52
+ //#endregion
53
+ export { s as languageNameMap };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * ★ BOFF-8867 (multilingual commands): the closed case forms of a page name.
3
+ *
4
+ * Tamil glues its case to the noun ("வானிலைக்கு போ", "பயிர்களை திற") and Hindi
5
+ * puts the feminine plural into the oblique before a postposition ("फसलों को
6
+ * खोलो"). The page is still found by EXACT equality: the engine adds these
7
+ * forms of each label the app shows to the same index entry, and a name that is
8
+ * not the label or one of these forms is not a page. Nothing here guesses a
9
+ * stem from the sentence; the stem is always the app's own label.
10
+ *
11
+ * The list is closed on purpose. A label whose last letters match no rule has
12
+ * no extra form (it is matched bare, as before), and a collision with another
13
+ * page's own label makes the sentence a clarification, never a pick.
14
+ */
15
+ /**
16
+ * Accusative (-ஐ), dative (-க்கு / -உக்கு) and locative (-இல்) forms of a
17
+ * Tamil label, plus each with the doubled consonant of the next word.
18
+ */
19
+ export declare function tamilNameForms(label: string): readonly string[];
20
+ /** The oblique plural of a Hindi feminine-plural label (फसलें → फसलों, सब्जियाँ → सब्जियों), for "फसलों को खोलो". */
21
+ export declare function hindiNameForms(label: string): readonly string[];
@@ -0,0 +1,44 @@
1
+ //#region src/shared/assistant/localeCommands/nameForms.ts
2
+ var e = "்", t = [
3
+ "",
4
+ "க்",
5
+ "ச்",
6
+ "த்",
7
+ "ப்"
8
+ ], n = new Set([
9
+ "ள",
10
+ "ல",
11
+ "ர",
12
+ "ன",
13
+ "ண",
14
+ "ஸ",
15
+ "ட",
16
+ "ழ"
17
+ ]);
18
+ function r(r) {
19
+ let i = r.normalize("NFKC").trim();
20
+ if (!i || /\s/.test(i.slice(-1))) return [];
21
+ let a = [...i].slice(-2), o = [], s = a[a.length - 1] ?? "", c = a.length === 2 ? a[0] : "";
22
+ if (s === e && c && n.has(c)) {
23
+ let e = i.slice(0, -1);
24
+ o.push(`${e}ை`, `${e}ுக்கு`, `${e}ில்`);
25
+ } else if (s === e && c === "ம") {
26
+ let e = i.slice(0, -2);
27
+ o.push(`${e}த்தை`, `${e}த்துக்கு`, `${e}த்தில்`);
28
+ } else if (s === "ை") o.push(`${i}யை`, `${i}க்கு`, `${i}யில்`);
29
+ else if (s === "ு") {
30
+ let e = i.slice(0, -1);
31
+ o.push(`${e}ை`, `${i}க்கு`, `${e}ில்`);
32
+ } else s === "ி" && o.push(`${i}யை`, `${i}க்கு`, `${i}யில்`);
33
+ return o.flatMap((e) => t.map((t) => `${e}${t}`));
34
+ }
35
+ function i(e) {
36
+ let t = e.normalize("NFKC").trim();
37
+ if (!t) return [];
38
+ let n = [], r = (e, r) => {
39
+ e.test(t) && n.push(t.replace(e, r));
40
+ };
41
+ return r(/ें$/u, "ों"), r(/ेँ$/u, "ों"), r(/ियाँ$/u, "ियों"), r(/ियां$/u, "ियों"), r(/ाएँ$/u, "ाओं"), r(/ाएं$/u, "ाओं"), n;
42
+ }
43
+ //#endregion
44
+ export { i as hindiNameForms, r as tamilNameForms };
@@ -0,0 +1,49 @@
1
+ import { CommandLocale } from './types';
2
+ export type FoldLocale = CommandLocale | "en";
3
+ /** The normalisation words a table (or the English additions) brings. */
4
+ export interface PrepSource {
5
+ maxLength: number;
6
+ courtesy: {
7
+ prefix: readonly string[];
8
+ suffix: readonly string[];
9
+ };
10
+ fillers: readonly string[];
11
+ compound: readonly string[];
12
+ polite?: PoliteSource | undefined;
13
+ }
14
+ /**
15
+ * A question is a command only as a polite request. A request opens with one of
16
+ * `prefix` ("can you…") or ends with one of `suffix` ("…can you?"); either is
17
+ * enough. `particle` words ("吗") are dropped after one of those, never alone.
18
+ */
19
+ export interface PoliteSource {
20
+ prefix: readonly string[];
21
+ suffix: readonly string[];
22
+ particle?: readonly string[];
23
+ }
24
+ /** `PrepSource` folded and ready: longest phrase first, so the longest courtesy wins. */
25
+ export interface PrepData {
26
+ fold: FoldLocale;
27
+ zh: boolean;
28
+ maxLength: number;
29
+ prefix: string[];
30
+ suffix: string[];
31
+ fillers: Set<string>;
32
+ compound: string[];
33
+ /** null: this table has no polite request, and every question is the model's. */
34
+ polite: {
35
+ prefix: string[];
36
+ suffix: string[];
37
+ particle: string[];
38
+ } | null;
39
+ }
40
+ export declare function foldedList(list: readonly string[], fold: FoldLocale): string[];
41
+ export declare function buildPrep(source: PrepSource, fold: FoldLocale): PrepData;
42
+ export declare function removeFillers(value: string, prep: PrepData): string;
43
+ /** A whole utterance said twice in a row ("go to X go to X") is said once. */
44
+ export declare function collapseRepeat(value: string, zh: boolean): string;
45
+ export declare function stripEdges(value: string, prefixes: readonly string[], suffixes: readonly string[], zh: boolean, mustStrip?: boolean): string | null;
46
+ /** The folded utterance a run reads: fillers, a repeat and courtesy removed; null when it cannot be a command. */
47
+ export declare function normaliseSaid(said: string, prep: PrepData, question: boolean): string | null;
48
+ /** A question mark at the end, or an opening `¿`. */
49
+ export declare function isQuestionText(said: string): boolean;
@@ -0,0 +1,87 @@
1
+ import { foldText as e } from "./fold.js";
2
+ //#region src/shared/assistant/localeCommands/normalise.ts
3
+ function t(t, n) {
4
+ return t.map((t) => e(t, n)).filter(Boolean).sort((e, t) => t.length - e.length);
5
+ }
6
+ function n(e, n) {
7
+ return {
8
+ fold: n,
9
+ zh: n === "zh",
10
+ maxLength: e.maxLength,
11
+ prefix: t(e.courtesy.prefix, n),
12
+ suffix: t(e.courtesy.suffix, n),
13
+ fillers: new Set(t(e.fillers, n)),
14
+ compound: t(e.compound, n),
15
+ polite: e.polite ? {
16
+ prefix: t(e.polite.prefix, n),
17
+ suffix: t(e.polite.suffix, n),
18
+ particle: t(e.polite.particle ?? [], n)
19
+ } : null
20
+ };
21
+ }
22
+ function r(e, t) {
23
+ let { fillers: n } = t;
24
+ if (n.size === 0) return e;
25
+ if (t.zh) {
26
+ let t = e;
27
+ for (;;) {
28
+ let e = [...n].reduce((e, t) => e.length > t.length && (e.startsWith(t) || e.endsWith(t)) ? e.startsWith(t) ? e.slice(t.length) : e.slice(0, e.length - t.length) : e, t);
29
+ if (e === t) return t;
30
+ t = e;
31
+ }
32
+ }
33
+ return e.split(" ").filter((e) => !n.has(e)).join(" ");
34
+ }
35
+ function i(e, t) {
36
+ if (t) {
37
+ let t = e.length / 2;
38
+ return e.length >= 4 && Number.isInteger(t) && e.slice(0, t) === e.slice(t) ? e.slice(0, t) : e;
39
+ }
40
+ let n = e.split(" "), r = n.length / 2;
41
+ if (n.length >= 2 && Number.isInteger(r)) {
42
+ let e = n.slice(0, r).join(" ");
43
+ if (e === n.slice(r).join(" ")) return e;
44
+ }
45
+ return e;
46
+ }
47
+ function a(e, t, n, r, i = !1) {
48
+ let a = (e) => {
49
+ for (let n of t) {
50
+ let t = r ? e.startsWith(n) ? e.slice(n.length) : null : e.startsWith(`${n} `) ? e.slice(n.length + 1) : null;
51
+ if (t !== null && t.length > 0) return t;
52
+ }
53
+ return null;
54
+ }, o = (e) => {
55
+ for (let t of n) {
56
+ let n = r ? e.endsWith(t) ? e.slice(0, e.length - t.length) : null : e.endsWith(` ${t}`) ? e.slice(0, e.length - t.length - 1) : null;
57
+ if (n !== null && n.length > 0) return n;
58
+ }
59
+ return null;
60
+ }, s = e, c = !1;
61
+ for (;;) {
62
+ let e = a(s) ?? o(s);
63
+ if (e === null) break;
64
+ s = e, c = !0;
65
+ }
66
+ return i && !c ? null : s;
67
+ }
68
+ function o(t, n, o) {
69
+ let s = e(t, n.fold);
70
+ if (!s) return null;
71
+ if (o) {
72
+ let { polite: e } = n;
73
+ if (e === null) return null;
74
+ let t = a(s, e.prefix, [], n.zh, !0), r = a(t ?? s, [], e.suffix, n.zh, !0);
75
+ if (t === null && r === null) return null;
76
+ let i = r ?? t ?? s;
77
+ s = a(i, [], e.particle, n.zh) ?? i;
78
+ }
79
+ s = r(s, n), s = i(s, n.zh);
80
+ let c = a(s, n.prefix, n.suffix, n.zh);
81
+ return c === null || !c || c.length > n.maxLength ? null : c;
82
+ }
83
+ function s(e) {
84
+ return /[?¿؟?][^\p{L}\p{N}]*$/u.test(e) || /^\s*¿/u.test(e);
85
+ }
86
+ //#endregion
87
+ export { n as buildPrep, i as collapseRepeat, t as foldedList, s as isQuestionText, o as normaliseSaid, r as removeFillers, a as stripEdges };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * ★ BOFF-8867: an emoji or symbol can carry the meaning — "🚫 dark mode", "dark
3
+ * mode 👎", "हाँ 🙄" — and the grammar cannot read it, so it must not strip it and
4
+ * act; nor can an ASCII emoticon ("dark mode :(", ">:(", "xD", "-_-"). Whitespace
5
+ * before the eyes, so "http://" and "10:30" are not faces. Shared by the
6
+ * English matcher, the per-language engine and the offer "yes".
7
+ */
8
+ export declare function carriesSymbolOrEmoticon(said: string): boolean;
@@ -0,0 +1,6 @@
1
+ //#region src/shared/assistant/localeCommands/symbols.ts
2
+ function e(e) {
3
+ return /[\p{Extended_Pictographic}\p{So}]/u.test(e) ? !0 : /(?:^|\s)>?[:;=][-'^]?[()[\]/\\|pPdDoO3*@$]+(?=\s|$)/.test(e) || /(?:^|\s)(?:xD|XD|-_-|T_T|<\/?3)(?=\s|$)/.test(e);
4
+ }
5
+ //#endregion
6
+ export { e as carriesSymbolOrEmoticon };
@@ -0,0 +1,10 @@
1
+ import { LocaleTable } from '../types';
2
+ /**
3
+ * Arabic (Modern Standard with the common colloquial imperatives). Arabic stays
4
+ * left-to-right in layout (operator decision 2026-10-07); matching is on the
5
+ * logical string. AI-written, no native review; every phrase is a seed the
6
+ * blind set measures. The fold removes tashkeel and unifies the alef, ya, ta
7
+ * marbuta and hamza forms on both sides, and a leading definite article is
8
+ * optional on the name (`lookup`, design §2.3).
9
+ */
10
+ export declare const ARABIC_COMMANDS: LocaleTable;