@ai-matrx/records 0.95.1 → 0.96.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/{client-DQWdJeFM.d.ts → client-D7bvxEZW.d.ts} +9 -0
- package/dist/{client-CEX4WdvX.d.cts → client-DKgbZUNt.d.cts} +9 -0
- package/dist/core/index.cjs +4 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +3 -2
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -1
- package/dist/data/index.cjs +4 -0
- package/dist/data/index.cjs.map +1 -1
- package/dist/data/index.d.cts +1 -1
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +4 -0
- package/dist/data/index.js.map +1 -1
- package/dist/{declareTable-Bkh3C1Ao.d.ts → declareTable-D_CiMnXM.d.ts} +1 -1
- package/dist/{declareTable-DeQ9_TMb.d.cts → declareTable-VoNRi_j_.d.cts} +1 -1
- package/dist/questions/index.d.cts +1 -1
- package/dist/questions/index.d.ts +1 -1
- package/dist/react/index.cjs +4 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +9 -0
- package/dist/react/index.d.ts +9 -0
- package/dist/react/index.js +4 -0
- package/dist/react/index.js.map +1 -1
- package/dist/setup/index.cjs +4 -0
- package/dist/setup/index.cjs.map +1 -1
- package/dist/setup/index.d.cts +1 -1
- package/dist/setup/index.d.ts +1 -1
- package/dist/setup/index.js +4 -0
- package/dist/setup/index.js.map +1 -1
- package/dist/templates/index.cjs +124 -65
- package/dist/templates/index.cjs.map +1 -1
- package/dist/templates/index.d.cts +6 -1
- package/dist/templates/index.d.ts +6 -1
- package/dist/templates/index.js +124 -65
- package/dist/templates/index.js.map +1 -1
- package/dist/typed-table/index.cjs +4 -0
- package/dist/typed-table/index.cjs.map +1 -1
- package/dist/typed-table/index.d.cts +2 -2
- package/dist/typed-table/index.d.ts +2 -2
- package/dist/typed-table/index.js +4 -0
- package/dist/typed-table/index.js.map +1 -1
- package/dist/views/index.d.cts +1 -1
- package/dist/views/index.d.ts +1 -1
- package/package.json +2 -2
package/dist/templates/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ __export(templates_exports, {
|
|
|
28
28
|
CHOICE_COLORS: () => CHOICE_COLORS,
|
|
29
29
|
CHOICE_COLORS_DISTINCT_UP_TO: () => CHOICE_COLORS_DISTINCT_UP_TO,
|
|
30
30
|
DEFAULT_TEMPLATE_AGENT: () => DEFAULT_TEMPLATE_AGENT,
|
|
31
|
+
DESCRIBE_RESULT_KIND: () => DESCRIBE_RESULT_KIND,
|
|
31
32
|
DESIGNATED_REAL_NUMBERS: () => DESIGNATED_REAL_NUMBERS,
|
|
32
33
|
ENTITY_REFERENCE_KINDS: () => ENTITY_REFERENCE_KINDS,
|
|
33
34
|
FORMULA_FUNCTION_NAMES: () => FORMULA_FUNCTION_NAMES,
|
|
@@ -85,6 +86,7 @@ __export(templates_exports, {
|
|
|
85
86
|
deferredRelations: () => deferredRelations,
|
|
86
87
|
derivedFieldSpec: () => derivedFieldSpec,
|
|
87
88
|
describeCheck: () => describeCheck,
|
|
89
|
+
describeResultJsonSchema: () => describeResultJsonSchema,
|
|
88
90
|
describeSpec: () => describeSpec,
|
|
89
91
|
describeVocabulary: () => describeVocabulary,
|
|
90
92
|
digestSpec: () => digestSpec,
|
|
@@ -113,6 +115,7 @@ __export(templates_exports, {
|
|
|
113
115
|
numberedStreet: () => numberedStreet,
|
|
114
116
|
packageDescribeResult: () => packageDescribeResult,
|
|
115
117
|
parseAt: () => parseAt,
|
|
118
|
+
parseDescribeTemplate: () => parseDescribeTemplate,
|
|
116
119
|
parseGateFormula: () => parseGateFormula,
|
|
117
120
|
parseNumeric: () => parseNumeric,
|
|
118
121
|
pathOfAt: () => pathOfAt,
|
|
@@ -1861,9 +1864,9 @@ var ACCOUNT_DOOR_PATTERNS = [
|
|
|
1861
1864
|
instead: "Call create_fixture_user / fixture_user (aidream/aidream/testing/persona.py) or createFixtureUser / withFixtureUser (scripts/lib/persona.mjs). A rolled-back raw insert is allowed with a trailing matrx-fixture:rollback-only <reason> on the same line."
|
|
1862
1865
|
}
|
|
1863
1866
|
];
|
|
1864
|
-
function findPlaceholders(
|
|
1867
|
+
function findPlaceholders(text2) {
|
|
1865
1868
|
const hits = [];
|
|
1866
|
-
const lines =
|
|
1869
|
+
const lines = text2.split("\n");
|
|
1867
1870
|
for (const line of lines) {
|
|
1868
1871
|
if (ALLOW_PRAGMA.test(line)) continue;
|
|
1869
1872
|
for (const pattern of PLACEHOLDER_PATTERNS) {
|
|
@@ -2729,8 +2732,8 @@ function bookingColumnsProblem(tables, booking) {
|
|
|
2729
2732
|
if (clash) return `The booking page adds a "${label}" column to "${t.name}", which already has one ("${clash.key}"). Rename that column, or key it "${key}".`;
|
|
2730
2733
|
}
|
|
2731
2734
|
const status = t.fields.find((f) => f.key === "status");
|
|
2732
|
-
const
|
|
2733
|
-
if (status && (!
|
|
2735
|
+
const words2 = new Set((status?.choices ?? []).map((c) => c.toLowerCase()));
|
|
2736
|
+
if (status && (!words2.has("booked") || !words2.has("cancelled"))) {
|
|
2734
2737
|
return `The booking page writes "booked" and "cancelled" into "${t.name}"'s status, and its choices do not hold both.`;
|
|
2735
2738
|
}
|
|
2736
2739
|
return null;
|
|
@@ -2848,8 +2851,8 @@ var q = (n, d) => {
|
|
|
2848
2851
|
};
|
|
2849
2852
|
var qAdd = (a, b) => q(a.n * b.d + b.n * a.d, a.d * b.d);
|
|
2850
2853
|
var qCmp = (a, b) => a.n * b.d === b.n * a.d ? 0 : a.n * b.d < b.n * a.d ? -1 : 1;
|
|
2851
|
-
function parseNumeric(
|
|
2852
|
-
const m = /^\s*([+-]?)(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?\s*$/.exec(
|
|
2854
|
+
function parseNumeric(text2) {
|
|
2855
|
+
const m = /^\s*([+-]?)(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?\s*$/.exec(text2);
|
|
2853
2856
|
if (!m || !m[2] && !m[3]) return null;
|
|
2854
2857
|
const frac = m[3] ?? "";
|
|
2855
2858
|
let n = BigInt(`${m[1] === "-" ? "-" : ""}${m[2] || "0"}${frac}` || "0");
|
|
@@ -2899,9 +2902,9 @@ function unwrap(v) {
|
|
|
2899
2902
|
return v && typeof v === "object" && !Array.isArray(v) && "value" in v ? unwrap(v.value) : v;
|
|
2900
2903
|
}
|
|
2901
2904
|
function boundMoment(raw) {
|
|
2902
|
-
const
|
|
2903
|
-
if (!
|
|
2904
|
-
const at = Date.parse(/^\d{4}-\d{2}-\d{2}$/.test(
|
|
2905
|
+
const text2 = raw.trim();
|
|
2906
|
+
if (!text2) return null;
|
|
2907
|
+
const at = Date.parse(/^\d{4}-\d{2}-\d{2}$/.test(text2) ? `${text2}T00:00:00Z` : text2);
|
|
2905
2908
|
return Number.isNaN(at) ? null : at;
|
|
2906
2909
|
}
|
|
2907
2910
|
function storeFilterMatches(row, filter, _timeZone) {
|
|
@@ -2939,8 +2942,8 @@ function toFx(n) {
|
|
|
2939
2942
|
return { k: "call", f: n.name, args: n.args.map(toFx) };
|
|
2940
2943
|
}
|
|
2941
2944
|
}
|
|
2942
|
-
function parseGateFormula(
|
|
2943
|
-
const parsed = (0, import_formula.parseFormula)(
|
|
2945
|
+
function parseGateFormula(text2) {
|
|
2946
|
+
const parsed = (0, import_formula.parseFormula)(text2);
|
|
2944
2947
|
return parsed.ok ? toFx(parsed.ast) : parsed.error;
|
|
2945
2948
|
}
|
|
2946
2949
|
function formulaRefs(n) {
|
|
@@ -3290,8 +3293,8 @@ function capabilitiesUsed(spec) {
|
|
|
3290
3293
|
}
|
|
3291
3294
|
var DESIGNATED_REAL_NUMBERS = ["9498072145", "9496662578", "9497027626"];
|
|
3292
3295
|
var DIGIT_ZEROS = [1632, 1776, 1984, 2406, 2534, 2662, 2790, 2918, 3046, 3174, 3302, 3430, 3664, 3792, 3872, 4160, 6112, 6160, 65296];
|
|
3293
|
-
function normalizeForPhones(
|
|
3294
|
-
return
|
|
3296
|
+
function normalizeForPhones(text2) {
|
|
3297
|
+
return text2.normalize("NFKC").replace(/\p{Nd}/gu, (c) => {
|
|
3295
3298
|
const cp = c.codePointAt(0) ?? 48;
|
|
3296
3299
|
const zero = DIGIT_ZEROS.find((z) => cp >= z && cp <= z + 9);
|
|
3297
3300
|
return zero === void 0 ? c : String(cp - zero);
|
|
@@ -3305,8 +3308,8 @@ function nanpBad(area, exchange, line) {
|
|
|
3305
3308
|
const n = Number(line);
|
|
3306
3309
|
return exchange !== "555" || n < 100 || n > 199 || DESIGNATED_REAL_NUMBERS.includes(`${area}${exchange}${line}`);
|
|
3307
3310
|
}
|
|
3308
|
-
function phonesOutsideFictionalRange(
|
|
3309
|
-
const t = normalizeForPhones(
|
|
3311
|
+
function phonesOutsideFictionalRange(text2, phoneField = false) {
|
|
3312
|
+
const t = normalizeForPhones(text2);
|
|
3310
3313
|
const out = [];
|
|
3311
3314
|
for (const m of t.matchAll(INTL)) {
|
|
3312
3315
|
const digits = m[0].replace(/\D/g, "");
|
|
@@ -3360,11 +3363,11 @@ var PUBLIC_MAIL_DOMAINS = [
|
|
|
3360
3363
|
"163.com",
|
|
3361
3364
|
"web.de"
|
|
3362
3365
|
];
|
|
3363
|
-
function domainsIn(
|
|
3366
|
+
function domainsIn(text2) {
|
|
3364
3367
|
const found = /* @__PURE__ */ new Set();
|
|
3365
|
-
for (const m of
|
|
3366
|
-
for (const m of
|
|
3367
|
-
for (const m of
|
|
3368
|
+
for (const m of text2.matchAll(/[a-z0-9._%+-]+@([a-z0-9-]+(?:\.[a-z0-9-]+)+)/gi)) found.add((m[1] ?? "").toLowerCase());
|
|
3369
|
+
for (const m of text2.matchAll(/\bhttps?:\/\/([a-z0-9-]+(?:\.[a-z0-9-]+)+)/gi)) found.add((m[1] ?? "").toLowerCase().replace(/^www\./, ""));
|
|
3370
|
+
for (const m of text2.matchAll(/\bwww\.([a-z0-9-]+(?:\.[a-z0-9-]+)+)/gi)) found.add((m[1] ?? "").toLowerCase().replace(/\.$/, ""));
|
|
3368
3371
|
return [...found];
|
|
3369
3372
|
}
|
|
3370
3373
|
function numberedStreet(line) {
|
|
@@ -3450,7 +3453,7 @@ function peopleOf(spec) {
|
|
|
3450
3453
|
function proseOf(spec) {
|
|
3451
3454
|
return templateText(spec).filter(([at]) => !at.startsWith("tables ("));
|
|
3452
3455
|
}
|
|
3453
|
-
var words8 = (
|
|
3456
|
+
var words8 = (text2) => text2.toLowerCase().replace(/\{\{[^}]*\}\}/g, " ").split(/[^a-z0-9']+/).filter(Boolean);
|
|
3454
3457
|
var FOREIGN_INDEX = /* @__PURE__ */ new WeakMap();
|
|
3455
3458
|
function foreignIndexOf(other) {
|
|
3456
3459
|
let ix = FOREIGN_INDEX.get(other);
|
|
@@ -3470,8 +3473,8 @@ function foreignIndexOf(other) {
|
|
|
3470
3473
|
}
|
|
3471
3474
|
function foreignTextProblems(spec, library) {
|
|
3472
3475
|
const problems = [];
|
|
3473
|
-
const
|
|
3474
|
-
const hay =
|
|
3476
|
+
const text2 = templateText(spec);
|
|
3477
|
+
const hay = text2.map(([at, v]) => [at, v.toLowerCase()]);
|
|
3475
3478
|
const all = hay.map(([, v]) => v).join("\n");
|
|
3476
3479
|
const esc = (w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3477
3480
|
const find = (word) => {
|
|
@@ -3481,9 +3484,9 @@ function foreignTextProblems(spec, library) {
|
|
|
3481
3484
|
return hay.find(([, v]) => re.test(v));
|
|
3482
3485
|
};
|
|
3483
3486
|
const mine = proseOf(spec).map(([at, w]) => {
|
|
3484
|
-
const
|
|
3487
|
+
const words2 = words8(w);
|
|
3485
3488
|
const runs = [];
|
|
3486
|
-
for (let i = 0; i + 8 <=
|
|
3489
|
+
for (let i = 0; i + 8 <= words2.length; i += 1) runs.push(words2.slice(i, i + 8).join(" "));
|
|
3487
3490
|
return [at, runs];
|
|
3488
3491
|
});
|
|
3489
3492
|
for (const other of library.specs) {
|
|
@@ -3518,8 +3521,8 @@ var SYNONYM_WORDS = {
|
|
|
3518
3521
|
function labelMeans(f, sameAs) {
|
|
3519
3522
|
const label = norm(f.label);
|
|
3520
3523
|
if (sameAs.includes(label)) return true;
|
|
3521
|
-
const
|
|
3522
|
-
return Object.values(SYNONYM_WORDS).some((group) => group.some((w) =>
|
|
3524
|
+
const words2 = new Set(label.split(" "));
|
|
3525
|
+
return Object.values(SYNONYM_WORDS).some((group) => group.some((w) => words2.has(w)) && sameAs.some((s) => group.some((w) => s.split(" ").includes(w))));
|
|
3523
3526
|
}
|
|
3524
3527
|
var sameJson = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
3525
3528
|
var sortedJson = (v) => JSON.stringify(Object.keys(v).sort().map((k) => [k, v[k]]));
|
|
@@ -4111,8 +4114,8 @@ function validateTemplate(raw, options = {}) {
|
|
|
4111
4114
|
for (const by of dim.default?.by ?? []) if (!dimKeys.has(by.split(":")[0] ?? "")) say(`${at}.default.by`, `The first screen groups by "${by}", which is not a dimension.`);
|
|
4112
4115
|
for (const show of dim.default?.show ?? []) if (!inferredMeasures.has(show) && !newMeasures.has(show)) say(`${at}.default.show`, `The first screen shows "${show}", which is not a measure of this table.`);
|
|
4113
4116
|
});
|
|
4114
|
-
const checkMerge = (at, table,
|
|
4115
|
-
for (const m of
|
|
4117
|
+
const checkMerge = (at, table, text3) => {
|
|
4118
|
+
for (const m of text3.matchAll(MERGE_TOKEN)) {
|
|
4116
4119
|
const key = m[1] ?? "";
|
|
4117
4120
|
const f = fieldOf(table, key);
|
|
4118
4121
|
if (!f) say(at, `{{${key}}} does not resolve: the store merges only the fields of "${table}" (no today, no business details).`);
|
|
@@ -5020,7 +5023,7 @@ function validateTemplate(raw, options = {}) {
|
|
|
5020
5023
|
const f = fieldOf(table, key);
|
|
5021
5024
|
if (f?.absoluteDates) say(`tables (${table}).fields (${key})`, `"${f.label}" holds history (absoluteDates), so it cannot drive ${where}: schedule dates are relative to install day.`);
|
|
5022
5025
|
}
|
|
5023
|
-
const
|
|
5026
|
+
const text2 = JSON.stringify(raw);
|
|
5024
5027
|
const isPhoneField = (f) => f.parityType === "phone" || /(^|_)(phone|mobile|cell|fax|tel)(_|$)/.test(f.key) || /\b(phone|mobile|cell|fax)\b/.test(norm(f.label));
|
|
5025
5028
|
const phoneHits = /* @__PURE__ */ new Set();
|
|
5026
5029
|
for (const p of phonesOutsideFictionalRange(raw.business?.phone ?? "", true)) phoneHits.add(p);
|
|
@@ -5038,7 +5041,7 @@ function validateTemplate(raw, options = {}) {
|
|
|
5038
5041
|
for (const d of raw.provenance?.fictionalDomains ?? []) {
|
|
5039
5042
|
if (PUBLIC_MAIL_DOMAINS.includes(d.toLowerCase())) say("provenance.fictionalDomains", `"${d}" is a public mail provider real people use, so it is never fictional. Give each person a mailbox on an invented domain.`);
|
|
5040
5043
|
}
|
|
5041
|
-
for (const domain of /* @__PURE__ */ new Set([...domainsIn(
|
|
5044
|
+
for (const domain of /* @__PURE__ */ new Set([...domainsIn(text2), ...websites])) {
|
|
5042
5045
|
if (!declaredDomains.has(domain)) say("provenance.fictionalDomains", `"${domain}" is used but not declared fictional. List every invented domain in provenance.fictionalDomains (each is checked not to resolve).`);
|
|
5043
5046
|
}
|
|
5044
5047
|
const streets = new Set((raw.provenance?.fictionalStreets ?? []).map((s) => s.toLowerCase()));
|
|
@@ -5081,10 +5084,10 @@ function validateTemplate(raw, options = {}) {
|
|
|
5081
5084
|
if (seeded) say(`tables (${t.token}).fields (${f.key})`, `"${f.label}" is computed by the store (${f.parityType}), and ${seeded} seed row(s) write a value into it; leave it to the store.`);
|
|
5082
5085
|
}
|
|
5083
5086
|
}
|
|
5084
|
-
const label = (at,
|
|
5085
|
-
if (!
|
|
5086
|
-
const hit = findBannedLabel(
|
|
5087
|
-
if (hit) say(at, `"${
|
|
5087
|
+
const label = (at, text3, kind) => {
|
|
5088
|
+
if (!text3) return;
|
|
5089
|
+
const hit = findBannedLabel(text3, kind);
|
|
5090
|
+
if (hit) say(at, `"${text3}" is ${hit.why}. Use the practitioner's own word.`);
|
|
5088
5091
|
};
|
|
5089
5092
|
for (const t of own) {
|
|
5090
5093
|
label(`tables (${t.token}).name`, t.name, "table");
|
|
@@ -5117,9 +5120,9 @@ function validateTemplate(raw, options = {}) {
|
|
|
5117
5120
|
for (const [k, v] of Object.entries(r.values ?? {})) {
|
|
5118
5121
|
const f = byKey.get(k);
|
|
5119
5122
|
if (!f || isRelation(f) || f.parityType === "email" || f.parityType === "url" || f.parityType === "phone") continue;
|
|
5120
|
-
const
|
|
5121
|
-
if (typeof
|
|
5122
|
-
say(`tables (${t.token}).rows (${r.key}).${k}`, `"${
|
|
5123
|
+
const text3 = unwrap(v);
|
|
5124
|
+
if (typeof text3 === "string" && !RELATIVE_DATE.test(text3) && STALE.test(text3)) {
|
|
5125
|
+
say(`tables (${t.token}).rows (${r.key}).${k}`, `"${text3}" says "${STALE.exec(text3)?.[0]}", which is only true on the day it was written. Put the date in a date field written relative to install day ("@today+1b 10:00"), and keep the text free of "today" and "next week".`);
|
|
5123
5126
|
}
|
|
5124
5127
|
}
|
|
5125
5128
|
}
|
|
@@ -5127,14 +5130,14 @@ function validateTemplate(raw, options = {}) {
|
|
|
5127
5130
|
for (const t of own) {
|
|
5128
5131
|
for (const f of t.fields) {
|
|
5129
5132
|
const at = `tables (${t.token}).fields (${f.key})`;
|
|
5130
|
-
const
|
|
5133
|
+
const text3 = `${f.key} ${norm(f.label)}`;
|
|
5131
5134
|
const e = effective(t.token, f);
|
|
5132
5135
|
const floor = sensitivityFloorOf(t, f);
|
|
5133
5136
|
if (floor === "restricted" && !(e.sensitivity === "restricted" && e.contextPolicy === "exclude")) {
|
|
5134
5137
|
say(at, `"${f.label}" is the kind of value that is restricted and kept from the AI (restricted + exclude).`);
|
|
5135
5138
|
} else if (floor === "confidential" && SENSITIVITY_RANK.indexOf(e.sensitivity) < 2) {
|
|
5136
5139
|
say(at, `"${f.label}" reaches a person or a home, so it is at least confidential.`);
|
|
5137
|
-
} else if (/note/.test(
|
|
5140
|
+
} else if (/note/.test(text3) && e.sensitivity === "public") {
|
|
5138
5141
|
say(at, `"${f.label}" is somebody's notes; notes are never public.`);
|
|
5139
5142
|
}
|
|
5140
5143
|
}
|
|
@@ -5319,8 +5322,8 @@ function validateTemplate(raw, options = {}) {
|
|
|
5319
5322
|
const status2 = fieldOf(e.table, "status");
|
|
5320
5323
|
if (!status2) say(at, `A booking table needs a choice field keyed \`status\` whose choices include "booked" and "cancelled"; "${e.table}" has none. Add it, so a held time can be confirmed and given back.`);
|
|
5321
5324
|
else if (isChoice(status2)) {
|
|
5322
|
-
const
|
|
5323
|
-
const missing = ["booked", "cancelled"].filter((w) => !
|
|
5325
|
+
const words2 = (status2.choices ?? []).map((c) => c.trim().toLowerCase());
|
|
5326
|
+
const missing = ["booked", "cancelled"].filter((w) => !words2.includes(w));
|
|
5324
5327
|
if (missing.length) say(`${at}`, `This table's Status is a list of choices, and it has no choice called ${missing.join(" or ")} \u2014 the booking page writes those words (custom.booking_declare refuses).`);
|
|
5325
5328
|
}
|
|
5326
5329
|
const columns = bookingColumnsProblem(tables, e);
|
|
@@ -5440,9 +5443,9 @@ ${e.body}`.matchAll(MERGE_TOKEN)].map((m) => m[1]));
|
|
|
5440
5443
|
if (agent?.question?.expect) {
|
|
5441
5444
|
const qq = agent.question;
|
|
5442
5445
|
const ask = norm(qq.ask);
|
|
5443
|
-
const
|
|
5446
|
+
const words2 = (t) => norm(t).split(" ").filter((w) => w.length >= 4);
|
|
5444
5447
|
const askWords = new Set(ask.split(" "));
|
|
5445
|
-
const mentions = (t) => !!norm(t) && ask.includes(norm(t)) ||
|
|
5448
|
+
const mentions = (t) => !!norm(t) && ask.includes(norm(t)) || words2(t).some((w) => ask.includes(w.replace(/s$/, ""))) || words2(t).length === 0 && norm(t).split(" ").filter(Boolean).some((w) => w.length >= 3 && (askWords.has(w) || askWords.has(`${w}s`)));
|
|
5446
5449
|
for (const [k, v] of Object.entries(qq.expect.filter ?? {})) {
|
|
5447
5450
|
if (typeof v === "string" && !mentions(v) && !ask.includes(norm(v))) say("agent.question.ask", `It filters on ${k} = "${v}", and the question never says so.`);
|
|
5448
5451
|
}
|
|
@@ -5524,9 +5527,9 @@ ${e.body}`.matchAll(MERGE_TOKEN)].map((m) => m[1]));
|
|
|
5524
5527
|
if (!t) return false;
|
|
5525
5528
|
if (says.includes(norm(t))) return true;
|
|
5526
5529
|
const long = [...new Set(norm(t).split(" ").filter((w) => w.length >= 4))];
|
|
5527
|
-
const
|
|
5530
|
+
const words2 = long.length ? long : [...new Set(norm(t).split(" ").filter((w) => w.length >= 2))];
|
|
5528
5531
|
const said = new Set(says.split(" "));
|
|
5529
|
-
return
|
|
5532
|
+
return words2.length > 0 && words2.filter((w) => said.has(w) || said.has(`${w}s`)).length >= Math.min(2, words2.length);
|
|
5530
5533
|
};
|
|
5531
5534
|
if (!a) return;
|
|
5532
5535
|
if (a.kind === "move" || a.kind === "edit" || a.kind === "archive" || a.kind === "restore") {
|
|
@@ -5627,9 +5630,9 @@ ${e.body}`.matchAll(MERGE_TOKEN)].map((m) => m[1]));
|
|
|
5627
5630
|
const LABEL_MAX = 40;
|
|
5628
5631
|
const IMPLEMENTATION = /\b(?:the\s+)?(?:ai|a\.i\.|agent|assistant|llm|model)\b[^.]*\b(?:reads?|sees?|never|cannot|can't|won't|doesn't|does not|is kept)\b|\b(?:never|not)\b[^.]*\b(?:ai|agent)\b|\bthe store\b|\bthis field\b|\bthis column\b|\bthe booking page writes\b|\bwrites? (?:a |the )?(?:booked )?time here\b|\bcustom\.[a-z_]+|\{\{|\b[a-z]+_[a-z0-9_]+\b|\b(?:sum|avg|count|min|max)\s*\(|\s=\s/i;
|
|
5629
5632
|
const STAFF_VOICE = /\bwe\b|\bour system\b/i;
|
|
5630
|
-
const screen = (at,
|
|
5631
|
-
if (!
|
|
5632
|
-
const t =
|
|
5633
|
+
const screen = (at, text3, slot, opts = {}) => {
|
|
5634
|
+
if (!text3?.trim()) return;
|
|
5635
|
+
const t = text3.trim();
|
|
5633
5636
|
if (slot === "label" && (t.length > LABEL_MAX || t.split(/\s+/).length > 6)) say(at, `"${t}" is a label (${t.length} characters); keep it to a few words \u2014 the name of the thing, no explanation.`);
|
|
5634
5637
|
if (slot === "secondary" && (t.length > 60 || sentences(t) > 1)) say(at, `"${t}" is ${t.length} characters; help text is one line of at most 60, one sentence.`);
|
|
5635
5638
|
if (slot === "tooltip" && (t.length > 140 || sentences(t) > 1)) say(at, `"${t.slice(0, 60)}\u2026" is ${t.length} characters; a description is one sentence of at most 140.`);
|
|
@@ -6004,14 +6007,14 @@ var holdsPeople = (t) => t.subject ? t.subject === "person" : t.fields.some((f)
|
|
|
6004
6007
|
var GOOD_OUTCOME = /^(completed?|done|paid|approved|won|closed won|booked|confirmed|delivered|resolved|published|signed|passed|hired|accepted)$/i;
|
|
6005
6008
|
var BAD_OUTCOME = /^(cancell?ed|skipped|lost|closed lost|denied|rejected|no[- ]show|failed|overdue|declined|void(ed)?|refunded|churned|expired|blocked)$/i;
|
|
6006
6009
|
function sensitivityFloorOf(table, f) {
|
|
6007
|
-
const
|
|
6010
|
+
const text2 = `${f.key} ${norm(f.label)}`;
|
|
6008
6011
|
const billingTable = /(invoice|bill|statement|receipt|payment|charge|fee|order|account)/i.test(`${table.token} ${table.labelSingular ?? ""}`);
|
|
6009
6012
|
const criminalCharges = /(^|_)charges?($|_)/.test(f.key) && behaviorOf(f) === "text" && !billingTable;
|
|
6010
6013
|
const phoneLike = behaviorOf(f) === "text" && /\b(cell|mobile|phone)\b/.test(norm(f.label));
|
|
6011
6014
|
const personal = PERSONAL_ONLY.test(f.key) || /\b(medical conditions?|medications?|diagnos[ie]s)\b/.test(norm(f.label));
|
|
6012
6015
|
const floored = (RESTRICTED_KEY.test(f.key) || RESTRICTED_LABEL.test(norm(f.label)) || criminalCharges) && (!personal || holdsPeople(table));
|
|
6013
6016
|
if (floored) return "restricted";
|
|
6014
|
-
if (f.parityType === "phone" || phoneLike || f.parityType === "email" || /address|street/.test(
|
|
6017
|
+
if (f.parityType === "phone" || phoneLike || f.parityType === "email" || /address|street/.test(text2)) return "confidential";
|
|
6015
6018
|
return null;
|
|
6016
6019
|
}
|
|
6017
6020
|
|
|
@@ -6497,6 +6500,7 @@ function describeSpec(template) {
|
|
|
6497
6500
|
s.specVersion = 1;
|
|
6498
6501
|
if (typeof s.version !== "number") s.version = 1;
|
|
6499
6502
|
if (typeof s.audience !== "string") s.audience = "organization";
|
|
6503
|
+
if (s.audience === "personal") s.audience = "individual";
|
|
6500
6504
|
if (typeof s.cleanupTag !== "string" && typeof s.id === "string") s.cleanupTag = `describe:${s.id}`;
|
|
6501
6505
|
for (const k of ["relationships", "sharedBlocks", "views", "forms", "dimensions", "extras"]) if (!Array.isArray(s[k])) s[k] = [];
|
|
6502
6506
|
if (Array.isArray(s.tables)) s.tables = s.tables.map(describeTable);
|
|
@@ -6539,9 +6543,9 @@ function stemWord(w) {
|
|
|
6539
6543
|
if (s.length > 4 && (s.endsWith("y") || s.endsWith("e"))) s = s.slice(0, -1);
|
|
6540
6544
|
return s;
|
|
6541
6545
|
}
|
|
6542
|
-
function meaningWords(
|
|
6543
|
-
const
|
|
6544
|
-
return [...new Set(
|
|
6546
|
+
function meaningWords(text2) {
|
|
6547
|
+
const words2 = text2.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w.toLowerCase()).filter((w) => !FILLER.has(w)).map(stemWord).filter(Boolean);
|
|
6548
|
+
return [...new Set(words2)].sort();
|
|
6545
6549
|
}
|
|
6546
6550
|
var same = (a, b) => a.length > 0 && a.length === b.length && a.every((w, i) => w === b[i]);
|
|
6547
6551
|
function sameMeaning(a, b) {
|
|
@@ -7081,31 +7085,31 @@ function fixFormulaRefs(spec, fixes) {
|
|
|
7081
7085
|
if (f.parityType !== "formula" || typeof f.formula !== "string") continue;
|
|
7082
7086
|
const others = t.fields.filter((x) => x.key !== f.key);
|
|
7083
7087
|
const labelFor = (name) => others.find((x) => x.label === name)?.label ?? others.find((x) => x.key === name)?.label ?? others.find((x) => same3(x.key, name))?.label ?? others.find((x) => same3(x.label, name))?.label;
|
|
7084
|
-
const
|
|
7088
|
+
const text2 = f.formula.replace(/^\s*=\s*/, "");
|
|
7085
7089
|
let out = "";
|
|
7086
7090
|
let i = 0;
|
|
7087
|
-
while (i <
|
|
7088
|
-
const c =
|
|
7091
|
+
while (i < text2.length) {
|
|
7092
|
+
const c = text2[i];
|
|
7089
7093
|
if (c === "{") {
|
|
7090
|
-
const j =
|
|
7094
|
+
const j = text2.indexOf("}", i);
|
|
7091
7095
|
if (j < 0) {
|
|
7092
|
-
out +=
|
|
7096
|
+
out += text2.slice(i);
|
|
7093
7097
|
break;
|
|
7094
7098
|
}
|
|
7095
|
-
const name =
|
|
7099
|
+
const name = text2.slice(i + 1, j).trim();
|
|
7096
7100
|
out += `{${labelFor(name) ?? name}}`;
|
|
7097
7101
|
i = j + 1;
|
|
7098
7102
|
} else if (c === "'" || c === '"') {
|
|
7099
|
-
const j =
|
|
7103
|
+
const j = text2.indexOf(c, i + 1);
|
|
7100
7104
|
if (j < 0) {
|
|
7101
|
-
out +=
|
|
7105
|
+
out += text2.slice(i);
|
|
7102
7106
|
break;
|
|
7103
7107
|
}
|
|
7104
|
-
out +=
|
|
7108
|
+
out += text2.slice(i, j + 1);
|
|
7105
7109
|
i = j + 1;
|
|
7106
7110
|
} else if (/[A-Za-z_]/.test(c)) {
|
|
7107
|
-
const m = /^[A-Za-z_][A-Za-z_0-9]*/.exec(
|
|
7108
|
-
const after =
|
|
7111
|
+
const m = /^[A-Za-z_][A-Za-z_0-9]*/.exec(text2.slice(i))[0];
|
|
7112
|
+
const after = text2.slice(i + m.length).trimStart()[0];
|
|
7109
7113
|
const label = after === "(" ? void 0 : labelFor(m);
|
|
7110
7114
|
out += label ? `{${label}}` : m;
|
|
7111
7115
|
i += m.length;
|
|
@@ -7837,7 +7841,7 @@ function describeVocabulary() {
|
|
|
7837
7841
|
field_shape: FIELD_SHAPE_FACTS,
|
|
7838
7842
|
formula_shape: FORMULA_SHAPE_FACTS,
|
|
7839
7843
|
relationship_shape: RELATIONSHIP_SHAPE_FACTS,
|
|
7840
|
-
phone_rule: "seed phone numbers are fictional: 555-0100 to 555-0199
|
|
7844
|
+
phone_rule: "seed phone numbers are fictional: 555-0100 to 555-0199 (e.g. (813) 555-0142); never a real-looking number, and never a number from a country the person did not name.",
|
|
7841
7845
|
team_rule: "the team (name, role, work email, mobile, start date) is the shared team_member block; do not declare a table with those fields \u2014 keep a table of your own to just name and role, or point a relation field at team_member.",
|
|
7842
7846
|
title_field: "a table's titleField is the key of a plain text-like field of that same table (a name, a title) \u2014 never a link to another table, a formula or a timed date"
|
|
7843
7847
|
};
|
|
@@ -7900,4 +7904,59 @@ function safeReuses(spec, reuses, existingTables) {
|
|
|
7900
7904
|
const out = { ...spec, tables: spec.tables.map((t) => rename.has(t.token) ? { ...t, ...rename.get(t.token) } : t) };
|
|
7901
7905
|
return { spec: out, reuses: kept, notes, converted };
|
|
7902
7906
|
}
|
|
7907
|
+
|
|
7908
|
+
// src/templates/describe/result-schema.ts
|
|
7909
|
+
var DESCRIBE_RESULT_KIND = "describe_template_result";
|
|
7910
|
+
var text = { type: "string" };
|
|
7911
|
+
var words = (list) => ({ type: "string", enum: [...list] });
|
|
7912
|
+
var jsonText = { type: "string", description: "one JSON object, as text" };
|
|
7913
|
+
var jsonTextList = { type: "array", items: jsonText };
|
|
7914
|
+
var DESCRIBE_TEXT_PARTS = ["tables", "relationships", "views", "forms", "extras"];
|
|
7915
|
+
function describeResultJsonSchema() {
|
|
7916
|
+
return {
|
|
7917
|
+
type: "object",
|
|
7918
|
+
required: ["__kind", "template", "notes", "reuses"],
|
|
7919
|
+
properties: {
|
|
7920
|
+
__kind: { type: "string", enum: [DESCRIBE_RESULT_KIND] },
|
|
7921
|
+
template: {
|
|
7922
|
+
type: "object",
|
|
7923
|
+
required: ["id", "useCase", "industry", "vertical", "job", "teaches", "business", "tables", "forms"],
|
|
7924
|
+
properties: {
|
|
7925
|
+
id: text,
|
|
7926
|
+
useCase: text,
|
|
7927
|
+
industry: words(INDUSTRY_GROUPS),
|
|
7928
|
+
vertical: text,
|
|
7929
|
+
job: words(TEMPLATE_JOBS),
|
|
7930
|
+
audience: words(TEMPLATE_AUDIENCES),
|
|
7931
|
+
teaches: words(TEACHES_FEATURES),
|
|
7932
|
+
business: jsonText,
|
|
7933
|
+
tables: jsonTextList,
|
|
7934
|
+
relationships: jsonTextList,
|
|
7935
|
+
views: jsonTextList,
|
|
7936
|
+
forms: jsonTextList,
|
|
7937
|
+
extras: jsonTextList
|
|
7938
|
+
}
|
|
7939
|
+
},
|
|
7940
|
+
notes: { type: "array", items: text },
|
|
7941
|
+
reuses: { type: "array", items: { type: "object", required: ["token", "existing_table_id"], properties: { token: text, existing_table_id: text } } }
|
|
7942
|
+
}
|
|
7943
|
+
};
|
|
7944
|
+
}
|
|
7945
|
+
function open(value, at) {
|
|
7946
|
+
if (typeof value !== "string") return value;
|
|
7947
|
+
try {
|
|
7948
|
+
return JSON.parse(value);
|
|
7949
|
+
} catch {
|
|
7950
|
+
throw new Error(`${at} was not valid JSON text.`);
|
|
7951
|
+
}
|
|
7952
|
+
}
|
|
7953
|
+
function parseDescribeTemplate(template) {
|
|
7954
|
+
const out = { ...template };
|
|
7955
|
+
if ("business" in out) out.business = open(out.business, "business");
|
|
7956
|
+
for (const k of DESCRIBE_TEXT_PARTS) {
|
|
7957
|
+
const v = out[k];
|
|
7958
|
+
if (Array.isArray(v)) out[k] = v.map((x, i) => open(x, `${k}[${i}]`));
|
|
7959
|
+
}
|
|
7960
|
+
return out;
|
|
7961
|
+
}
|
|
7903
7962
|
//# sourceMappingURL=index.cjs.map
|