@lexq/cli 0.1.36 → 0.1.38

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/dist/index.js CHANGED
@@ -2183,13 +2183,141 @@ function resolveBody(opts) {
2183
2183
  return {};
2184
2184
  }
2185
2185
 
2186
- // src/commands/history.ts
2186
+ // src/commands/profile.ts
2187
2187
  import "commander";
2188
2188
  import dedent9 from "dedent";
2189
+
2190
+ // src/types/enums.ts
2191
+ var ProfileCacheState = ["HIT", "MISS"];
2192
+ var FailureStatus = ["PENDING", "RESOLVED", "IGNORED"];
2193
+ var FailureAction = ["RETRY", "IGNORE", "RESOLVE"];
2194
+ var TaskCategory = ["INTEGRATION", "INTERNAL"];
2195
+ var TaskType = [
2196
+ // Integration
2197
+ "COUPON_ISSUE",
2198
+ "COUPON_CANCEL",
2199
+ "POINT_EARN",
2200
+ "POINT_USE",
2201
+ "POINT_REFUND",
2202
+ "NOTIFICATION_SEND",
2203
+ "CRM_SYNC_USER",
2204
+ "CRM_ADD_TAG",
2205
+ "WEBHOOK_EXECUTE",
2206
+ // Internal
2207
+ "IMAGE_PROCESSING",
2208
+ "DAILY_SETTLEMENT",
2209
+ "PLATFORM_WEBHOOK"
2210
+ ];
2211
+ var PlatformEventType = [
2212
+ "VERSION_PUBLISHED",
2213
+ "DEPLOYED",
2214
+ "ROLLED_BACK",
2215
+ "UNDEPLOYED"
2216
+ ];
2217
+ var WebhookPayloadFormat = ["GENERIC", "SLACK"];
2218
+
2219
+ // src/commands/profile.ts
2220
+ var ms = (nanos) => nanos == null ? "\u2013" : (nanos / 1e6).toFixed(2);
2221
+ var msWithUnit = (nanos) => nanos == null ? "\u2013" : `${(nanos / 1e6).toFixed(2)}ms`;
2222
+ function registerProfileCommands(program) {
2223
+ program.command("profile <groupId>").description("Per-rule latency profile with relative slow-rule flags").option("--rule <ruleId>", "Single-rule detail (distributions + 60s window series)").option("--version <versionId>", "Version to inspect (default: live version)").option("--from <instant>", "Window start, ISO-8601 instant (default: 24h ago)").option("--to <instant>", "Window end, ISO-8601 instant (default: now)").option("--cache <state>", "Cache dimension for the rule table: HIT | MISS (default: HIT)").addHelpText(
2224
+ "after",
2225
+ dedent9`
2226
+
2227
+ Slow-rule judgment is relative only: flagged = p50 ≥ 10× the median of
2228
+ per-rule p50s within the group. Absolute ms thresholds are intentionally
2229
+ not supported. Each percentile is withheld (–) unless n×(1−q) ≥ 3 —
2230
+ p50 from n ≥ 6, p95 from n ≥ 60, p99 from n ≥ 300. TOTAL is recorded
2231
+ on every call, rule detail from a deterministic 1% sample.
2232
+
2233
+ Examples:
2234
+ $ lexq profile <groupId>
2235
+ $ lexq profile <groupId> --cache MISS --from 2026-07-01T00:00:00Z
2236
+ $ lexq profile <groupId> --rule <ruleId> --version <versionId>
2237
+ `
2238
+ ).action(async (groupId, opts) => {
2239
+ try {
2240
+ const globalOpts = program.opts();
2241
+ const format = globalOpts.format ?? "json";
2242
+ if (opts.cache && !ProfileCacheState.includes(opts.cache)) {
2243
+ throw new Error(`--cache must be one of: ${ProfileCacheState.join(" | ")}`);
2244
+ }
2245
+ const params = {};
2246
+ if (opts.version) params.versionId = opts.version;
2247
+ if (opts.from) params.from = opts.from;
2248
+ if (opts.to) params.to = opts.to;
2249
+ if (opts.cache) params.cacheState = opts.cache;
2250
+ const clientOpts = {
2251
+ apiKey: globalOpts.apiKey,
2252
+ baseUrl: globalOpts.baseUrl,
2253
+ dryRun: globalOpts.dryRun,
2254
+ verbose: globalOpts.verbose
2255
+ };
2256
+ if (opts.rule) {
2257
+ const data2 = await apiRequest(
2258
+ "GET",
2259
+ `policy-groups/${groupId}/profile/rules/${opts.rule}`,
2260
+ { ...clientOpts, params }
2261
+ );
2262
+ if (format === "table") {
2263
+ console.error("note: --rule detail is nested; printing JSON (table not supported)");
2264
+ }
2265
+ printJson(data2);
2266
+ return;
2267
+ }
2268
+ const data = await apiRequest("GET", `policy-groups/${groupId}/profile`, {
2269
+ ...clientOpts,
2270
+ params
2271
+ });
2272
+ if (format !== "table") {
2273
+ printJson(data);
2274
+ return;
2275
+ }
2276
+ console.log(`window : ${data.from} ~ ${data.to} (cache: ${data.ruleCacheState})`);
2277
+ console.log(`version : ${data.policyVersionId ?? "\u2013 (no live version)"}`);
2278
+ for (const s of data.summary) {
2279
+ const t = s.total;
2280
+ console.log(
2281
+ `TOTAL ${s.cacheState.padEnd(4)}: n=${t.n} p50=${msWithUnit(t.p50Nanos)} p95=${msWithUnit(t.p95Nanos)} p99=${msWithUnit(t.p99Nanos)}`
2282
+ );
2283
+ }
2284
+ for (const b of data.baselines) {
2285
+ const base = b.status === "OK" ? `${msWithUnit(b.baselineP50Nanos)} (cohort ${b.cohortSize})` : `\u2013 (${b.status}, cohort ${b.cohortSize})`;
2286
+ console.log(`baseline ${b.phase.padEnd(9)}: ${base}`);
2287
+ }
2288
+ if (data.droppedRows > 0) {
2289
+ console.log(`\u26A0 droppedRows=${data.droppedRows} (corrupt histogram rows skipped)`);
2290
+ }
2291
+ printTable(
2292
+ ["Rule", "Phase", "n", "p50(ms)", "p95(ms)", "p99(ms)", "\xD7base", "Flagged"],
2293
+ data.rules.flatMap(
2294
+ (rule) => rule.phases.map((p) => [
2295
+ rule.ruleId.substring(0, 12),
2296
+ p.phase,
2297
+ String(p.stats.n),
2298
+ ms(p.stats.p50Nanos),
2299
+ ms(p.stats.p95Nanos),
2300
+ ms(p.stats.p99Nanos),
2301
+ p.baselineMultiple == null ? "\u2013" : `${p.baselineMultiple.toFixed(1)}\xD7`,
2302
+ p.flagged ? "YES" : ""
2303
+ ])
2304
+ ),
2305
+ { truncate: 24 }
2306
+ );
2307
+ } catch (error) {
2308
+ printError(error);
2309
+ process.exit(1);
2310
+ }
2311
+ });
2312
+ }
2313
+
2314
+ // src/commands/history.ts
2315
+ import "commander";
2316
+ import dedent10 from "dedent";
2189
2317
  function registerHistoryCommands(program) {
2190
2318
  const history = program.command("history").description("Execution history").addHelpText(
2191
2319
  "after",
2192
- dedent9`
2320
+ dedent10`
2193
2321
 
2194
2322
  View and analyze policy execution logs from production traffic.
2195
2323
 
@@ -2203,7 +2331,7 @@ function registerHistoryCommands(program) {
2203
2331
  );
2204
2332
  history.command("list").description("List execution history").option("--trace-id <traceId>", "Filter by trace ID").option("--group-id <groupId>", "Filter by policy group").option("--version-id <versionId>", "Filter by version").option("--status <status>", "Filter by status (SUCCESS, NO_MATCH, ERROR, TIMEOUT)").option("--start-date <date>", "Start date (yyyy-MM-dd)").option("--end-date <date>", "End date (yyyy-MM-dd)").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").addHelpText(
2205
2333
  "after",
2206
- dedent9`
2334
+ dedent10`
2207
2335
 
2208
2336
  Examples:
2209
2337
  $ lexq history list --status ERROR --format table
@@ -2257,7 +2385,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2257
2385
  });
2258
2386
  history.command("get").description("Get execution detail").requiredOption("--id <traceId>", "Trace ID").addHelpText(
2259
2387
  "after",
2260
- dedent9`
2388
+ dedent10`
2261
2389
 
2262
2390
  Returns the full execution detail including request facts, result traces,
2263
2391
  and decision traces (SELECTED, NO_MATCH, BLOCKED, etc.).
@@ -2283,7 +2411,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2283
2411
  });
2284
2412
  history.command("stats").description("Get execution statistics").option("--group-id <groupId>", "Filter by policy group").option("--start-date <date>", "Start date (yyyy-MM-dd)").option("--end-date <date>", "End date (yyyy-MM-dd)").addHelpText(
2285
2413
  "after",
2286
- dedent9`
2414
+ dedent10`
2287
2415
 
2288
2416
  Shows total executions, success/no-match/failure counts, success rate, and avg latency.
2289
2417
 
@@ -2332,11 +2460,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2332
2460
 
2333
2461
  // src/commands/replay.ts
2334
2462
  import "commander";
2335
- import dedent10 from "dedent";
2463
+ import dedent11 from "dedent";
2336
2464
  function registerReplayCommands(program) {
2337
2465
  const replay = program.command("replay").description("Decision Replay").addHelpText(
2338
2466
  "after",
2339
- dedent10`
2467
+ dedent11`
2340
2468
 
2341
2469
  Re-evaluate past production executions against a candidate version.
2342
2470
 
@@ -2352,7 +2480,7 @@ function registerReplayCommands(program) {
2352
2480
  );
2353
2481
  replay.command("decision").description("Replay a single execution against a candidate version").requiredOption("--trace-id <traceId>", "Trace ID of the past execution").requiredOption("--version-id <versionId>", "Candidate version to re-evaluate against").addHelpText(
2354
2482
  "after",
2355
- dedent10`
2483
+ dedent11`
2356
2484
 
2357
2485
  Free of charge (TPS throttle only). Returns decisionChanged, effect
2358
2486
  changes, fired rules on both sides, and a determinism verdict.
@@ -2378,7 +2506,7 @@ function registerReplayCommands(program) {
2378
2506
  });
2379
2507
  replay.command("start").description("Submit a window replay job (blast radius)").requiredOption("--version-id <versionId>", "Candidate version to re-evaluate against").requiredOption("--from <date>", "Window start date (yyyy-MM-dd)").requiredOption("--to <date>", "Window end date (yyyy-MM-dd)").option("--max-records <number>", "Sample cap (hard cap 50k)").addHelpText(
2380
2508
  "after",
2381
- dedent10`
2509
+ dedent11`
2382
2510
 
2383
2511
  Billed per replayed record (REPLAY metric). Poll with "lexq replay get".
2384
2512
 
@@ -2476,11 +2604,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2476
2604
 
2477
2605
  // src/commands/provenance.ts
2478
2606
  import "commander";
2479
- import dedent11 from "dedent";
2607
+ import dedent12 from "dedent";
2480
2608
  function registerProvenanceCommands(program) {
2481
2609
  const provenance = program.command("provenance").description("Decision Provenance").addHelpText(
2482
2610
  "after",
2483
- dedent11`
2611
+ dedent12`
2484
2612
 
2485
2613
  Trace who authored, published, and deployed the rules behind a decision.
2486
2614
 
@@ -2506,7 +2634,7 @@ function registerProvenanceCommands(program) {
2506
2634
  });
2507
2635
  provenance.command("reveal-audits").description("List PII reveal audits (who revealed what, when)").option("--trace-id <traceId>", "Filter by trace ID (exact)").option("--fact-key <factKey>", "Filter by fact key (partial, case-insensitive)").option("--revealed-by <operatorId>", "Filter by operator ID (exact)").option("--start-date <date>", "Start date (yyyy-MM-dd)").option("--end-date <date>", "End date (yyyy-MM-dd)").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").addHelpText(
2508
2636
  "after",
2509
- dedent11`
2637
+ dedent12`
2510
2638
 
2511
2639
  Metadata only — revealed values are never stored or returned.
2512
2640
 
@@ -2559,11 +2687,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2559
2687
 
2560
2688
  // src/commands/integrations.ts
2561
2689
  import "commander";
2562
- import dedent12 from "dedent";
2690
+ import dedent13 from "dedent";
2563
2691
  function registerIntegrationCommands(program) {
2564
2692
  const integrations = program.command("integrations").description("Manage external integrations").addHelpText(
2565
2693
  "after",
2566
- dedent12`
2694
+ dedent13`
2567
2695
 
2568
2696
  Integrations connect rule actions to external services (webhooks, coupons,
2569
2697
  points, notifications, CRM, messengers).
@@ -2633,7 +2761,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2633
2761
  });
2634
2762
  integrations.command("save").description("Create or update an integration").requiredOption("--json <body>", "Request body as JSON string").addHelpText(
2635
2763
  "after",
2636
- dedent12`
2764
+ dedent13`
2637
2765
 
2638
2766
  Examples:
2639
2767
  # Create
@@ -2683,7 +2811,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2683
2811
  });
2684
2812
  integrations.command("delete").description("Delete an integration").requiredOption("--id <integrationId>", "Integration ID").option("--force", "Skip confirmation prompt").addHelpText(
2685
2813
  "after",
2686
- dedent12`
2814
+ dedent13`
2687
2815
 
2688
2816
  Rules referencing this integration will fail at execution time.
2689
2817
  Use --force to skip confirmation.
@@ -2715,7 +2843,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2715
2843
  });
2716
2844
  integrations.command("config-spec").description("Get integration configuration field specs").addHelpText(
2717
2845
  "after",
2718
- dedent12`
2846
+ dedent13`
2719
2847
 
2720
2848
  Shows required and optional configuration fields for each integration type.
2721
2849
 
@@ -2741,41 +2869,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2741
2869
 
2742
2870
  // src/commands/logs.ts
2743
2871
  import "commander";
2744
- import dedent13 from "dedent";
2745
-
2746
- // src/types/enums.ts
2747
- var FailureStatus = ["PENDING", "RESOLVED", "IGNORED"];
2748
- var FailureAction = ["RETRY", "IGNORE", "RESOLVE"];
2749
- var TaskCategory = ["INTEGRATION", "INTERNAL"];
2750
- var TaskType = [
2751
- // Integration
2752
- "COUPON_ISSUE",
2753
- "COUPON_CANCEL",
2754
- "POINT_EARN",
2755
- "POINT_USE",
2756
- "POINT_REFUND",
2757
- "NOTIFICATION_SEND",
2758
- "CRM_SYNC_USER",
2759
- "CRM_ADD_TAG",
2760
- "WEBHOOK_EXECUTE",
2761
- // Internal
2762
- "IMAGE_PROCESSING",
2763
- "DAILY_SETTLEMENT",
2764
- "PLATFORM_WEBHOOK"
2765
- ];
2766
- var PlatformEventType = [
2767
- "VERSION_PUBLISHED",
2768
- "DEPLOYED",
2769
- "ROLLED_BACK",
2770
- "UNDEPLOYED"
2771
- ];
2772
- var WebhookPayloadFormat = ["GENERIC", "SLACK"];
2773
-
2774
- // src/commands/logs.ts
2872
+ import dedent14 from "dedent";
2775
2873
  function registerLogCommands(program) {
2776
2874
  const logs = program.command("logs").description("Failure logs").addHelpText(
2777
2875
  "after",
2778
- dedent13`
2876
+ dedent14`
2779
2877
 
2780
2878
  System failure logs (DLQ) for background tasks — webhook calls, coupon issuance,
2781
2879
  point operations, notifications, and platform event webhooks.
@@ -2792,7 +2890,7 @@ function registerLogCommands(program) {
2792
2890
  );
2793
2891
  logs.command("list").description("List failure logs").option("--category <category>", "Filter by category (INTEGRATION, INTERNAL)").option("--task-type <taskType>", `Filter by task type (${TaskType.join(", ")})`).option("--status <status>", "Filter by status (PENDING, RESOLVED, IGNORED)").option("--keyword <keyword>", "Search keyword").option("--start-date <date>", "Start date (yyyy-MM-dd)").option("--end-date <date>", "End date (yyyy-MM-dd)").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").addHelpText(
2794
2892
  "after",
2795
- dedent13`
2893
+ dedent14`
2796
2894
 
2797
2895
  Examples:
2798
2896
  $ lexq logs list --status PENDING --format table
@@ -2843,7 +2941,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2843
2941
  });
2844
2942
  logs.command("get").description("Get failure log detail").requiredOption("--id <logId>", "Log ID").addHelpText(
2845
2943
  "after",
2846
- dedent13`
2944
+ dedent14`
2847
2945
 
2848
2946
  Includes the full payload that was used for the failed operation.
2849
2947
  Use this to inspect what went wrong before deciding to RETRY or RESOLVE.
@@ -2865,7 +2963,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2865
2963
  });
2866
2964
  logs.command("action").description("Process a failure log action (RETRY, IGNORE, RESOLVE)").requiredOption("--id <logId>", "Log ID").requiredOption("--action <action>", "Action: RETRY, IGNORE, or RESOLVE").addHelpText(
2867
2965
  "after",
2868
- dedent13`
2966
+ dedent14`
2869
2967
 
2870
2968
  Actions:
2871
2969
  RETRY Re-execute the failed operation with the original payload
@@ -2897,7 +2995,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2897
2995
  });
2898
2996
  logs.command("bulk-action").description("Bulk process failure logs").requiredOption("--ids <logIds>", "Comma-separated log IDs").requiredOption("--action <action>", "Action: RETRY, IGNORE, or RESOLVE").addHelpText(
2899
2997
  "after",
2900
- dedent13`
2998
+ dedent14`
2901
2999
 
2902
3000
  Processes each log individually. Failures are skipped with a warning.
2903
3001
 
@@ -2925,11 +3023,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2925
3023
 
2926
3024
  // src/commands/webhook-subscriptions.ts
2927
3025
  import "commander";
2928
- import dedent14 from "dedent";
3026
+ import dedent15 from "dedent";
2929
3027
  function registerWebhookSubscriptionCommands(program) {
2930
3028
  const webhooks = program.command("webhook-subscriptions").description("Manage platform event webhook subscriptions").addHelpText(
2931
3029
  "after",
2932
- dedent14`
3030
+ dedent15`
2933
3031
 
2934
3032
  Receive notifications when deployment lifecycle events occur
2935
3033
  (publish, deploy, rollback, undeploy).
@@ -3003,7 +3101,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
3003
3101
  });
3004
3102
  webhooks.command("save").description("Create or update a webhook subscription").requiredOption("--json <body>", "Request body as JSON string").addHelpText(
3005
3103
  "after",
3006
- dedent14`
3104
+ dedent15`
3007
3105
 
3008
3106
  Examples:
3009
3107
  # Create (Slack format)
@@ -3058,7 +3156,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
3058
3156
  });
3059
3157
  webhooks.command("delete").description("Delete a webhook subscription").requiredOption("--id <subscriptionId>", "Subscription ID").option("--force", "Skip confirmation prompt").addHelpText(
3060
3158
  "after",
3061
- dedent14`
3159
+ dedent15`
3062
3160
 
3063
3161
  Use --force to skip the confirmation prompt.
3064
3162
 
@@ -3092,7 +3190,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
3092
3190
  });
3093
3191
  webhooks.command("test").description("Send a test event to verify webhook connectivity").requiredOption("--id <subscriptionId>", "Subscription ID").addHelpText(
3094
3192
  "after",
3095
- dedent14`
3193
+ dedent15`
3096
3194
 
3097
3195
  Sends a test event to the webhook URL and reports the HTTP status code.
3098
3196
  Does not record failures in the failure log.
@@ -3128,7 +3226,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
3128
3226
 
3129
3227
  // src/commands/serve.ts
3130
3228
  import "commander";
3131
- import dedent17 from "dedent";
3229
+ import dedent18 from "dedent";
3132
3230
 
3133
3231
  // src/mcp/server.ts
3134
3232
  import { readFileSync as readFileSync3 } from "fs";
@@ -3455,7 +3553,7 @@ function registerVersionTools(server, callApi) {
3455
3553
 
3456
3554
  // src/mcp/tools/rules.ts
3457
3555
  import { z as z3 } from "zod";
3458
- import dedent15 from "dedent";
3556
+ import dedent16 from "dedent";
3459
3557
  function registerRuleTools(server, callApi) {
3460
3558
  server.registerTool(
3461
3559
  "lexq_rules_list",
@@ -3486,7 +3584,7 @@ function registerRuleTools(server, callApi) {
3486
3584
  "lexq_rules_create",
3487
3585
  {
3488
3586
  title: "Create Rule",
3489
- description: dedent15`
3587
+ description: dedent16`
3490
3588
  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.
3491
3589
 
3492
3590
  Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
@@ -3832,13 +3930,13 @@ function registerDeployTools(server, callApi) {
3832
3930
 
3833
3931
  // src/mcp/tools/analytics.ts
3834
3932
  import { z as z6 } from "zod";
3835
- import dedent16 from "dedent";
3933
+ import dedent17 from "dedent";
3836
3934
  function registerAnalyticsTools(server, callApi) {
3837
3935
  server.registerTool(
3838
3936
  "lexq_dry_run",
3839
3937
  {
3840
3938
  title: "Dry Run",
3841
- description: dedent16`
3939
+ description: dedent17`
3842
3940
  Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
3843
3941
 
3844
3942
  Returns:
@@ -3868,7 +3966,7 @@ function registerAnalyticsTools(server, callApi) {
3868
3966
  "lexq_dry_run_compare",
3869
3967
  {
3870
3968
  title: "Dry Run Compare",
3871
- description: dedent16`
3969
+ description: dedent17`
3872
3970
  Compare dry run results between two versions using the same input facts. Useful for validating changes.
3873
3971
 
3874
3972
  Returns:
@@ -3904,7 +4002,7 @@ function registerAnalyticsTools(server, callApi) {
3904
4002
  "lexq_simulation_start",
3905
4003
  {
3906
4004
  title: "Start Simulation",
3907
- description: dedent16`
4005
+ description: dedent17`
3908
4006
  Start an Impact Simulation against historical, uploaded, or inline data.
3909
4007
 
3910
4008
  dataset.type and dataset.source are BOTH required, and must be paired:
@@ -4002,7 +4100,7 @@ function registerAnalyticsTools(server, callApi) {
4002
4100
  "lexq_dataset_upload",
4003
4101
  {
4004
4102
  title: "Upload Dataset",
4005
- description: dedent16`
4103
+ description: dedent17`
4006
4104
  Upload inline CSV or JSON content as a simulation dataset.
4007
4105
  The content is uploaded to S3 and a path is returned in the "path" field.
4008
4106
 
@@ -4058,8 +4156,56 @@ function registerAnalyticsTools(server, callApi) {
4058
4156
  );
4059
4157
  }
4060
4158
 
4061
- // src/mcp/tools/replay.ts
4159
+ // src/mcp/tools/profile.ts
4062
4160
  import { z as z7 } from "zod";
4161
+ var RELATIVE_THRESHOLD = "flagged = p50 \u2265 10\xD7 median of per-rule p50s within the group; absolute thresholds are intentionally not supported.";
4162
+ function profileParams(opts) {
4163
+ const params = {};
4164
+ if (opts.versionId) params.versionId = opts.versionId;
4165
+ if (opts.from) params.from = opts.from;
4166
+ if (opts.to) params.to = opts.to;
4167
+ if (opts.cacheState) params.cacheState = opts.cacheState;
4168
+ return params;
4169
+ }
4170
+ function registerProfileTools(server, callApi) {
4171
+ server.registerTool(
4172
+ "lexq_profile_overview",
4173
+ {
4174
+ title: "Group Latency Profile",
4175
+ 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 3 (p50 needs n \u2265 6, p95 n \u2265 60, p99 n \u2265 300 \u2014 display gate, separate from the n \u2265 100 judgment gate). Baselines report INSUFFICIENT_COHORT when fewer than 3 rules qualify. Rule detail comes from a deterministic 1% sample of calls; TOTAL is recorded for every call. Defaults: last 24h, live version, cacheState HIT.",
4176
+ inputSchema: {
4177
+ groupId: z7.string().uuid().describe("Policy group ID"),
4178
+ versionId: z7.string().uuid().optional().describe("Version to inspect (default: live version)"),
4179
+ from: z7.string().optional().describe("Window start, ISO-8601 instant (e.g. 2026-07-01T00:00:00Z). Default: 24h ago"),
4180
+ to: z7.string().optional().describe("Window end, ISO-8601 instant. Default: now"),
4181
+ cacheState: z7.enum(["HIT", "MISS"]).optional().describe("Cache dimension for the rule table and judgment (default: HIT)")
4182
+ }
4183
+ },
4184
+ async ({ groupId, versionId, from, to, cacheState }) => callApi("GET", `policy-groups/${groupId}/profile`, {
4185
+ params: profileParams({ versionId, from, to, cacheState })
4186
+ })
4187
+ );
4188
+ server.registerTool(
4189
+ "lexq_profile_rule",
4190
+ {
4191
+ title: "Rule Latency Detail",
4192
+ description: "Single-rule latency detail: merged phase \xD7 cacheState distributions plus a per-window time series (60s 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 3 (p50 n \u2265 6, p95 n \u2265 60, p99 n \u2265 300). " + RELATIVE_THRESHOLD,
4193
+ inputSchema: {
4194
+ groupId: z7.string().uuid().describe("Policy group ID"),
4195
+ ruleId: z7.string().uuid().describe("Rule ID (from lexq_profile_overview)"),
4196
+ versionId: z7.string().uuid().optional().describe("Version to inspect (default: live version)"),
4197
+ from: z7.string().optional().describe("Window start, ISO-8601 instant. Default: 24h ago"),
4198
+ to: z7.string().optional().describe("Window end, ISO-8601 instant. Default: now")
4199
+ }
4200
+ },
4201
+ async ({ groupId, ruleId, versionId, from, to }) => callApi("GET", `policy-groups/${groupId}/profile/rules/${ruleId}`, {
4202
+ params: profileParams({ versionId, from, to })
4203
+ })
4204
+ );
4205
+ }
4206
+
4207
+ // src/mcp/tools/replay.ts
4208
+ import { z as z8 } from "zod";
4063
4209
  function registerReplayTools(server, callApi) {
4064
4210
  server.registerTool(
4065
4211
  "lexq_replay_decision",
@@ -4067,8 +4213,8 @@ function registerReplayTools(server, callApi) {
4067
4213
  title: "Replay a Decision",
4068
4214
  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). External effects (webhooks, notifications) are always mocked \u2014 nothing fires.",
4069
4215
  inputSchema: {
4070
- traceId: z7.string().describe("Trace ID of the past execution to replay"),
4071
- candidateVersionId: z7.string().uuid().describe("Version to re-evaluate against")
4216
+ traceId: z8.string().describe("Trace ID of the past execution to replay"),
4217
+ candidateVersionId: z8.string().uuid().describe("Version to re-evaluate against")
4072
4218
  }
4073
4219
  },
4074
4220
  async ({ traceId, candidateVersionId }) => callApi("POST", "replay/decisions", { body: { traceId, candidateVersionId } })
@@ -4079,10 +4225,10 @@ function registerReplayTools(server, callApi) {
4079
4225
  title: "Start Window Replay (Blast Radius)",
4080
4226
  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.",
4081
4227
  inputSchema: {
4082
- candidateVersionId: z7.string().uuid().describe("Version to re-evaluate against"),
4083
- from: z7.string().describe("Window start date (yyyy-MM-dd)"),
4084
- to: z7.string().describe("Window end date (yyyy-MM-dd)"),
4085
- maxRecords: z7.number().int().min(1).optional().describe("Sample cap (server default applies; hard cap 50k)")
4228
+ candidateVersionId: z8.string().uuid().describe("Version to re-evaluate against"),
4229
+ from: z8.string().describe("Window start date (yyyy-MM-dd)"),
4230
+ to: z8.string().describe("Window end date (yyyy-MM-dd)"),
4231
+ maxRecords: z8.number().int().min(1).optional().describe("Sample cap (server default applies; hard cap 50k)")
4086
4232
  }
4087
4233
  },
4088
4234
  async ({ candidateVersionId, from, to, maxRecords }) => callApi("POST", "replay/jobs", { body: { candidateVersionId, from, to, maxRecords } })
@@ -4093,7 +4239,7 @@ function registerReplayTools(server, callApi) {
4093
4239
  title: "Get Replay Job Status",
4094
4240
  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.",
4095
4241
  inputSchema: {
4096
- jobId: z7.string().describe("Replay job ID from lexq_replay_start")
4242
+ jobId: z8.string().describe("Replay job ID from lexq_replay_start")
4097
4243
  }
4098
4244
  },
4099
4245
  async ({ jobId }) => callApi("GET", `replay/jobs/${jobId}`)
@@ -4104,8 +4250,8 @@ function registerReplayTools(server, callApi) {
4104
4250
  title: "List Replay Jobs",
4105
4251
  description: "List window replay job history (reverse-chronological). Lightweight items \u2014 use lexq_replay_status for summary and changed samples.",
4106
4252
  inputSchema: {
4107
- page: z7.number().int().min(0).default(0).describe("Page number"),
4108
- size: z7.number().int().min(1).max(100).default(20).describe("Page size")
4253
+ page: z8.number().int().min(0).default(0).describe("Page number"),
4254
+ size: z8.number().int().min(1).max(100).default(20).describe("Page size")
4109
4255
  }
4110
4256
  },
4111
4257
  async ({ page, size }) => callApi("GET", "replay/jobs", { params: paginationParams(page, size) })
@@ -4116,7 +4262,7 @@ function registerReplayTools(server, callApi) {
4116
4262
  title: "Cancel Replay Job",
4117
4263
  description: "Cooperatively cancel a PENDING or RUNNING window replay job. Other states are rejected. VIEWER role cannot cancel.",
4118
4264
  inputSchema: {
4119
- jobId: z7.string().describe("Replay job ID")
4265
+ jobId: z8.string().describe("Replay job ID")
4120
4266
  }
4121
4267
  },
4122
4268
  async ({ jobId }) => callApi("POST", `replay/jobs/${jobId}/cancel`)
@@ -4124,7 +4270,7 @@ function registerReplayTools(server, callApi) {
4124
4270
  }
4125
4271
 
4126
4272
  // src/mcp/tools/history.ts
4127
- import { z as z8 } from "zod";
4273
+ import { z as z9 } from "zod";
4128
4274
  function registerHistoryTools(server, callApi) {
4129
4275
  server.registerTool(
4130
4276
  "lexq_history_list",
@@ -4132,14 +4278,14 @@ function registerHistoryTools(server, callApi) {
4132
4278
  title: "List Execution History",
4133
4279
  description: "List policy execution history. Shows trace ID, group, version, status, match result, and latency.",
4134
4280
  inputSchema: {
4135
- page: z8.number().int().min(0).default(0).describe("Page number"),
4136
- size: z8.number().int().min(1).max(100).default(20).describe("Page size"),
4137
- traceId: z8.string().optional().describe("Filter by trace ID"),
4138
- groupId: z8.string().uuid().optional().describe("Filter by policy group"),
4139
- versionId: z8.string().uuid().optional().describe("Filter by version"),
4140
- status: z8.enum(["SUCCESS", "NO_MATCH", "ERROR", "TIMEOUT"]).optional().describe("Filter by execution status"),
4141
- startDate: z8.string().optional().describe("Start date (yyyy-MM-dd)"),
4142
- endDate: z8.string().optional().describe("End date (yyyy-MM-dd)")
4281
+ page: z9.number().int().min(0).default(0).describe("Page number"),
4282
+ size: z9.number().int().min(1).max(100).default(20).describe("Page size"),
4283
+ traceId: z9.string().optional().describe("Filter by trace ID"),
4284
+ groupId: z9.string().uuid().optional().describe("Filter by policy group"),
4285
+ versionId: z9.string().uuid().optional().describe("Filter by version"),
4286
+ status: z9.enum(["SUCCESS", "NO_MATCH", "ERROR", "TIMEOUT"]).optional().describe("Filter by execution status"),
4287
+ startDate: z9.string().optional().describe("Start date (yyyy-MM-dd)"),
4288
+ endDate: z9.string().optional().describe("End date (yyyy-MM-dd)")
4143
4289
  }
4144
4290
  },
4145
4291
  async ({ page, size, traceId, groupId, versionId, status, startDate, endDate }) => {
@@ -4159,7 +4305,7 @@ function registerHistoryTools(server, callApi) {
4159
4305
  title: "Get Execution Detail",
4160
4306
  description: "Get full execution detail including inputFacts, mutatedFacts, generatedVariables, executionTraces, and decisionTraces.",
4161
4307
  inputSchema: {
4162
- traceId: z8.string().describe("Trace ID from execution history")
4308
+ traceId: z9.string().describe("Trace ID from execution history")
4163
4309
  }
4164
4310
  },
4165
4311
  async ({ traceId }) => callApi("GET", `execution/history/${traceId}`)
@@ -4170,9 +4316,9 @@ function registerHistoryTools(server, callApi) {
4170
4316
  title: "Execution Statistics",
4171
4317
  description: "Get execution KPIs: total executions, success/failure counts, success rate, and average latency.",
4172
4318
  inputSchema: {
4173
- groupId: z8.string().uuid().optional().describe("Filter by policy group"),
4174
- startDate: z8.string().optional().describe("Start date (yyyy-MM-dd)"),
4175
- endDate: z8.string().optional().describe("End date (yyyy-MM-dd)")
4319
+ groupId: z9.string().uuid().optional().describe("Filter by policy group"),
4320
+ startDate: z9.string().optional().describe("Start date (yyyy-MM-dd)"),
4321
+ endDate: z9.string().optional().describe("End date (yyyy-MM-dd)")
4176
4322
  }
4177
4323
  },
4178
4324
  async ({ groupId, startDate, endDate }) => {
@@ -4186,7 +4332,7 @@ function registerHistoryTools(server, callApi) {
4186
4332
  }
4187
4333
 
4188
4334
  // src/mcp/tools/provenance.ts
4189
- import { z as z9 } from "zod";
4335
+ import { z as z10 } from "zod";
4190
4336
  function registerProvenanceTools(server, callApi) {
4191
4337
  server.registerTool(
4192
4338
  "lexq_provenance_get",
@@ -4194,7 +4340,7 @@ function registerProvenanceTools(server, callApi) {
4194
4340
  title: "Get Decision Provenance",
4195
4341
  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.",
4196
4342
  inputSchema: {
4197
- traceId: z9.string().describe("Trace ID of the execution")
4343
+ traceId: z10.string().describe("Trace ID of the execution")
4198
4344
  }
4199
4345
  },
4200
4346
  async ({ traceId }) => callApi("GET", `provenance/${traceId}`)
@@ -4205,13 +4351,13 @@ function registerProvenanceTools(server, callApi) {
4205
4351
  title: "List PII Reveal Audits",
4206
4352
  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.",
4207
4353
  inputSchema: {
4208
- page: z9.number().int().min(0).default(0).describe("Page number"),
4209
- size: z9.number().int().min(1).max(100).default(20).describe("Page size"),
4210
- traceId: z9.string().optional().describe("Filter by trace ID (exact match)"),
4211
- revealedBy: z9.string().optional().describe("Filter by operator ID (exact match)"),
4212
- factKey: z9.string().optional().describe("Filter by fact key (partial match, case-insensitive)"),
4213
- startDate: z9.string().optional().describe("Start date (yyyy-MM-dd)"),
4214
- endDate: z9.string().optional().describe("End date (yyyy-MM-dd)")
4354
+ page: z10.number().int().min(0).default(0).describe("Page number"),
4355
+ size: z10.number().int().min(1).max(100).default(20).describe("Page size"),
4356
+ traceId: z10.string().optional().describe("Filter by trace ID (exact match)"),
4357
+ revealedBy: z10.string().optional().describe("Filter by operator ID (exact match)"),
4358
+ factKey: z10.string().optional().describe("Filter by fact key (partial match, case-insensitive)"),
4359
+ startDate: z10.string().optional().describe("Start date (yyyy-MM-dd)"),
4360
+ endDate: z10.string().optional().describe("End date (yyyy-MM-dd)")
4215
4361
  }
4216
4362
  },
4217
4363
  async ({ page, size, traceId, revealedBy, factKey, startDate, endDate }) => {
@@ -4227,7 +4373,7 @@ function registerProvenanceTools(server, callApi) {
4227
4373
  }
4228
4374
 
4229
4375
  // src/mcp/tools/integrations.ts
4230
- import { z as z10 } from "zod";
4376
+ import { z as z11 } from "zod";
4231
4377
  function registerIntegrationTools(server, callApi) {
4232
4378
  server.registerTool(
4233
4379
  "lexq_integrations_list",
@@ -4235,9 +4381,9 @@ function registerIntegrationTools(server, callApi) {
4235
4381
  title: "List Integrations",
4236
4382
  description: "List all external integrations (webhooks, CRM, notification, etc.).",
4237
4383
  inputSchema: {
4238
- page: z10.number().int().min(0).default(0).describe("Page number"),
4239
- size: z10.number().int().min(1).max(100).default(20).describe("Page size"),
4240
- type: z10.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).optional().describe("Filter by integration type")
4384
+ page: z11.number().int().min(0).default(0).describe("Page number"),
4385
+ size: z11.number().int().min(1).max(100).default(20).describe("Page size"),
4386
+ type: z11.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).optional().describe("Filter by integration type")
4241
4387
  }
4242
4388
  },
4243
4389
  async ({ page, size, type }) => {
@@ -4252,7 +4398,7 @@ function registerIntegrationTools(server, callApi) {
4252
4398
  title: "Get Integration",
4253
4399
  description: "Get integration detail by ID.",
4254
4400
  inputSchema: {
4255
- integrationId: z10.string().uuid().describe("Integration ID")
4401
+ integrationId: z11.string().uuid().describe("Integration ID")
4256
4402
  }
4257
4403
  },
4258
4404
  async ({ integrationId }) => callApi("GET", `integrations/${integrationId}`)
@@ -4263,13 +4409,13 @@ function registerIntegrationTools(server, callApi) {
4263
4409
  title: "Save Integration",
4264
4410
  description: "Create or update an integration. Provide id to update an existing one; omit id to create new. Types: COUPON, POINT, NOTIFICATION, CRM, MESSENGER, WEBHOOK.",
4265
4411
  inputSchema: {
4266
- id: z10.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
4267
- type: z10.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
4268
- name: z10.string().describe("Integration name"),
4269
- baseUrl: z10.string().describe("Base URL of the external service"),
4270
- credential: z10.string().optional().describe("API key or token for the service"),
4271
- additionalConfig: z10.string().optional().describe("JSON string of additional config key-value pairs"),
4272
- isActive: z10.boolean().default(true).describe("Whether the integration is active")
4412
+ id: z11.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
4413
+ type: z11.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
4414
+ name: z11.string().describe("Integration name"),
4415
+ baseUrl: z11.string().describe("Base URL of the external service"),
4416
+ credential: z11.string().optional().describe("API key or token for the service"),
4417
+ additionalConfig: z11.string().optional().describe("JSON string of additional config key-value pairs"),
4418
+ isActive: z11.boolean().default(true).describe("Whether the integration is active")
4273
4419
  }
4274
4420
  },
4275
4421
  async ({ additionalConfig, ...rest }) => {
@@ -4284,7 +4430,7 @@ function registerIntegrationTools(server, callApi) {
4284
4430
  title: "Delete Integration",
4285
4431
  description: "Delete an integration by ID.",
4286
4432
  inputSchema: {
4287
- integrationId: z10.string().uuid().describe("Integration ID")
4433
+ integrationId: z11.string().uuid().describe("Integration ID")
4288
4434
  }
4289
4435
  },
4290
4436
  async ({ integrationId }) => callApi("DELETE", `integrations/${integrationId}`)
@@ -4301,7 +4447,7 @@ function registerIntegrationTools(server, callApi) {
4301
4447
  }
4302
4448
 
4303
4449
  // src/mcp/tools/logs.ts
4304
- import { z as z11 } from "zod";
4450
+ import { z as z12 } from "zod";
4305
4451
  function registerLogTools(server, callApi) {
4306
4452
  server.registerTool(
4307
4453
  "lexq_logs_list",
@@ -4309,14 +4455,14 @@ function registerLogTools(server, callApi) {
4309
4455
  title: "List Failure Logs",
4310
4456
  description: "List system failure logs from background tasks (webhook calls, coupon issuance, etc.).",
4311
4457
  inputSchema: {
4312
- page: z11.number().int().min(0).default(0).describe("Page number"),
4313
- size: z11.number().int().min(1).max(100).default(20).describe("Page size"),
4314
- category: z11.enum(TaskCategory).optional().describe("Task category"),
4315
- taskType: z11.enum(TaskType).optional().describe("Task type"),
4316
- status: z11.enum(FailureStatus).optional().describe("Log status"),
4317
- keyword: z11.string().optional().describe("Search in refId, refSubId, errorMessage"),
4318
- startDate: z11.string().optional().describe("Start date (yyyy-MM-dd)"),
4319
- endDate: z11.string().optional().describe("End date (yyyy-MM-dd)")
4458
+ page: z12.number().int().min(0).default(0).describe("Page number"),
4459
+ size: z12.number().int().min(1).max(100).default(20).describe("Page size"),
4460
+ category: z12.enum(TaskCategory).optional().describe("Task category"),
4461
+ taskType: z12.enum(TaskType).optional().describe("Task type"),
4462
+ status: z12.enum(FailureStatus).optional().describe("Log status"),
4463
+ keyword: z12.string().optional().describe("Search in refId, refSubId, errorMessage"),
4464
+ startDate: z12.string().optional().describe("Start date (yyyy-MM-dd)"),
4465
+ endDate: z12.string().optional().describe("End date (yyyy-MM-dd)")
4320
4466
  }
4321
4467
  },
4322
4468
  async ({ page, size, category, taskType, status, keyword, startDate, endDate }) => {
@@ -4336,7 +4482,7 @@ function registerLogTools(server, callApi) {
4336
4482
  title: "Get Failure Log",
4337
4483
  description: "Get failure log detail by ID.",
4338
4484
  inputSchema: {
4339
- logId: z11.string().uuid().describe("Failure log ID")
4485
+ logId: z12.string().uuid().describe("Failure log ID")
4340
4486
  }
4341
4487
  },
4342
4488
  async ({ logId }) => callApi("GET", `failure-logs/${logId}`)
@@ -4347,8 +4493,8 @@ function registerLogTools(server, callApi) {
4347
4493
  title: "Process Failure Log",
4348
4494
  description: "Process a single failure log: RETRY (re-execute with original payload), RESOLVE (mark as manually fixed), or IGNORE (skip intentionally).",
4349
4495
  inputSchema: {
4350
- logId: z11.string().uuid().describe("Failure log ID"),
4351
- action: z11.enum(FailureAction).describe("Action to take")
4496
+ logId: z12.string().uuid().describe("Failure log ID"),
4497
+ action: z12.enum(FailureAction).describe("Action to take")
4352
4498
  }
4353
4499
  },
4354
4500
  async ({ logId, action }) => callApi("POST", `failure-logs/${logId}/actions`, {
@@ -4361,8 +4507,8 @@ function registerLogTools(server, callApi) {
4361
4507
  title: "Bulk Process Failure Logs",
4362
4508
  description: "Process multiple failure logs at once. Provide an array of log IDs and the action.",
4363
4509
  inputSchema: {
4364
- logIds: z11.array(z11.string().uuid()).describe("Array of failure log IDs"),
4365
- action: z11.enum(FailureAction).describe("Action to apply to all logs")
4510
+ logIds: z12.array(z12.string().uuid()).describe("Array of failure log IDs"),
4511
+ action: z12.enum(FailureAction).describe("Action to apply to all logs")
4366
4512
  }
4367
4513
  },
4368
4514
  async ({ logIds, action }) => callApi("POST", "failure-logs/bulk-actions", {
@@ -4372,7 +4518,7 @@ function registerLogTools(server, callApi) {
4372
4518
  }
4373
4519
 
4374
4520
  // src/mcp/tools/webhook-subscriptions.ts
4375
- import { z as z12 } from "zod";
4521
+ import { z as z13 } from "zod";
4376
4522
  function registerWebhookSubscriptionTools(server, callApi) {
4377
4523
  server.registerTool(
4378
4524
  "lexq_webhook_subscriptions_list",
@@ -4380,8 +4526,8 @@ function registerWebhookSubscriptionTools(server, callApi) {
4380
4526
  title: "List Webhook Subscriptions",
4381
4527
  description: "List platform event webhook subscriptions. These receive deployment lifecycle notifications (publish, deploy, rollback, undeploy).",
4382
4528
  inputSchema: {
4383
- page: z12.number().int().min(0).default(0).describe("Page number"),
4384
- size: z12.number().int().min(1).max(100).default(20).describe("Page size")
4529
+ page: z13.number().int().min(0).default(0).describe("Page number"),
4530
+ size: z13.number().int().min(1).max(100).default(20).describe("Page size")
4385
4531
  }
4386
4532
  },
4387
4533
  async ({ page, size }) => {
@@ -4395,7 +4541,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
4395
4541
  title: "Get Webhook Subscription",
4396
4542
  description: "Get webhook subscription detail by ID.",
4397
4543
  inputSchema: {
4398
- id: z12.string().uuid().describe("Webhook subscription ID")
4544
+ id: z13.string().uuid().describe("Webhook subscription ID")
4399
4545
  }
4400
4546
  },
4401
4547
  async ({ id }) => callApi("GET", `webhook-subscriptions/${id}`)
@@ -4406,13 +4552,13 @@ function registerWebhookSubscriptionTools(server, callApi) {
4406
4552
  title: "Save Webhook Subscription",
4407
4553
  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": "..."}).',
4408
4554
  inputSchema: {
4409
- id: z12.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
4410
- name: z12.string().min(1).describe("Subscription name (unique per tenant)"),
4411
- webhookUrl: z12.string().url().describe("Webhook endpoint URL"),
4412
- subscribedEvents: z12.array(z12.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
4413
- payloadFormat: z12.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
4414
- secret: z12.string().optional().describe("HMAC-SHA256 signing secret"),
4415
- isActive: z12.boolean().optional().default(true).describe("Whether the subscription is active")
4555
+ id: z13.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
4556
+ name: z13.string().min(1).describe("Subscription name (unique per tenant)"),
4557
+ webhookUrl: z13.string().url().describe("Webhook endpoint URL"),
4558
+ subscribedEvents: z13.array(z13.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
4559
+ payloadFormat: z13.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
4560
+ secret: z13.string().optional().describe("HMAC-SHA256 signing secret"),
4561
+ isActive: z13.boolean().optional().default(true).describe("Whether the subscription is active")
4416
4562
  }
4417
4563
  },
4418
4564
  async ({ ...body }) => callApi("POST", "webhook-subscriptions", { body })
@@ -4423,7 +4569,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
4423
4569
  title: "Delete Webhook Subscription",
4424
4570
  description: "Delete a webhook subscription by ID.",
4425
4571
  inputSchema: {
4426
- id: z12.string().uuid().describe("Webhook subscription ID")
4572
+ id: z13.string().uuid().describe("Webhook subscription ID")
4427
4573
  }
4428
4574
  },
4429
4575
  async ({ id }) => callApi("DELETE", `webhook-subscriptions/${id}`)
@@ -4434,7 +4580,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
4434
4580
  title: "Test Webhook Subscription",
4435
4581
  description: "Send a test event to verify webhook connectivity. Returns the HTTP status code and success/failure message.",
4436
4582
  inputSchema: {
4437
- id: z12.string().uuid().describe("Webhook subscription ID")
4583
+ id: z13.string().uuid().describe("Webhook subscription ID")
4438
4584
  }
4439
4585
  },
4440
4586
  async ({ id }) => callApi("POST", `webhook-subscriptions/${id}/test`)
@@ -4442,7 +4588,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
4442
4588
  }
4443
4589
 
4444
4590
  // src/mcp/tools/domain-templates.ts
4445
- import { z as z13 } from "zod";
4591
+ import { z as z14 } from "zod";
4446
4592
  function registerDomainTemplateTools(server, callApi) {
4447
4593
  server.registerTool(
4448
4594
  "lexq_domain_templates_list",
@@ -4459,7 +4605,7 @@ function registerDomainTemplateTools(server, callApi) {
4459
4605
  title: "Preview Domain Template",
4460
4606
  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.",
4461
4607
  inputSchema: {
4462
- template: z13.string().describe(
4608
+ template: z14.string().describe(
4463
4609
  "Domain template key (e.g. ECOMMERCE). Use lexq_domain_templates_list to see available keys \u2014 currently only ECOMMERCE is ACTIVE."
4464
4610
  )
4465
4611
  }
@@ -4472,8 +4618,8 @@ function registerDomainTemplateTools(server, callApi) {
4472
4618
  title: "Apply Domain Template",
4473
4619
  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.",
4474
4620
  inputSchema: {
4475
- template: z13.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
4476
- customName: z13.string().optional().describe(
4621
+ template: z14.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
4622
+ customName: z14.string().optional().describe(
4477
4623
  "Optional custom name for the policy group that gets created. If omitted, the template's default name is used."
4478
4624
  )
4479
4625
  }
@@ -4495,6 +4641,7 @@ function registerAllTools(server, callApi) {
4495
4641
  registerFactTools(server, callApi);
4496
4642
  registerDeployTools(server, callApi);
4497
4643
  registerAnalyticsTools(server, callApi);
4644
+ registerProfileTools(server, callApi);
4498
4645
  registerReplayTools(server, callApi);
4499
4646
  registerHistoryTools(server, callApi);
4500
4647
  registerProvenanceTools(server, callApi);
@@ -4529,7 +4676,7 @@ async function startMcpServer() {
4529
4676
  function registerServeCommand(program) {
4530
4677
  program.command("serve").description("Start LexQ as a server for AI agent integrations").option("--mcp", "Start as MCP (Model Context Protocol) server over stdio").addHelpText(
4531
4678
  "after",
4532
- dedent17`
4679
+ dedent18`
4533
4680
 
4534
4681
  Example:
4535
4682
  $ lexq serve --mcp
@@ -4560,11 +4707,11 @@ function registerServeCommand(program) {
4560
4707
 
4561
4708
  // src/commands/domain-templates.ts
4562
4709
  import "commander";
4563
- import dedent18 from "dedent";
4710
+ import dedent19 from "dedent";
4564
4711
  function registerDomainTemplateCommands(program) {
4565
4712
  const templates = program.command("domain-templates").description("Browse and apply domain templates").addHelpText(
4566
4713
  "after",
4567
- dedent18`
4714
+ dedent19`
4568
4715
 
4569
4716
  A domain template is an industry-specific starter pack of fact
4570
4717
  definitions and sample rules. Applying one provisions a ready-to-use
@@ -4612,7 +4759,7 @@ function registerDomainTemplateCommands(program) {
4612
4759
  });
4613
4760
  templates.command("preview").description("Preview what a domain template provisions").requiredOption("--template <key>", "Domain template key (e.g. ECOMMERCE)").addHelpText(
4614
4761
  "after",
4615
- dedent18`
4762
+ dedent19`
4616
4763
 
4617
4764
  Read-only dry run — shows the fact definitions and sample rules the
4618
4765
  template will create. Nothing is provisioned.
@@ -4641,7 +4788,7 @@ function registerDomainTemplateCommands(program) {
4641
4788
  });
4642
4789
  templates.command("apply").description("Apply a domain template to the current tenant").requiredOption("--template <key>", "Domain template key (e.g. ECOMMERCE)").option("--name <n>", "Custom name for the policy group that gets created").option("--force", "Skip confirmation prompt").addHelpText(
4643
4790
  "after",
4644
- dedent18`
4791
+ dedent19`
4645
4792
 
4646
4793
  Creates the template's fact definitions and a new DRAFT policy group
4647
4794
  populated with its sample rules. Existing facts are skipped — apply is
@@ -4716,6 +4863,7 @@ function createCli() {
4716
4863
  registerDomainTemplateCommands(program);
4717
4864
  registerDeployCommands(program);
4718
4865
  registerAnalyticsCommands(program);
4866
+ registerProfileCommands(program);
4719
4867
  registerHistoryCommands(program);
4720
4868
  registerReplayCommands(program);
4721
4869
  registerProvenanceCommands(program);