@lexq/cli 0.3.1 → 1.0.0
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/README.md +58 -2
- package/dist/index.js +174 -5
- package/dist/mcp/register.js +96 -1
- package/package.json +4 -2
- package/skills/lexq-recipes/SKILL.md +5 -1
- package/skills/lexq-shared/SKILL.md +1 -0
- package/skills/lexq-simulation/SKILL.md +12 -6
package/dist/index.js
CHANGED
|
@@ -72,6 +72,7 @@ var survivesDoubleRoundTrip = (literal) => isSafeNumber(literal);
|
|
|
72
72
|
var parseNumber = (value) => survivesDoubleRoundTrip(value) ? parseFloat(value) : new LosslessNumber(value);
|
|
73
73
|
var parseJson = (text) => losslessParse(text, void 0, parseNumber);
|
|
74
74
|
var stringifyJson = (value, space) => losslessStringify(value, void 0, space);
|
|
75
|
+
var isLosslessNumber = (value) => value instanceof LosslessNumber;
|
|
75
76
|
|
|
76
77
|
// src/lib/api-client.ts
|
|
77
78
|
var ApiError = class extends Error {
|
|
@@ -1338,7 +1339,7 @@ ${data.length} unregistered`);
|
|
|
1338
1339
|
process.exit(1);
|
|
1339
1340
|
}
|
|
1340
1341
|
});
|
|
1341
|
-
facts.command("create").description("Create a new fact definition").option("--key <key>", "Fact key (letters, numbers, underscores)").option("--name <n>", "Display name").option("--type <type>", "Value type: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER").option("--description <desc>", "Description").option("--required", "Mark as required", false).option("--pii", "Mark as PII \u2014 value is masked on every read surface", false).option("--json <body>", "Full request body as JSON (overrides other options)").addHelpText(
|
|
1342
|
+
facts.command("create").description("Create a new fact definition").option("--key <key>", "Fact key (letters, numbers, underscores)").option("--name <n>", "Display name").option("--type <type>", "Value type: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER").option("--description <desc>", "Description").option("--required", "Mark as required", false).option("--pii", "Mark as PII \u2014 value is masked on every read surface", false).option("--allowed-values <csv>", "Comma-separated list of accepted values").option("--min <number>", "Lower bound, inclusive (numeric types only)").option("--max <number>", "Upper bound, inclusive (numeric types only)").option("--json <body>", "Full request body as JSON (overrides other options)").addHelpText(
|
|
1342
1343
|
"after",
|
|
1343
1344
|
dedent6`
|
|
1344
1345
|
|
|
@@ -1354,9 +1355,21 @@ ${data.length} unregistered`);
|
|
|
1354
1355
|
"isRequired": false
|
|
1355
1356
|
}'
|
|
1356
1357
|
|
|
1358
|
+
$ lexq facts create --key riskScore --name "Risk Score" --type NUMBER --min 0 --max 100
|
|
1359
|
+
$ lexq facts create --key tier --name "Tier" --type STRING \
|
|
1360
|
+
--allowed-values "GOLD, SILVER, BRONZE"
|
|
1361
|
+
|
|
1357
1362
|
Value Types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER
|
|
1358
1363
|
Key Format: starts with a letter, then letters, numbers, and underscores.
|
|
1359
1364
|
Casing is yours to choose (e.g., paymentAmount, payment_amount)
|
|
1365
|
+
|
|
1366
|
+
Value Domain: --allowed-values, --min, and --max say which values the fact accepts.
|
|
1367
|
+
A rule can then only compare against those values, and the console turns
|
|
1368
|
+
the rule editor into a dropdown instead of a free text box.
|
|
1369
|
+
STRING types take --allowed-values. NUMBER types take all three.
|
|
1370
|
+
BOOLEAN takes none — it has only two values to begin with.
|
|
1371
|
+
For list types the constraint applies to each element.
|
|
1372
|
+
Leave them out for no constraint, which is how facts behaved before.
|
|
1360
1373
|
`
|
|
1361
1374
|
).action(async (opts) => {
|
|
1362
1375
|
try {
|
|
@@ -1375,14 +1388,25 @@ ${data.length} unregistered`);
|
|
|
1375
1388
|
process.exit(1);
|
|
1376
1389
|
}
|
|
1377
1390
|
});
|
|
1378
|
-
facts.command("update").description("Update a fact definition").requiredOption("--id <factId>", "Fact definition ID").option("--name <n>", "Display name").option("--description <desc>", "Description").option("--required", "Mark as required").option("--no-required", "Mark as not required").option("--pii", "Mark as PII (enables masking)").option("--no-pii", "Unmark as PII (disables masking \u2014 value becomes visible)").option("--json <body>", "Full request body as JSON (overrides other options)").addHelpText(
|
|
1391
|
+
facts.command("update").description("Update a fact definition").requiredOption("--id <factId>", "Fact definition ID").option("--name <n>", "Display name").option("--description <desc>", "Description").option("--required", "Mark as required").option("--no-required", "Mark as not required").option("--pii", "Mark as PII (enables masking)").option("--no-pii", "Unmark as PII (disables masking \u2014 value becomes visible)").option("--type <type>", "Value type: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER").option("--allowed-values <csv>", "Comma-separated list of accepted values").option("--min <number>", "Lower bound, inclusive (numeric types only)").option("--max <number>", "Upper bound, inclusive (numeric types only)").option("--clear-value-domain", "Remove the value constraint entirely").option("--json <body>", "Full request body as JSON (overrides other options)").addHelpText(
|
|
1379
1392
|
"after",
|
|
1380
1393
|
dedent6`
|
|
1381
1394
|
|
|
1382
|
-
System facts
|
|
1395
|
+
System facts accept name, description, and the PII flag only.
|
|
1383
1396
|
|
|
1384
|
-
|
|
1397
|
+
Examples:
|
|
1385
1398
|
$ lexq facts update --id <factId> --name "Updated Name" --required
|
|
1399
|
+
$ lexq facts update --id <factId> --min 0 --max 100
|
|
1400
|
+
$ lexq facts update --id <factId> --clear-value-domain
|
|
1401
|
+
|
|
1402
|
+
Only the flags you pass are changed. Leaving out --allowed-values, --min, and --max
|
|
1403
|
+
leaves the constraint alone; --clear-value-domain removes it.
|
|
1404
|
+
|
|
1405
|
+
Narrowing a constraint does not rewrite rules that already reference this fact. They keep
|
|
1406
|
+
working, and the next time one is saved it is judged against the new constraint.
|
|
1407
|
+
|
|
1408
|
+
Pass --type when the accepted values are numbers and you are not also naming the type,
|
|
1409
|
+
so "10" is sent as a number rather than a string. --json settles it either way.
|
|
1386
1410
|
`
|
|
1387
1411
|
).action(async (opts) => {
|
|
1388
1412
|
try {
|
|
@@ -1496,16 +1520,66 @@ function buildCreateBody2(opts) {
|
|
|
1496
1520
|
isPii: opts.pii === true
|
|
1497
1521
|
};
|
|
1498
1522
|
if (opts.description) body.description = opts.description;
|
|
1523
|
+
const domain = buildValueDomain(opts, opts.type);
|
|
1524
|
+
if (domain) body.valueDomain = domain;
|
|
1499
1525
|
return body;
|
|
1500
1526
|
}
|
|
1501
1527
|
function buildUpdateBody2(opts) {
|
|
1502
1528
|
const body = {};
|
|
1503
1529
|
if (opts.name) body.name = opts.name;
|
|
1504
1530
|
if (opts.description !== void 0) body.description = opts.description;
|
|
1531
|
+
if (opts.type) body.type = opts.type;
|
|
1505
1532
|
if (typeof opts.required === "boolean") body.isRequired = opts.required;
|
|
1506
1533
|
if (typeof opts.isPii === "boolean") body.isPii = opts.isPii;
|
|
1534
|
+
if (opts.clearValueDomain === true) {
|
|
1535
|
+
if (hasDomainFlag(opts)) {
|
|
1536
|
+
throw new Error(
|
|
1537
|
+
"--clear-value-domain cannot be combined with --allowed-values, --min, or --max."
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
body.valueDomain = {};
|
|
1541
|
+
return body;
|
|
1542
|
+
}
|
|
1543
|
+
const domain = buildValueDomain(opts, opts.type);
|
|
1544
|
+
if (domain) body.valueDomain = domain;
|
|
1507
1545
|
return body;
|
|
1508
1546
|
}
|
|
1547
|
+
function hasDomainFlag(opts) {
|
|
1548
|
+
return opts.allowedValues !== void 0 || opts.min !== void 0 || opts.max !== void 0;
|
|
1549
|
+
}
|
|
1550
|
+
function parseNumericLiteral(text, flag) {
|
|
1551
|
+
let parsed;
|
|
1552
|
+
try {
|
|
1553
|
+
parsed = parseJson(text);
|
|
1554
|
+
} catch {
|
|
1555
|
+
throw new Error(`${flag} must be a number, got: ${text}`);
|
|
1556
|
+
}
|
|
1557
|
+
if (typeof parsed !== "number" && !isLosslessNumber(parsed)) {
|
|
1558
|
+
throw new Error(`${flag} must be a number, got: ${text}`);
|
|
1559
|
+
}
|
|
1560
|
+
return parsed;
|
|
1561
|
+
}
|
|
1562
|
+
function asDomainValue(text, numeric) {
|
|
1563
|
+
if (numeric === true) return parseNumericLiteral(text, "--allowed-values");
|
|
1564
|
+
if (numeric === false) return text;
|
|
1565
|
+
try {
|
|
1566
|
+
const parsed = parseJson(text);
|
|
1567
|
+
return typeof parsed === "number" || isLosslessNumber(parsed) ? parsed : text;
|
|
1568
|
+
} catch {
|
|
1569
|
+
return text;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
function buildValueDomain(opts, type) {
|
|
1573
|
+
if (!hasDomainFlag(opts)) return void 0;
|
|
1574
|
+
const numeric = type === void 0 ? void 0 : type === "NUMBER" || type === "LIST_NUMBER";
|
|
1575
|
+
const domain = {};
|
|
1576
|
+
if (opts.allowedValues !== void 0) {
|
|
1577
|
+
domain.allowedValues = opts.allowedValues.split(",").map((part) => part.trim()).filter((part) => part.length > 0).map((part) => asDomainValue(part, numeric));
|
|
1578
|
+
}
|
|
1579
|
+
if (opts.min !== void 0) domain.min = parseNumericLiteral(opts.min, "--min");
|
|
1580
|
+
if (opts.max !== void 0) domain.max = parseNumericLiteral(opts.max, "--max");
|
|
1581
|
+
return domain;
|
|
1582
|
+
}
|
|
1509
1583
|
|
|
1510
1584
|
// src/commands/deploy.ts
|
|
1511
1585
|
import "commander";
|
|
@@ -3421,6 +3495,7 @@ function registerStatusTools(server, callApi) {
|
|
|
3421
3495
|
"lexq_whoami",
|
|
3422
3496
|
{
|
|
3423
3497
|
title: "Who Am I",
|
|
3498
|
+
annotations: { readOnlyHint: true },
|
|
3424
3499
|
description: "Show current authentication info (tenant ID, user ID, role).",
|
|
3425
3500
|
inputSchema: z.object({})
|
|
3426
3501
|
},
|
|
@@ -3435,6 +3510,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3435
3510
|
"lexq_groups_list",
|
|
3436
3511
|
{
|
|
3437
3512
|
title: "List Policy Groups",
|
|
3513
|
+
annotations: { readOnlyHint: true },
|
|
3438
3514
|
description: "List all policy groups (tenant-wide, priority ASC).",
|
|
3439
3515
|
inputSchema: z2.object({})
|
|
3440
3516
|
},
|
|
@@ -3444,6 +3520,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3444
3520
|
"lexq_groups_get",
|
|
3445
3521
|
{
|
|
3446
3522
|
title: "Get Policy Group",
|
|
3523
|
+
annotations: { readOnlyHint: true },
|
|
3447
3524
|
description: "Get a single policy group by ID.",
|
|
3448
3525
|
inputSchema: z2.object({
|
|
3449
3526
|
groupId: z2.string().uuid().describe("Policy group ID")
|
|
@@ -3455,6 +3532,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3455
3532
|
"lexq_groups_create",
|
|
3456
3533
|
{
|
|
3457
3534
|
title: "Create Policy Group",
|
|
3535
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
3458
3536
|
description: "Create a new policy group. Requires name. Priority is auto-assigned (appended last, tenant-wide); use lexq_groups_reorder to change order. Optionally set conflict resolution, activation group, and description. Policy groups that share an activationGroup form a cluster and must share the same activationMode / activationStrategy / executionLimit; executionLimit is how many of those groups run, not how many rules.",
|
|
3459
3537
|
inputSchema: z2.object({
|
|
3460
3538
|
name: z2.string().describe("Group name (unique among non-ARCHIVED)"),
|
|
@@ -3485,6 +3563,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3485
3563
|
"lexq_groups_update",
|
|
3486
3564
|
{
|
|
3487
3565
|
title: "Update Policy Group",
|
|
3566
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3488
3567
|
description: "Update a policy group. Only provided fields are updated; omitted fields remain unchanged.",
|
|
3489
3568
|
inputSchema: z2.object({
|
|
3490
3569
|
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
@@ -3507,6 +3586,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3507
3586
|
"lexq_groups_delete",
|
|
3508
3587
|
{
|
|
3509
3588
|
title: "Delete Policy Group",
|
|
3589
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3510
3590
|
description: "Archive a policy group. Only non-live groups can be deleted. This is irreversible.",
|
|
3511
3591
|
inputSchema: z2.object({
|
|
3512
3592
|
groupId: z2.string().uuid().describe("Policy group ID")
|
|
@@ -3518,6 +3598,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3518
3598
|
"lexq_groups_reorder",
|
|
3519
3599
|
{
|
|
3520
3600
|
title: "Reorder Policy Groups",
|
|
3601
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3521
3602
|
description: "Reorder policy groups by priority. Priority is tenant-wide and flat (1...N continuous); array index 0 = priority 1 (highest precedence). activationGroup is not affected \u2014 this only changes priority.",
|
|
3522
3603
|
inputSchema: z2.object({
|
|
3523
3604
|
groupIds: z2.array(z2.string().uuid()).describe("Group IDs in desired priority order (index 0 = priority 1)")
|
|
@@ -3535,6 +3616,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3535
3616
|
"lexq_ab_test_start",
|
|
3536
3617
|
{
|
|
3537
3618
|
title: "Start A/B Test",
|
|
3619
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
3538
3620
|
description: "Start an A/B test on a policy group. Requires a challenger version ID and traffic rate. The split is computed from context.trafficKey on each execution request; requests that omit it never reach the challenger and the test stays at 0%.",
|
|
3539
3621
|
inputSchema: z2.object({
|
|
3540
3622
|
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
@@ -3548,6 +3630,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3548
3630
|
"lexq_ab_test_stop",
|
|
3549
3631
|
{
|
|
3550
3632
|
title: "Stop A/B Test",
|
|
3633
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3551
3634
|
description: "Stop a running A/B test. All traffic is restored to the control (current) version.",
|
|
3552
3635
|
inputSchema: z2.object({
|
|
3553
3636
|
groupId: z2.string().uuid().describe("Policy group ID")
|
|
@@ -3559,6 +3642,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3559
3642
|
"lexq_ab_test_adjust",
|
|
3560
3643
|
{
|
|
3561
3644
|
title: "Adjust A/B Test",
|
|
3645
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3562
3646
|
description: "Adjust traffic rate of a running A/B test.",
|
|
3563
3647
|
inputSchema: z2.object({
|
|
3564
3648
|
groupId: z2.string().uuid().describe("Policy group ID"),
|
|
@@ -3576,6 +3660,7 @@ function registerVersionTools(server, callApi) {
|
|
|
3576
3660
|
"lexq_versions_list",
|
|
3577
3661
|
{
|
|
3578
3662
|
title: "List Policy Versions",
|
|
3663
|
+
annotations: { readOnlyHint: true },
|
|
3579
3664
|
description: "List all versions of a policy group.",
|
|
3580
3665
|
inputSchema: z3.object({
|
|
3581
3666
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -3591,6 +3676,7 @@ function registerVersionTools(server, callApi) {
|
|
|
3591
3676
|
"lexq_versions_get",
|
|
3592
3677
|
{
|
|
3593
3678
|
title: "Get Policy Version",
|
|
3679
|
+
annotations: { readOnlyHint: true },
|
|
3594
3680
|
description: "Get a single version by ID, including its rules and fact requirements.",
|
|
3595
3681
|
inputSchema: z3.object({
|
|
3596
3682
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -3603,6 +3689,7 @@ function registerVersionTools(server, callApi) {
|
|
|
3603
3689
|
"lexq_versions_create",
|
|
3604
3690
|
{
|
|
3605
3691
|
title: "Create Policy Version",
|
|
3692
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
3606
3693
|
description: "Create a new DRAFT version in a policy group. Optionally provide a commit message and effective date range.",
|
|
3607
3694
|
inputSchema: z3.object({
|
|
3608
3695
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -3617,6 +3704,7 @@ function registerVersionTools(server, callApi) {
|
|
|
3617
3704
|
"lexq_versions_update",
|
|
3618
3705
|
{
|
|
3619
3706
|
title: "Update Policy Version",
|
|
3707
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3620
3708
|
description: "Update a DRAFT version. Only DRAFT versions can be modified. Only provided fields are changed.",
|
|
3621
3709
|
inputSchema: z3.object({
|
|
3622
3710
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -3632,6 +3720,7 @@ function registerVersionTools(server, callApi) {
|
|
|
3632
3720
|
"lexq_versions_delete",
|
|
3633
3721
|
{
|
|
3634
3722
|
title: "Delete Policy Version",
|
|
3723
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3635
3724
|
description: "Delete a DRAFT version. Only DRAFT versions can be deleted.",
|
|
3636
3725
|
inputSchema: z3.object({
|
|
3637
3726
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -3644,6 +3733,7 @@ function registerVersionTools(server, callApi) {
|
|
|
3644
3733
|
"lexq_versions_clone",
|
|
3645
3734
|
{
|
|
3646
3735
|
title: "Clone Policy Version",
|
|
3736
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
3647
3737
|
description: "Clone an existing version to create a new DRAFT. Useful when the source version is already published.",
|
|
3648
3738
|
inputSchema: z3.object({
|
|
3649
3739
|
groupId: z3.string().uuid().describe("Policy group ID"),
|
|
@@ -3666,6 +3756,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3666
3756
|
"lexq_rules_list",
|
|
3667
3757
|
{
|
|
3668
3758
|
title: "List Rules",
|
|
3759
|
+
annotations: { readOnlyHint: true },
|
|
3669
3760
|
description: "List all rules in a version (priority ASC). Returns summary with conditionSummary and actionSummary.",
|
|
3670
3761
|
inputSchema: z4.object({
|
|
3671
3762
|
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
@@ -3678,6 +3769,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3678
3769
|
"lexq_rules_get",
|
|
3679
3770
|
{
|
|
3680
3771
|
title: "Get Rule Detail",
|
|
3772
|
+
annotations: { readOnlyHint: true },
|
|
3681
3773
|
description: "Get full rule detail including condition tree and action definitions.",
|
|
3682
3774
|
inputSchema: z4.object({
|
|
3683
3775
|
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
@@ -3691,6 +3783,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3691
3783
|
"lexq_rules_create",
|
|
3692
3784
|
{
|
|
3693
3785
|
title: "Create Rule",
|
|
3786
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
3694
3787
|
description: dedent15`
|
|
3695
3788
|
Create a rule in a DRAFT version. Requires name, condition tree, and actions array. priority is auto-assigned (appended last); use lexq_rules_reorder to change order.
|
|
3696
3789
|
|
|
@@ -3769,6 +3862,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3769
3862
|
"lexq_rules_update",
|
|
3770
3863
|
{
|
|
3771
3864
|
title: "Update Rule",
|
|
3865
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3772
3866
|
description: "Update an existing rule in a DRAFT version. Only provided fields are changed.",
|
|
3773
3867
|
inputSchema: z4.object({
|
|
3774
3868
|
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
@@ -3790,6 +3884,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3790
3884
|
"lexq_rules_delete",
|
|
3791
3885
|
{
|
|
3792
3886
|
title: "Delete Rule",
|
|
3887
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3793
3888
|
description: "Delete a rule from a DRAFT version.",
|
|
3794
3889
|
inputSchema: z4.object({
|
|
3795
3890
|
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
@@ -3810,6 +3905,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3810
3905
|
"lexq_rules_reorder",
|
|
3811
3906
|
{
|
|
3812
3907
|
title: "Reorder Rules",
|
|
3908
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3813
3909
|
description: "Reorder rules by specifying rule IDs in desired order. Priorities are assigned 1...N (1-based, continuous); array index 0 = priority 1 (highest precedence).",
|
|
3814
3910
|
inputSchema: z4.object({
|
|
3815
3911
|
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
@@ -3831,6 +3927,7 @@ function registerRuleTools(server, callApi) {
|
|
|
3831
3927
|
"lexq_rules_toggle",
|
|
3832
3928
|
{
|
|
3833
3929
|
title: "Toggle Rule",
|
|
3930
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3834
3931
|
description: "Enable or disable a rule without deleting it.",
|
|
3835
3932
|
inputSchema: z4.object({
|
|
3836
3933
|
groupId: z4.string().uuid().describe("Policy group ID"),
|
|
@@ -3858,6 +3955,7 @@ function registerFactTools(server, callApi) {
|
|
|
3858
3955
|
"lexq_facts_list",
|
|
3859
3956
|
{
|
|
3860
3957
|
title: "List Fact Definitions",
|
|
3958
|
+
annotations: { readOnlyHint: true },
|
|
3861
3959
|
description: "List all fact definitions (input variable schema). Shows key, type, required, and PII status. Always check this before creating rules.",
|
|
3862
3960
|
inputSchema: z5.object({
|
|
3863
3961
|
page: z5.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -3875,6 +3973,7 @@ function registerFactTools(server, callApi) {
|
|
|
3875
3973
|
"lexq_facts_create",
|
|
3876
3974
|
{
|
|
3877
3975
|
title: "Create Fact Definition",
|
|
3976
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
3878
3977
|
description: "Register a new input variable. Key starts with a letter, then letters, numbers, and underscores (e.g. paymentAmount). Casing is not enforced. Types: STRING, NUMBER, BOOLEAN, LIST_STRING, LIST_NUMBER.",
|
|
3879
3978
|
inputSchema: z5.object({
|
|
3880
3979
|
key: z5.string().regex(FACT_KEY_PATTERN).describe("Variable key. Any casing; must start with a letter."),
|
|
@@ -3884,6 +3983,15 @@ function registerFactTools(server, callApi) {
|
|
|
3884
3983
|
isRequired: z5.boolean().default(false).describe("Whether this fact is required for rule evaluation"),
|
|
3885
3984
|
isPii: z5.boolean().default(false).describe(
|
|
3886
3985
|
"Mark as PII \u2014 masked on every read surface, revealable only in the console (audited)"
|
|
3986
|
+
),
|
|
3987
|
+
valueDomain: z5.object({
|
|
3988
|
+
allowedValues: z5.array(z5.union([z5.string(), z5.number()])).optional().describe(
|
|
3989
|
+
"Accepted values. For list types the constraint applies to each element, not to the list as a whole."
|
|
3990
|
+
),
|
|
3991
|
+
min: z5.number().optional().describe("Lower bound, inclusive. Numeric types only."),
|
|
3992
|
+
max: z5.number().optional().describe("Upper bound, inclusive. Numeric types only.")
|
|
3993
|
+
}).optional().describe(
|
|
3994
|
+
"Which values this fact accepts. Omit for no constraint. STRING and LIST_STRING take allowedValues only; NUMBER and LIST_NUMBER take all three; BOOLEAN takes none. Declaring it lets the rule editor offer a dropdown and rejects a rule literal that could never match."
|
|
3887
3995
|
)
|
|
3888
3996
|
})
|
|
3889
3997
|
},
|
|
@@ -3893,6 +4001,7 @@ function registerFactTools(server, callApi) {
|
|
|
3893
4001
|
"lexq_facts_update",
|
|
3894
4002
|
{
|
|
3895
4003
|
title: "Update Fact Definition",
|
|
4004
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3896
4005
|
description: "Update a fact definition. The key is immutable. The type can change only while no rule references the fact; if any does, the call fails with FD-007 and reports the count. Only the fields you send are changed. System facts accept name, description, and PII only.",
|
|
3897
4006
|
inputSchema: z5.object({
|
|
3898
4007
|
factId: z5.string().uuid().describe("Fact definition ID"),
|
|
@@ -3902,7 +4011,16 @@ function registerFactTools(server, callApi) {
|
|
|
3902
4011
|
"Value type. Omit to leave it unchanged. Changing it fails with FD-007 while any rule references the fact."
|
|
3903
4012
|
),
|
|
3904
4013
|
isRequired: z5.boolean().optional().describe("Required flag"),
|
|
3905
|
-
isPii: z5.boolean().optional().describe("PII flag \u2014 enables/disables masking (changeable even on system facts)")
|
|
4014
|
+
isPii: z5.boolean().optional().describe("PII flag \u2014 enables/disables masking (changeable even on system facts)"),
|
|
4015
|
+
valueDomain: z5.object({
|
|
4016
|
+
allowedValues: z5.array(z5.union([z5.string(), z5.number()])).optional().describe(
|
|
4017
|
+
"Accepted values. For list types the constraint applies to each element, not to the list as a whole."
|
|
4018
|
+
),
|
|
4019
|
+
min: z5.number().optional().describe("Lower bound, inclusive. Numeric types only."),
|
|
4020
|
+
max: z5.number().optional().describe("Upper bound, inclusive. Numeric types only.")
|
|
4021
|
+
}).optional().describe(
|
|
4022
|
+
"Which values this fact accepts. Three states: omit the field to leave the constraint alone, send {} to remove it, or send a populated object to replace it. Narrowing it does not rewrite rules that already reference this fact \u2014 they keep working, and the next save is judged against the new constraint."
|
|
4023
|
+
)
|
|
3906
4024
|
})
|
|
3907
4025
|
},
|
|
3908
4026
|
async ({ factId, ...body }) => callApi("PUT", `schema/facts/${factId}`, { body })
|
|
@@ -3911,6 +4029,7 @@ function registerFactTools(server, callApi) {
|
|
|
3911
4029
|
"lexq_facts_delete",
|
|
3912
4030
|
{
|
|
3913
4031
|
title: "Delete Fact Definition",
|
|
4032
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3914
4033
|
description: "Delete a fact definition. System facts cannot be deleted. Neither can a fact that any rule references: that call fails with FD-006 and reports the count. Remove the references first.",
|
|
3915
4034
|
inputSchema: z5.object({
|
|
3916
4035
|
factId: z5.string().uuid().describe("Fact definition ID")
|
|
@@ -3922,6 +4041,7 @@ function registerFactTools(server, callApi) {
|
|
|
3922
4041
|
"lexq_facts_action_metadata",
|
|
3923
4042
|
{
|
|
3924
4043
|
title: "Get Action Runtime Fact Metadata",
|
|
4044
|
+
annotations: { readOnlyHint: true },
|
|
3925
4045
|
description: "Retrieve runtime fact requirements per Action type. For each Action, shows which input facts must be present in the execution payload \u2014 e.g. MUTATE_FACT always requires its targetVar fact, plus refVar when one is specified. The factRequired flag describes the FACT, not the parameter: refVar is an optional parameter, but if you specify it the named fact must exist. A required fact absent at runtime throws \u2014 the engine never defaults to 0. Facts are supplied as input or written by a prior action in the same rule; only SET_FACT creates a fact from nothing. Static data, safe to cache in-session.",
|
|
3926
4046
|
inputSchema: z5.object({})
|
|
3927
4047
|
},
|
|
@@ -3931,6 +4051,7 @@ function registerFactTools(server, callApi) {
|
|
|
3931
4051
|
"lexq_facts_unregistered",
|
|
3932
4052
|
{
|
|
3933
4053
|
title: "List Unregistered Facts",
|
|
4054
|
+
annotations: { readOnlyHint: true },
|
|
3934
4055
|
description: "List facts referenced by a version's rules but not yet defined (read-only \u2014 does not block publish/deploy, INV-4). Version-wide: covers every rule in the version. Each entry carries the inferred type, suggested name, and where it is referenced (condition/action). Register them with lexq_facts_create to enable type validation and the dry-run requirements analyzer.",
|
|
3935
4056
|
inputSchema: z5.object({
|
|
3936
4057
|
groupId: z5.string().uuid().describe("Policy group ID"),
|
|
@@ -3943,6 +4064,7 @@ function registerFactTools(server, callApi) {
|
|
|
3943
4064
|
"lexq_facts_export",
|
|
3944
4065
|
{
|
|
3945
4066
|
title: "Export Fact Catalog",
|
|
4067
|
+
annotations: { readOnlyHint: true },
|
|
3946
4068
|
description: "Export the fact catalog. The two formats carry different things: CSV is the catalog as it stands, system facts included, for reading in a spreadsheet; JSON matches the shape that batch create accepts, so it can be fed straight back in, which is why it leaves out the fields that endpoint does not take. Returns the file contents as text.",
|
|
3947
4069
|
inputSchema: z5.object({
|
|
3948
4070
|
format: z5.enum(EXPORT_FORMATS).default("csv").describe("Export format"),
|
|
@@ -3962,6 +4084,7 @@ function registerDeployTools(server, callApi) {
|
|
|
3962
4084
|
"lexq_deploy_publish",
|
|
3963
4085
|
{
|
|
3964
4086
|
title: "Publish Version",
|
|
4087
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
3965
4088
|
description: "Publish a DRAFT version (DRAFT \u2192 ACTIVE). Locks the version from further edits. Must have at least one rule. Undefined facts referenced by rules do not block publishing (INV-4); call lexq_facts_unregistered first to review them.",
|
|
3966
4089
|
inputSchema: z6.object({
|
|
3967
4090
|
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
@@ -3975,6 +4098,7 @@ function registerDeployTools(server, callApi) {
|
|
|
3975
4098
|
"lexq_deploy_live",
|
|
3976
4099
|
{
|
|
3977
4100
|
title: "Deploy to Live",
|
|
4101
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3978
4102
|
description: "Deploy an ACTIVE (published) version to live traffic. Takes effect immediately. Versions whose effective start date has not arrived are rejected (P-037) \u2014 use lexq_deploy_schedule for those. Undefined facts do not block deployment (INV-4); use lexq_facts_unregistered to review what the version references but has not defined.",
|
|
3979
4103
|
inputSchema: z6.object({
|
|
3980
4104
|
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
@@ -3990,6 +4114,7 @@ function registerDeployTools(server, callApi) {
|
|
|
3990
4114
|
"lexq_deploy_rollback",
|
|
3991
4115
|
{
|
|
3992
4116
|
title: "Rollback Deployment",
|
|
4117
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3993
4118
|
description: "Rollback to the previous deployed version. Only available if there is a previous version.",
|
|
3994
4119
|
inputSchema: z6.object({
|
|
3995
4120
|
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
@@ -4004,6 +4129,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4004
4129
|
"lexq_deploy_undeploy",
|
|
4005
4130
|
{
|
|
4006
4131
|
title: "Undeploy",
|
|
4132
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
4007
4133
|
description: "Remove the live version from traffic. The version stays ACTIVE but no longer serves requests.",
|
|
4008
4134
|
inputSchema: z6.object({
|
|
4009
4135
|
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
@@ -4018,6 +4144,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4018
4144
|
"lexq_deploy_schedule",
|
|
4019
4145
|
{
|
|
4020
4146
|
title: "Schedule Deployment",
|
|
4147
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
4021
4148
|
description: "Schedule an ACTIVE version with a future effective start date to auto-deploy at that time (Scheduled Deployment). One pending schedule per group; manual deploy/rollback/undeploy, starting an A/B test, or archiving the group cancels it. The snapshot hash is sealed at scheduling and re-verified at execution (fail-closed).",
|
|
4022
4149
|
inputSchema: z6.object({
|
|
4023
4150
|
groupId: z6.string().uuid().describe("Policy group ID"),
|
|
@@ -4033,6 +4160,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4033
4160
|
"lexq_deploy_unschedule",
|
|
4034
4161
|
{
|
|
4035
4162
|
title: "Cancel Scheduled Deployment",
|
|
4163
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
4036
4164
|
description: "Cancel the pending scheduled deployment for a group. The version itself is not affected. Fails with P-039 if no pending schedule exists.",
|
|
4037
4165
|
inputSchema: z6.object({
|
|
4038
4166
|
groupId: z6.string().uuid().describe("Policy group ID")
|
|
@@ -4044,6 +4172,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4044
4172
|
"lexq_deploy_schedules",
|
|
4045
4173
|
{
|
|
4046
4174
|
title: "List Scheduled Deployments",
|
|
4175
|
+
annotations: { readOnlyHint: true },
|
|
4047
4176
|
description: "List scheduled deployments across all groups (all statuses: PENDING, EXECUTED, CANCELED, FAILED), newest first.",
|
|
4048
4177
|
inputSchema: z6.object({
|
|
4049
4178
|
page: z6.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -4056,6 +4185,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4056
4185
|
"lexq_deploy_history",
|
|
4057
4186
|
{
|
|
4058
4187
|
title: "Deployment History",
|
|
4188
|
+
annotations: { readOnlyHint: true },
|
|
4059
4189
|
description: "List deployment history across all groups.",
|
|
4060
4190
|
inputSchema: z6.object({
|
|
4061
4191
|
page: z6.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -4079,6 +4209,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4079
4209
|
"lexq_deploy_detail",
|
|
4080
4210
|
{
|
|
4081
4211
|
title: "Deployment Detail",
|
|
4212
|
+
annotations: { readOnlyHint: true },
|
|
4082
4213
|
description: "Get detailed info about a specific deployment including snapshot hash and integrity check.",
|
|
4083
4214
|
inputSchema: z6.object({
|
|
4084
4215
|
deploymentId: z6.string().uuid().describe("Deployment ID")
|
|
@@ -4090,6 +4221,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4090
4221
|
"lexq_deploy_overview",
|
|
4091
4222
|
{
|
|
4092
4223
|
title: "Deployment Overview",
|
|
4224
|
+
annotations: { readOnlyHint: true },
|
|
4093
4225
|
description: "Show current deployment status of all groups \u2014 which version is live, last deployment type, and deployer.",
|
|
4094
4226
|
inputSchema: z6.object({})
|
|
4095
4227
|
},
|
|
@@ -4099,6 +4231,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4099
4231
|
"lexq_deploy_deployable",
|
|
4100
4232
|
{
|
|
4101
4233
|
title: "List Deployable Versions",
|
|
4234
|
+
annotations: { readOnlyHint: true },
|
|
4102
4235
|
description: "List ACTIVE (published) versions that can be deployed for a group. Use this to find which versions are available before calling deploy live.",
|
|
4103
4236
|
inputSchema: z6.object({
|
|
4104
4237
|
groupId: z6.string().uuid().describe("Policy group ID")
|
|
@@ -4110,6 +4243,7 @@ function registerDeployTools(server, callApi) {
|
|
|
4110
4243
|
"lexq_deploy_diff",
|
|
4111
4244
|
{
|
|
4112
4245
|
title: "Deployment Diff",
|
|
4246
|
+
annotations: { readOnlyHint: true },
|
|
4113
4247
|
description: "Compare rule snapshots between two versions. Shows added, removed, and modified rules. Useful for reviewing changes before deploying a new version.",
|
|
4114
4248
|
inputSchema: z6.object({
|
|
4115
4249
|
baseVersionId: z6.string().uuid().describe("Base version ID (typically the current live)"),
|
|
@@ -4130,6 +4264,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4130
4264
|
"lexq_dry_run",
|
|
4131
4265
|
{
|
|
4132
4266
|
title: "Dry Run",
|
|
4267
|
+
annotations: { readOnlyHint: true },
|
|
4133
4268
|
description: dedent16`
|
|
4134
4269
|
Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
|
|
4135
4270
|
|
|
@@ -4159,6 +4294,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4159
4294
|
"lexq_dry_run_compare",
|
|
4160
4295
|
{
|
|
4161
4296
|
title: "Dry Run Compare",
|
|
4297
|
+
annotations: { readOnlyHint: true },
|
|
4162
4298
|
description: dedent16`
|
|
4163
4299
|
Compare dry run results between two versions using the same input facts. Useful for validating changes.
|
|
4164
4300
|
|
|
@@ -4183,6 +4319,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4183
4319
|
"lexq_requirements",
|
|
4184
4320
|
{
|
|
4185
4321
|
title: "Analyze Requirements",
|
|
4322
|
+
annotations: { readOnlyHint: true },
|
|
4186
4323
|
description: "Analyze which input facts a version requires. Returns required keys, types, and an example request body.",
|
|
4187
4324
|
inputSchema: z7.object({
|
|
4188
4325
|
groupId: z7.string().uuid().describe("Policy group ID"),
|
|
@@ -4195,6 +4332,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4195
4332
|
"lexq_simulation_start",
|
|
4196
4333
|
{
|
|
4197
4334
|
title: "Start Simulation",
|
|
4335
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
4198
4336
|
description: dedent16`
|
|
4199
4337
|
Start an Impact Simulation against historical, uploaded, or inline data.
|
|
4200
4338
|
|
|
@@ -4236,6 +4374,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4236
4374
|
"lexq_simulation_status",
|
|
4237
4375
|
{
|
|
4238
4376
|
title: "Simulation Status",
|
|
4377
|
+
annotations: { readOnlyHint: true },
|
|
4239
4378
|
description: "Get simulation status and results. Poll until status is COMPLETED or FAILED.",
|
|
4240
4379
|
inputSchema: z7.object({
|
|
4241
4380
|
simulationId: z7.string().uuid().describe("Simulation ID")
|
|
@@ -4247,6 +4386,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4247
4386
|
"lexq_simulation_list",
|
|
4248
4387
|
{
|
|
4249
4388
|
title: "List Simulations",
|
|
4389
|
+
annotations: { readOnlyHint: true },
|
|
4250
4390
|
description: "List simulation history with optional filters.",
|
|
4251
4391
|
inputSchema: z7.object({
|
|
4252
4392
|
page: z7.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -4268,6 +4408,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4268
4408
|
"lexq_simulation_cancel",
|
|
4269
4409
|
{
|
|
4270
4410
|
title: "Cancel Simulation",
|
|
4411
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
4271
4412
|
description: "Cancel a running or pending simulation.",
|
|
4272
4413
|
inputSchema: z7.object({
|
|
4273
4414
|
simulationId: z7.string().uuid().describe("Simulation ID")
|
|
@@ -4279,6 +4420,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4279
4420
|
"lexq_simulation_export",
|
|
4280
4421
|
{
|
|
4281
4422
|
title: "Export Simulation",
|
|
4423
|
+
annotations: { readOnlyHint: true },
|
|
4282
4424
|
description: "Export simulation results as JSON or CSV. Returns the raw data.",
|
|
4283
4425
|
inputSchema: z7.object({
|
|
4284
4426
|
simulationId: z7.string().uuid().describe("Simulation ID"),
|
|
@@ -4293,6 +4435,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4293
4435
|
"lexq_dataset_upload",
|
|
4294
4436
|
{
|
|
4295
4437
|
title: "Upload Dataset",
|
|
4438
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
4296
4439
|
description: dedent16`
|
|
4297
4440
|
Upload inline CSV or JSON content as a simulation dataset.
|
|
4298
4441
|
The content is uploaded to S3 and a path is returned in the "path" field.
|
|
@@ -4336,6 +4479,7 @@ function registerAnalyticsTools(server, callApi) {
|
|
|
4336
4479
|
"lexq_dataset_template",
|
|
4337
4480
|
{
|
|
4338
4481
|
title: "Download Dataset Template",
|
|
4482
|
+
annotations: { readOnlyHint: true },
|
|
4339
4483
|
description: "Generate a sample CSV or JSON template based on the required facts of a version. Use this to understand the expected data format before uploading a dataset.",
|
|
4340
4484
|
inputSchema: z7.object({
|
|
4341
4485
|
groupId: z7.string().uuid().describe("Policy group ID"),
|
|
@@ -4365,6 +4509,7 @@ function registerProfileTools(server, callApi) {
|
|
|
4365
4509
|
"lexq_profile_overview",
|
|
4366
4510
|
{
|
|
4367
4511
|
title: "Group Latency Profile",
|
|
4512
|
+
annotations: { readOnlyHint: true },
|
|
4368
4513
|
description: "Per-rule latency profile of a policy group over a time window: group TOTAL distribution split by cache state (HIT = compiled ruleset cache hit, MISS = deep-load + compile), a per-rule CONDITION/ACTION percentile table, and slow-rule flags. " + RELATIVE_THRESHOLD + ` Every percentile is accompanied by its sample count n; a percentile is withheld (null) unless n\xD7(1\u2212q) \u2265 ${TAIL_MIN_OBS} (p50 needs n \u2265 6, p95 n \u2265 60, p99 n \u2265 300 \u2014 display gate, separate from the n \u2265 ${MIN_SAMPLES} judgment gate). Baselines report INSUFFICIENT_COHORT when fewer than ${MIN_COHORT_SIZE} rules qualify. Rule detail comes from a deterministic ${PROFILE_SAMPLE_PERMILLE / 10}% sample of calls; TOTAL is recorded for every call. Defaults: last ${PROFILE_DEFAULT_WINDOW_HOURS}h, live version, cacheState HIT.`,
|
|
4369
4514
|
inputSchema: z8.object({
|
|
4370
4515
|
groupId: z8.string().uuid().describe("Policy group ID"),
|
|
@@ -4384,6 +4529,7 @@ function registerProfileTools(server, callApi) {
|
|
|
4384
4529
|
"lexq_profile_rule",
|
|
4385
4530
|
{
|
|
4386
4531
|
title: "Rule Latency Detail",
|
|
4532
|
+
annotations: { readOnlyHint: true },
|
|
4387
4533
|
description: `Single-rule latency detail: merged phase \xD7 cacheState distributions plus a per-window time series (${LATENCY_WINDOW_MILLIS / 1e3}s windows). Missing windows are genuine gaps \u2014 never interpolated. Series points carry each window's own values; percentiles in merged distributions are withheld (null) unless n\xD7(1\u2212q) \u2265 ${TAIL_MIN_OBS} (p50 n \u2265 6, p95 n \u2265 60, p99 n \u2265 300). ` + RELATIVE_THRESHOLD,
|
|
4388
4534
|
inputSchema: z8.object({
|
|
4389
4535
|
groupId: z8.string().uuid().describe("Policy group ID"),
|
|
@@ -4408,6 +4554,7 @@ function registerReplayTools(server, callApi) {
|
|
|
4408
4554
|
"lexq_replay_decision",
|
|
4409
4555
|
{
|
|
4410
4556
|
title: "Replay a Decision",
|
|
4557
|
+
annotations: { readOnlyHint: true },
|
|
4411
4558
|
description: "Re-evaluate a past execution (traceId) against a candidate version and return the decision diff (decisionChanged, effect changes, fired rules) plus a determinism verdict. Synchronous and free of charge (TPS throttle only). A replay sends no webhook, notification, or event: rule actions produce no outward effects.",
|
|
4412
4559
|
inputSchema: z9.object({
|
|
4413
4560
|
traceId: z9.string().describe("Trace ID of the past execution to replay"),
|
|
@@ -4420,6 +4567,7 @@ function registerReplayTools(server, callApi) {
|
|
|
4420
4567
|
"lexq_replay_start",
|
|
4421
4568
|
{
|
|
4422
4569
|
title: "Start Window Replay (Blast Radius)",
|
|
4570
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
4423
4571
|
description: "Submit an async job that replays a date window of past executions against a candidate version and measures the blast radius (how many decisions change). Billed per replayed record (REPLAY metric); VIEWER role cannot submit. Poll with lexq_replay_status.",
|
|
4424
4572
|
inputSchema: z9.object({
|
|
4425
4573
|
candidateVersionId: z9.string().uuid().describe("Version to re-evaluate against"),
|
|
@@ -4436,6 +4584,7 @@ function registerReplayTools(server, callApi) {
|
|
|
4436
4584
|
"lexq_replay_status",
|
|
4437
4585
|
{
|
|
4438
4586
|
title: "Get Replay Job Status",
|
|
4587
|
+
annotations: { readOnlyHint: true },
|
|
4439
4588
|
description: "Poll a window replay job. RUNNING shows progress 0\u2013100; COMPLETED fills summary and changedSamples; FAILED carries errorMessage. capped=true means the window exceeded the sample cap and only part was replayed.",
|
|
4440
4589
|
inputSchema: z9.object({
|
|
4441
4590
|
jobId: z9.string().describe("Replay job ID from lexq_replay_start")
|
|
@@ -4447,6 +4596,7 @@ function registerReplayTools(server, callApi) {
|
|
|
4447
4596
|
"lexq_replay_list",
|
|
4448
4597
|
{
|
|
4449
4598
|
title: "List Replay Jobs",
|
|
4599
|
+
annotations: { readOnlyHint: true },
|
|
4450
4600
|
description: "List window replay job history (reverse-chronological). Lightweight items \u2014 use lexq_replay_status for summary and changed samples.",
|
|
4451
4601
|
inputSchema: z9.object({
|
|
4452
4602
|
page: z9.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -4459,6 +4609,7 @@ function registerReplayTools(server, callApi) {
|
|
|
4459
4609
|
"lexq_replay_cancel",
|
|
4460
4610
|
{
|
|
4461
4611
|
title: "Cancel Replay Job",
|
|
4612
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
4462
4613
|
description: "Cooperatively cancel a PENDING or RUNNING window replay job. Other states are rejected. VIEWER role cannot cancel.",
|
|
4463
4614
|
inputSchema: z9.object({
|
|
4464
4615
|
jobId: z9.string().describe("Replay job ID")
|
|
@@ -4470,6 +4621,7 @@ function registerReplayTools(server, callApi) {
|
|
|
4470
4621
|
"lexq_replay_export",
|
|
4471
4622
|
{
|
|
4472
4623
|
title: "Export Replay Result",
|
|
4624
|
+
annotations: { readOnlyHint: true },
|
|
4473
4625
|
description: "Export a COMPLETED window replay job. A running job is rejected \u2014 a partial result reads as the whole thing on the receiving end. The two formats carry different things: CSV holds the effect blast radius, whose columns are fixed; JSON holds the changed samples and action parameters, which nest and whose keys differ per tenant. JSON is not a superset of CSV. Returns the file contents as text.",
|
|
4474
4626
|
inputSchema: z9.object({
|
|
4475
4627
|
jobId: z9.string().describe("Replay job ID"),
|
|
@@ -4487,6 +4639,7 @@ function registerHistoryTools(server, callApi) {
|
|
|
4487
4639
|
"lexq_history_list",
|
|
4488
4640
|
{
|
|
4489
4641
|
title: "List Execution History",
|
|
4642
|
+
annotations: { readOnlyHint: true },
|
|
4490
4643
|
description: "List policy execution history. Shows trace ID, group, version, status, match result, and latency.",
|
|
4491
4644
|
inputSchema: z10.object({
|
|
4492
4645
|
page: z10.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -4514,6 +4667,7 @@ function registerHistoryTools(server, callApi) {
|
|
|
4514
4667
|
"lexq_history_get",
|
|
4515
4668
|
{
|
|
4516
4669
|
title: "Get Execution Detail",
|
|
4670
|
+
annotations: { readOnlyHint: true },
|
|
4517
4671
|
description: "Get full execution detail including inputFacts, mutatedFacts, generatedVariables, executionTraces, and decisionTraces.",
|
|
4518
4672
|
inputSchema: z10.object({
|
|
4519
4673
|
traceId: z10.string().describe("Trace ID from execution history")
|
|
@@ -4525,6 +4679,7 @@ function registerHistoryTools(server, callApi) {
|
|
|
4525
4679
|
"lexq_history_stats",
|
|
4526
4680
|
{
|
|
4527
4681
|
title: "Execution Statistics",
|
|
4682
|
+
annotations: { readOnlyHint: true },
|
|
4528
4683
|
description: "Get execution KPIs: total executions, success/failure counts, success rate, and average latency.",
|
|
4529
4684
|
inputSchema: z10.object({
|
|
4530
4685
|
groupId: z10.string().uuid().optional().describe("Filter by policy group"),
|
|
@@ -4549,6 +4704,7 @@ function registerProvenanceTools(server, callApi) {
|
|
|
4549
4704
|
"lexq_provenance_get",
|
|
4550
4705
|
{
|
|
4551
4706
|
title: "Get Decision Provenance",
|
|
4707
|
+
annotations: { readOnlyHint: true },
|
|
4552
4708
|
description: "Get the lineage of a single decision: what was decided, deterministic why per rule, input facts (PII facts are masked as \u2022\u2022\u2022\u2022\u2022\u2022 with maskedKeys listing them \u2014 values are revealable only in the console, audited), the authored/published/deployed responsibility chain, and the rule snapshot fingerprint.",
|
|
4553
4709
|
inputSchema: z11.object({
|
|
4554
4710
|
traceId: z11.string().describe("Trace ID of the execution")
|
|
@@ -4560,6 +4716,7 @@ function registerProvenanceTools(server, callApi) {
|
|
|
4560
4716
|
"lexq_pii_reveals_list",
|
|
4561
4717
|
{
|
|
4562
4718
|
title: "List PII Reveal Audits",
|
|
4719
|
+
annotations: { readOnlyHint: true },
|
|
4563
4720
|
description: "List the PII reveal audit ledger \u2014 who revealed which fact of which trace, and when. Metadata only; revealed values are never stored or returned. Use for monthly access-log inspection and SIEM collection.",
|
|
4564
4721
|
inputSchema: z11.object({
|
|
4565
4722
|
page: z11.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -4590,6 +4747,7 @@ function registerLogTools(server, callApi) {
|
|
|
4590
4747
|
"lexq_logs_list",
|
|
4591
4748
|
{
|
|
4592
4749
|
title: "List Failure Logs",
|
|
4750
|
+
annotations: { readOnlyHint: true },
|
|
4593
4751
|
description: "List system failure logs from background tasks (platform event webhooks, scheduled deployments).",
|
|
4594
4752
|
inputSchema: z12.object({
|
|
4595
4753
|
page: z12.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -4615,6 +4773,7 @@ function registerLogTools(server, callApi) {
|
|
|
4615
4773
|
"lexq_logs_get",
|
|
4616
4774
|
{
|
|
4617
4775
|
title: "Get Failure Log",
|
|
4776
|
+
annotations: { readOnlyHint: true },
|
|
4618
4777
|
description: "Get failure log detail by ID.",
|
|
4619
4778
|
inputSchema: z12.object({
|
|
4620
4779
|
logId: z12.string().uuid().describe("Failure log ID")
|
|
@@ -4626,6 +4785,7 @@ function registerLogTools(server, callApi) {
|
|
|
4626
4785
|
"lexq_logs_action",
|
|
4627
4786
|
{
|
|
4628
4787
|
title: "Process Failure Log",
|
|
4788
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
4629
4789
|
description: "Process a single failure log: RESOLVE (mark as manually fixed) or IGNORE (skip intentionally).",
|
|
4630
4790
|
inputSchema: z12.object({
|
|
4631
4791
|
logId: z12.string().uuid().describe("Failure log ID"),
|
|
@@ -4640,6 +4800,7 @@ function registerLogTools(server, callApi) {
|
|
|
4640
4800
|
"lexq_logs_bulk_action",
|
|
4641
4801
|
{
|
|
4642
4802
|
title: "Bulk Process Failure Logs",
|
|
4803
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
4643
4804
|
description: "Process multiple failure logs at once. Provide an array of log IDs and the action.",
|
|
4644
4805
|
inputSchema: z12.object({
|
|
4645
4806
|
logIds: z12.array(z12.string().uuid()).describe("Array of failure log IDs"),
|
|
@@ -4659,6 +4820,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4659
4820
|
"lexq_webhook_subscriptions_list",
|
|
4660
4821
|
{
|
|
4661
4822
|
title: "List Webhook Subscriptions",
|
|
4823
|
+
annotations: { readOnlyHint: true },
|
|
4662
4824
|
description: "List platform event webhook subscriptions. These receive deployment lifecycle notifications (publish, deploy, rollback, undeploy).",
|
|
4663
4825
|
inputSchema: z13.object({
|
|
4664
4826
|
page: z13.number().int().min(0).default(0).describe("Page number"),
|
|
@@ -4674,6 +4836,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4674
4836
|
"lexq_webhook_subscriptions_get",
|
|
4675
4837
|
{
|
|
4676
4838
|
title: "Get Webhook Subscription",
|
|
4839
|
+
annotations: { readOnlyHint: true },
|
|
4677
4840
|
description: "Get webhook subscription detail by ID.",
|
|
4678
4841
|
inputSchema: z13.object({
|
|
4679
4842
|
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
@@ -4685,6 +4848,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4685
4848
|
"lexq_webhook_subscriptions_save",
|
|
4686
4849
|
{
|
|
4687
4850
|
title: "Save Webhook Subscription",
|
|
4851
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
4688
4852
|
description: 'Create or update a webhook subscription. Omit id to create, provide id to update. Events: VERSION_PUBLISHED, DEPLOYED, ROLLED_BACK, UNDEPLOYED. Formats: GENERIC (full JSON), SLACK ({"text": "..."}).',
|
|
4689
4853
|
inputSchema: z13.object({
|
|
4690
4854
|
id: z13.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
|
|
@@ -4702,6 +4866,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4702
4866
|
"lexq_webhook_subscriptions_delete",
|
|
4703
4867
|
{
|
|
4704
4868
|
title: "Delete Webhook Subscription",
|
|
4869
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
4705
4870
|
description: "Delete a webhook subscription by ID.",
|
|
4706
4871
|
inputSchema: z13.object({
|
|
4707
4872
|
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
@@ -4713,6 +4878,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
|
|
|
4713
4878
|
"lexq_webhook_subscriptions_test",
|
|
4714
4879
|
{
|
|
4715
4880
|
title: "Test Webhook Subscription",
|
|
4881
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
4716
4882
|
description: "Send a test event to verify webhook connectivity. Returns the HTTP status code and success/failure message.",
|
|
4717
4883
|
inputSchema: z13.object({
|
|
4718
4884
|
id: z13.string().uuid().describe("Webhook subscription ID")
|
|
@@ -4729,6 +4895,7 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
4729
4895
|
"lexq_domain_templates_list",
|
|
4730
4896
|
{
|
|
4731
4897
|
title: "List Domain Templates",
|
|
4898
|
+
annotations: { readOnlyHint: true },
|
|
4732
4899
|
description: "List all domain templates. A domain template is a curated, industry-specific starter pack of fact definitions and sample rules (e.g. ECOMMERCE). Each entry reports its key, status (ACTIVE or COMING_SOON), and a summary of what it provisions. Call this before preview or apply to discover which templates can currently be applied.",
|
|
4733
4900
|
inputSchema: z14.object({})
|
|
4734
4901
|
},
|
|
@@ -4738,6 +4905,7 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
4738
4905
|
"lexq_domain_templates_preview",
|
|
4739
4906
|
{
|
|
4740
4907
|
title: "Preview Domain Template",
|
|
4908
|
+
annotations: { readOnlyHint: true },
|
|
4741
4909
|
description: "Preview exactly what a domain template will provision before applying it: the fact definitions it registers, the sample rules it creates, and an apply plan. This is a read-only dry run \u2014 nothing is created. Only ACTIVE templates can be previewed.",
|
|
4742
4910
|
inputSchema: z14.object({
|
|
4743
4911
|
template: z14.string().describe(
|
|
@@ -4751,6 +4919,7 @@ function registerDomainTemplateTools(server, callApi) {
|
|
|
4751
4919
|
"lexq_domain_templates_apply",
|
|
4752
4920
|
{
|
|
4753
4921
|
title: "Apply Domain Template",
|
|
4922
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
4754
4923
|
description: "Apply a domain template to the current tenant. Creates the template's fact definitions and a new policy group pre-populated with its sample rules as a DRAFT version. Existing facts are skipped \u2014 apply is additive and never overwrites existing schema. Run lexq_domain_templates_preview first to review what will be created. Only ACTIVE templates can be applied.",
|
|
4755
4924
|
inputSchema: z14.object({
|
|
4756
4925
|
template: z14.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
|