@lexq/cli 0.1.33 → 0.1.35

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
@@ -2327,13 +2327,240 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2327
2327
  });
2328
2328
  }
2329
2329
 
2330
- // src/commands/integrations.ts
2330
+ // src/commands/replay.ts
2331
2331
  import "commander";
2332
2332
  import dedent10 from "dedent";
2333
+ function registerReplayCommands(program) {
2334
+ const replay = program.command("replay").description("Decision Replay").addHelpText(
2335
+ "after",
2336
+ dedent10`
2337
+
2338
+ Re-evaluate past production executions against a candidate version.
2339
+
2340
+ Commands:
2341
+ decision Replay one execution and show the decision diff (sync, free)
2342
+ start Submit a window replay job — blast radius (async, billed)
2343
+ list List replay job history
2344
+ get Poll a job's status, summary, and changed samples
2345
+ cancel Cancel a PENDING/RUNNING job
2346
+
2347
+ External effects (webhooks, notifications) are always mocked during replay.
2348
+ `
2349
+ );
2350
+ 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(
2351
+ "after",
2352
+ dedent10`
2353
+
2354
+ Free of charge (TPS throttle only). Returns decisionChanged, effect
2355
+ changes, fired rules on both sides, and a determinism verdict.
2356
+
2357
+ Example:
2358
+ $ lexq replay decision --trace-id <tid> --version-id <vid>
2359
+ `
2360
+ ).action(async (opts) => {
2361
+ try {
2362
+ const globalOpts = program.opts();
2363
+ const data = await apiRequest("POST", "replay/decisions", {
2364
+ apiKey: globalOpts.apiKey,
2365
+ baseUrl: globalOpts.baseUrl,
2366
+ dryRun: globalOpts.dryRun,
2367
+ verbose: globalOpts.verbose,
2368
+ body: { traceId: opts.traceId, candidateVersionId: opts.versionId }
2369
+ });
2370
+ printJson(data);
2371
+ } catch (error) {
2372
+ printError(error);
2373
+ process.exit(1);
2374
+ }
2375
+ });
2376
+ 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(
2377
+ "after",
2378
+ dedent10`
2379
+
2380
+ Billed per replayed record (REPLAY metric). Poll with "lexq replay get".
2381
+
2382
+ Example:
2383
+ $ lexq replay start --version-id <vid> --from 2026-06-01 --to 2026-06-30
2384
+ `
2385
+ ).action(async (opts) => {
2386
+ try {
2387
+ const globalOpts = program.opts();
2388
+ const body = {
2389
+ candidateVersionId: opts.versionId,
2390
+ from: opts.from,
2391
+ to: opts.to
2392
+ };
2393
+ if (opts.maxRecords) body.maxRecords = Number(opts.maxRecords);
2394
+ const data = await apiRequest("POST", "replay/jobs", {
2395
+ apiKey: globalOpts.apiKey,
2396
+ baseUrl: globalOpts.baseUrl,
2397
+ dryRun: globalOpts.dryRun,
2398
+ verbose: globalOpts.verbose,
2399
+ body
2400
+ });
2401
+ printJson(data);
2402
+ } catch (error) {
2403
+ printError(error);
2404
+ process.exit(1);
2405
+ }
2406
+ });
2407
+ replay.command("list").description("List replay jobs").option("--page <number>", "Page number", "0").option("--size <number>", "Page size", "20").action(async (opts) => {
2408
+ try {
2409
+ const globalOpts = program.opts();
2410
+ const format = globalOpts.format ?? "json";
2411
+ const data = await apiRequest("GET", "replay/jobs", {
2412
+ apiKey: globalOpts.apiKey,
2413
+ baseUrl: globalOpts.baseUrl,
2414
+ dryRun: globalOpts.dryRun,
2415
+ verbose: globalOpts.verbose,
2416
+ params: { page: opts.page, size: opts.size }
2417
+ });
2418
+ if (format === "table") {
2419
+ printTable(
2420
+ ["Job", "Version", "Window", "Status", "Progress", "Changed", "At"],
2421
+ data.content.map((j) => [
2422
+ j.jobId.substring(0, 12),
2423
+ j.candidateVersionName ?? "\u2013",
2424
+ `${j.fromDate}~${j.toDate}`,
2425
+ j.status,
2426
+ `${j.progress}%`,
2427
+ `${j.changedCount}${j.capped ? " (capped)" : ""}`,
2428
+ j.createdAt.substring(0, 16)
2429
+ ]),
2430
+ { truncate: 24 }
2431
+ );
2432
+ console.log(`
2433
+ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2434
+ } else {
2435
+ printJson(data);
2436
+ }
2437
+ } catch (error) {
2438
+ printError(error);
2439
+ process.exit(1);
2440
+ }
2441
+ });
2442
+ replay.command("get").description("Get replay job status and results").requiredOption("--id <jobId>", "Replay job ID").action(async (opts) => {
2443
+ try {
2444
+ const globalOpts = program.opts();
2445
+ const data = await apiRequest("GET", `replay/jobs/${opts.id}`, {
2446
+ apiKey: globalOpts.apiKey,
2447
+ baseUrl: globalOpts.baseUrl,
2448
+ dryRun: globalOpts.dryRun,
2449
+ verbose: globalOpts.verbose
2450
+ });
2451
+ printJson(data);
2452
+ } catch (error) {
2453
+ printError(error);
2454
+ process.exit(1);
2455
+ }
2456
+ });
2457
+ replay.command("cancel").description("Cancel a PENDING/RUNNING replay job").requiredOption("--id <jobId>", "Replay job ID").action(async (opts) => {
2458
+ try {
2459
+ const globalOpts = program.opts();
2460
+ const data = await apiRequest("POST", `replay/jobs/${opts.id}/cancel`, {
2461
+ apiKey: globalOpts.apiKey,
2462
+ baseUrl: globalOpts.baseUrl,
2463
+ dryRun: globalOpts.dryRun,
2464
+ verbose: globalOpts.verbose
2465
+ });
2466
+ printJson(data);
2467
+ } catch (error) {
2468
+ printError(error);
2469
+ process.exit(1);
2470
+ }
2471
+ });
2472
+ }
2473
+
2474
+ // src/commands/provenance.ts
2475
+ import "commander";
2476
+ import dedent11 from "dedent";
2477
+ function registerProvenanceCommands(program) {
2478
+ const provenance = program.command("provenance").description("Decision Provenance").addHelpText(
2479
+ "after",
2480
+ dedent11`
2481
+
2482
+ Trace who authored, published, and deployed the rules behind a decision.
2483
+
2484
+ Commands:
2485
+ get Get the lineage of one decision (PII facts masked)
2486
+ reveal-audits List the PII reveal audit ledger (metadata only)
2487
+ `
2488
+ );
2489
+ provenance.command("get").description("Get decision lineage").requiredOption("--trace-id <traceId>", "Trace ID of the execution").action(async (opts) => {
2490
+ try {
2491
+ const globalOpts = program.opts();
2492
+ const data = await apiRequest("GET", `provenance/${opts.traceId}`, {
2493
+ apiKey: globalOpts.apiKey,
2494
+ baseUrl: globalOpts.baseUrl,
2495
+ dryRun: globalOpts.dryRun,
2496
+ verbose: globalOpts.verbose
2497
+ });
2498
+ printJson(data);
2499
+ } catch (error) {
2500
+ printError(error);
2501
+ process.exit(1);
2502
+ }
2503
+ });
2504
+ 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(
2505
+ "after",
2506
+ dedent11`
2507
+
2508
+ Metadata only — revealed values are never stored or returned.
2509
+
2510
+ Example:
2511
+ $ lexq provenance reveal-audits --start-date 2026-07-01 --format table
2512
+ `
2513
+ ).action(async (opts) => {
2514
+ try {
2515
+ const globalOpts = program.opts();
2516
+ const format = globalOpts.format ?? "json";
2517
+ const params = { page: opts.page, size: opts.size };
2518
+ if (opts.traceId) params.traceId = opts.traceId;
2519
+ if (opts.factKey) params.factKey = opts.factKey;
2520
+ if (opts.revealedBy) params.revealedBy = opts.revealedBy;
2521
+ if (opts.startDate) params.startDate = opts.startDate;
2522
+ if (opts.endDate) params.endDate = opts.endDate;
2523
+ const data = await apiRequest(
2524
+ "GET",
2525
+ "provenance/reveal-audits",
2526
+ {
2527
+ apiKey: globalOpts.apiKey,
2528
+ baseUrl: globalOpts.baseUrl,
2529
+ dryRun: globalOpts.dryRun,
2530
+ verbose: globalOpts.verbose,
2531
+ params
2532
+ }
2533
+ );
2534
+ if (format === "table") {
2535
+ printTable(
2536
+ ["Revealed At", "By", "Fact Key", "Trace"],
2537
+ data.content.map((a) => [
2538
+ a.revealedAt.substring(0, 16),
2539
+ a.revealedByName,
2540
+ a.factKey,
2541
+ a.traceId.substring(0, 12)
2542
+ ]),
2543
+ { truncate: 24 }
2544
+ );
2545
+ console.log(`
2546
+ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2547
+ } else {
2548
+ printJson(data);
2549
+ }
2550
+ } catch (error) {
2551
+ printError(error);
2552
+ process.exit(1);
2553
+ }
2554
+ });
2555
+ }
2556
+
2557
+ // src/commands/integrations.ts
2558
+ import "commander";
2559
+ import dedent12 from "dedent";
2333
2560
  function registerIntegrationCommands(program) {
2334
2561
  const integrations = program.command("integrations").description("Manage external integrations").addHelpText(
2335
2562
  "after",
2336
- dedent10`
2563
+ dedent12`
2337
2564
 
2338
2565
  Integrations connect rule actions to external services (webhooks, coupons,
2339
2566
  points, notifications, CRM, messengers).
@@ -2403,7 +2630,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2403
2630
  });
2404
2631
  integrations.command("save").description("Create or update an integration").requiredOption("--json <body>", "Request body as JSON string").addHelpText(
2405
2632
  "after",
2406
- dedent10`
2633
+ dedent12`
2407
2634
 
2408
2635
  Examples:
2409
2636
  # Create
@@ -2453,7 +2680,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2453
2680
  });
2454
2681
  integrations.command("delete").description("Delete an integration").requiredOption("--id <integrationId>", "Integration ID").option("--force", "Skip confirmation prompt").addHelpText(
2455
2682
  "after",
2456
- dedent10`
2683
+ dedent12`
2457
2684
 
2458
2685
  Rules referencing this integration will fail at execution time.
2459
2686
  Use --force to skip confirmation.
@@ -2485,7 +2712,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2485
2712
  });
2486
2713
  integrations.command("config-spec").description("Get integration configuration field specs").addHelpText(
2487
2714
  "after",
2488
- dedent10`
2715
+ dedent12`
2489
2716
 
2490
2717
  Shows required and optional configuration fields for each integration type.
2491
2718
 
@@ -2511,7 +2738,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2511
2738
 
2512
2739
  // src/commands/logs.ts
2513
2740
  import "commander";
2514
- import dedent11 from "dedent";
2741
+ import dedent13 from "dedent";
2515
2742
 
2516
2743
  // src/types/enums.ts
2517
2744
  var FailureStatus = ["PENDING", "RESOLVED", "IGNORED"];
@@ -2545,7 +2772,7 @@ var WebhookPayloadFormat = ["GENERIC", "SLACK"];
2545
2772
  function registerLogCommands(program) {
2546
2773
  const logs = program.command("logs").description("Failure logs").addHelpText(
2547
2774
  "after",
2548
- dedent11`
2775
+ dedent13`
2549
2776
 
2550
2777
  System failure logs (DLQ) for background tasks — webhook calls, coupon issuance,
2551
2778
  point operations, notifications, and platform event webhooks.
@@ -2562,7 +2789,7 @@ function registerLogCommands(program) {
2562
2789
  );
2563
2790
  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(
2564
2791
  "after",
2565
- dedent11`
2792
+ dedent13`
2566
2793
 
2567
2794
  Examples:
2568
2795
  $ lexq logs list --status PENDING --format table
@@ -2613,7 +2840,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2613
2840
  });
2614
2841
  logs.command("get").description("Get failure log detail").requiredOption("--id <logId>", "Log ID").addHelpText(
2615
2842
  "after",
2616
- dedent11`
2843
+ dedent13`
2617
2844
 
2618
2845
  Includes the full payload that was used for the failed operation.
2619
2846
  Use this to inspect what went wrong before deciding to RETRY or RESOLVE.
@@ -2635,7 +2862,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2635
2862
  });
2636
2863
  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(
2637
2864
  "after",
2638
- dedent11`
2865
+ dedent13`
2639
2866
 
2640
2867
  Actions:
2641
2868
  RETRY Re-execute the failed operation with the original payload
@@ -2667,7 +2894,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2667
2894
  });
2668
2895
  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(
2669
2896
  "after",
2670
- dedent11`
2897
+ dedent13`
2671
2898
 
2672
2899
  Processes each log individually. Failures are skipped with a warning.
2673
2900
 
@@ -2695,11 +2922,11 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2695
2922
 
2696
2923
  // src/commands/webhook-subscriptions.ts
2697
2924
  import "commander";
2698
- import dedent12 from "dedent";
2925
+ import dedent14 from "dedent";
2699
2926
  function registerWebhookSubscriptionCommands(program) {
2700
2927
  const webhooks = program.command("webhook-subscriptions").description("Manage platform event webhook subscriptions").addHelpText(
2701
2928
  "after",
2702
- dedent12`
2929
+ dedent14`
2703
2930
 
2704
2931
  Receive notifications when deployment lifecycle events occur
2705
2932
  (publish, deploy, rollback, undeploy).
@@ -2773,7 +3000,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2773
3000
  });
2774
3001
  webhooks.command("save").description("Create or update a webhook subscription").requiredOption("--json <body>", "Request body as JSON string").addHelpText(
2775
3002
  "after",
2776
- dedent12`
3003
+ dedent14`
2777
3004
 
2778
3005
  Examples:
2779
3006
  # Create (Slack format)
@@ -2828,7 +3055,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2828
3055
  });
2829
3056
  webhooks.command("delete").description("Delete a webhook subscription").requiredOption("--id <subscriptionId>", "Subscription ID").option("--force", "Skip confirmation prompt").addHelpText(
2830
3057
  "after",
2831
- dedent12`
3058
+ dedent14`
2832
3059
 
2833
3060
  Use --force to skip the confirmation prompt.
2834
3061
 
@@ -2862,7 +3089,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2862
3089
  });
2863
3090
  webhooks.command("test").description("Send a test event to verify webhook connectivity").requiredOption("--id <subscriptionId>", "Subscription ID").addHelpText(
2864
3091
  "after",
2865
- dedent12`
3092
+ dedent14`
2866
3093
 
2867
3094
  Sends a test event to the webhook URL and reports the HTTP status code.
2868
3095
  Does not record failures in the failure log.
@@ -2898,7 +3125,7 @@ ${data.totalElements} total \xB7 page ${data.pageNo + 1}/${data.totalPages}`);
2898
3125
 
2899
3126
  // src/commands/serve.ts
2900
3127
  import "commander";
2901
- import dedent15 from "dedent";
3128
+ import dedent17 from "dedent";
2902
3129
 
2903
3130
  // src/mcp/server.ts
2904
3131
  import { readFileSync as readFileSync3 } from "fs";
@@ -3220,7 +3447,7 @@ function registerVersionTools(server, callApi) {
3220
3447
 
3221
3448
  // src/mcp/tools/rules.ts
3222
3449
  import { z as z3 } from "zod";
3223
- import dedent13 from "dedent";
3450
+ import dedent15 from "dedent";
3224
3451
  function registerRuleTools(server, callApi) {
3225
3452
  server.registerTool(
3226
3453
  "lexq_rules_list",
@@ -3251,7 +3478,7 @@ function registerRuleTools(server, callApi) {
3251
3478
  "lexq_rules_create",
3252
3479
  {
3253
3480
  title: "Create Rule",
3254
- description: dedent13`
3481
+ description: dedent15`
3255
3482
  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.
3256
3483
 
3257
3484
  Before creating rules with new fact keys, call lexq_facts_list to check existing facts.
@@ -3593,13 +3820,13 @@ function registerDeployTools(server, callApi) {
3593
3820
 
3594
3821
  // src/mcp/tools/analytics.ts
3595
3822
  import { z as z6 } from "zod";
3596
- import dedent14 from "dedent";
3823
+ import dedent16 from "dedent";
3597
3824
  function registerAnalyticsTools(server, callApi) {
3598
3825
  server.registerTool(
3599
3826
  "lexq_dry_run",
3600
3827
  {
3601
3828
  title: "Dry Run",
3602
- description: dedent14`
3829
+ description: dedent16`
3603
3830
  Execute a single dry run against a version. Tests how rules evaluate given input facts without side effects.
3604
3831
 
3605
3832
  Returns:
@@ -3629,7 +3856,7 @@ function registerAnalyticsTools(server, callApi) {
3629
3856
  "lexq_dry_run_compare",
3630
3857
  {
3631
3858
  title: "Dry Run Compare",
3632
- description: dedent14`
3859
+ description: dedent16`
3633
3860
  Compare dry run results between two versions using the same input facts. Useful for validating changes.
3634
3861
 
3635
3862
  Returns:
@@ -3665,22 +3892,33 @@ function registerAnalyticsTools(server, callApi) {
3665
3892
  "lexq_simulation_start",
3666
3893
  {
3667
3894
  title: "Start Simulation",
3668
- description: dedent14`
3669
- Start an Impact Simulation against historical or uploaded data.
3895
+ description: dedent16`
3896
+ Start an Impact Simulation against historical, uploaded, or inline data.
3897
+
3898
+ dataset.type and dataset.source are BOTH required, and must be paired:
3899
+ HISTORICAL → source EXECUTION_LOGS, with dataset.from / dataset.to (yyyy-MM-dd)
3900
+ UPLOADED → source S3_BUCKET, with dataset.path (the path returned by lexq_dataset_upload)
3901
+ MANUAL → source REQUEST_BODY, with dataset.manualData (array of fact records)
3670
3902
 
3671
- dataset.type: "HISTORICAL" or "UPLOADED"
3672
- dataset.source (when HISTORICAL): "EXECUTION_LOGS"
3673
- dataset.from / dataset.to: date range (yyyy-MM-dd, when HISTORICAL)
3674
3903
  options.maxRecords: number (max 100000, default 10000)
3675
- options.baselinePolicyVersionId: uuid (optional, for comparison)
3904
+ options.baselinePolicyVersionId: uuid (optional, for baseline comparison)
3676
3905
  options.includeRuleStats: boolean
3906
+ options.metricConfig: optional — omit for plain execution count. To aggregate a fact, pass
3907
+ { "targetVariable": "<fact>", "aggregationType": "COUNT" | "SUM" | "AVG" }
3677
3908
 
3678
- Example body:
3909
+ Example (uploaded dataset):
3679
3910
  {
3680
3911
  "policyVersionId": "<uuid>",
3681
- "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2025-01-01", "to": "2025-01-31" },
3912
+ "dataset": { "type": "UPLOADED", "source": "S3_BUCKET", "path": "<path from lexq_dataset_upload>" },
3682
3913
  "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true, "maxRecords": 10000 }
3683
3914
  }
3915
+
3916
+ Example (historical):
3917
+ {
3918
+ "policyVersionId": "<uuid>",
3919
+ "dataset": { "type": "HISTORICAL", "source": "EXECUTION_LOGS", "from": "2026-01-01", "to": "2026-01-31" },
3920
+ "options": { "baselinePolicyVersionId": "<uuid>", "includeRuleStats": true }
3921
+ }
3684
3922
  `,
3685
3923
  inputSchema: {
3686
3924
  body: z6.string().describe("JSON string of SimulationRequest")
@@ -3752,10 +3990,12 @@ function registerAnalyticsTools(server, callApi) {
3752
3990
  "lexq_dataset_upload",
3753
3991
  {
3754
3992
  title: "Upload Dataset",
3755
- description: dedent14`
3993
+ description: dedent16`
3756
3994
  Upload inline CSV or JSON content as a simulation dataset.
3757
- The content is uploaded to S3 and a path is returned.
3758
- Use this path in simulation start with dataset type UPLOADED.
3995
+ The content is uploaded to S3 and a path is returned in the "path" field.
3996
+
3997
+ To use the returned path in lexq_simulation_start, set:
3998
+ dataset: { "type": "UPLOADED", "source": "S3_BUCKET", "path": "<returned path>" }
3759
3999
 
3760
4000
  CSV example:
3761
4001
  user_id,payment_amount
@@ -3769,9 +4009,25 @@ function registerAnalyticsTools(server, callApi) {
3769
4009
  filename: z6.string().default("dataset.csv").describe("Filename with extension (.csv or .json)")
3770
4010
  }
3771
4011
  },
3772
- async ({ content, filename }) => callApi("POST", "analytics/datasets/upload", {
3773
- upload: { content, filename, fieldName: "file" }
3774
- })
4012
+ async ({ content, filename }) => {
4013
+ const result = await callApi("POST", "analytics/datasets/upload", {
4014
+ upload: { content, filename, fieldName: "file" }
4015
+ });
4016
+ if (!result.isError) {
4017
+ try {
4018
+ const uploaded = JSON.parse(result.content[0]?.text ?? "{}");
4019
+ if (uploaded.path) {
4020
+ const dataset = { type: "UPLOADED", source: "S3_BUCKET", path: uploaded.path };
4021
+ result.content.push({
4022
+ type: "text",
4023
+ text: "Ready-to-use dataset block for lexq_simulation_start:\n" + JSON.stringify({ dataset }, null, 2)
4024
+ });
4025
+ }
4026
+ } catch {
4027
+ }
4028
+ }
4029
+ return result;
4030
+ }
3775
4031
  );
3776
4032
  server.registerTool(
3777
4033
  "lexq_dataset_template",
@@ -3790,8 +4046,73 @@ function registerAnalyticsTools(server, callApi) {
3790
4046
  );
3791
4047
  }
3792
4048
 
3793
- // src/mcp/tools/history.ts
4049
+ // src/mcp/tools/replay.ts
3794
4050
  import { z as z7 } from "zod";
4051
+ function registerReplayTools(server, callApi) {
4052
+ server.registerTool(
4053
+ "lexq_replay_decision",
4054
+ {
4055
+ title: "Replay a Decision",
4056
+ 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.",
4057
+ inputSchema: {
4058
+ traceId: z7.string().describe("Trace ID of the past execution to replay"),
4059
+ candidateVersionId: z7.string().uuid().describe("Version to re-evaluate against")
4060
+ }
4061
+ },
4062
+ async ({ traceId, candidateVersionId }) => callApi("POST", "replay/decisions", { body: { traceId, candidateVersionId } })
4063
+ );
4064
+ server.registerTool(
4065
+ "lexq_replay_start",
4066
+ {
4067
+ title: "Start Window Replay (Blast Radius)",
4068
+ 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.",
4069
+ inputSchema: {
4070
+ candidateVersionId: z7.string().uuid().describe("Version to re-evaluate against"),
4071
+ from: z7.string().describe("Window start date (yyyy-MM-dd)"),
4072
+ to: z7.string().describe("Window end date (yyyy-MM-dd)"),
4073
+ maxRecords: z7.number().int().min(1).optional().describe("Sample cap (server default applies; hard cap 50k)")
4074
+ }
4075
+ },
4076
+ async ({ candidateVersionId, from, to, maxRecords }) => callApi("POST", "replay/jobs", { body: { candidateVersionId, from, to, maxRecords } })
4077
+ );
4078
+ server.registerTool(
4079
+ "lexq_replay_status",
4080
+ {
4081
+ title: "Get Replay Job Status",
4082
+ 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.",
4083
+ inputSchema: {
4084
+ jobId: z7.string().describe("Replay job ID from lexq_replay_start")
4085
+ }
4086
+ },
4087
+ async ({ jobId }) => callApi("GET", `replay/jobs/${jobId}`)
4088
+ );
4089
+ server.registerTool(
4090
+ "lexq_replay_list",
4091
+ {
4092
+ title: "List Replay Jobs",
4093
+ description: "List window replay job history (reverse-chronological). Lightweight items \u2014 use lexq_replay_status for summary and changed samples.",
4094
+ inputSchema: {
4095
+ page: z7.number().int().min(0).default(0).describe("Page number"),
4096
+ size: z7.number().int().min(1).max(100).default(20).describe("Page size")
4097
+ }
4098
+ },
4099
+ async ({ page, size }) => callApi("GET", "replay/jobs", { params: paginationParams(page, size) })
4100
+ );
4101
+ server.registerTool(
4102
+ "lexq_replay_cancel",
4103
+ {
4104
+ title: "Cancel Replay Job",
4105
+ description: "Cooperatively cancel a PENDING or RUNNING window replay job. Other states are rejected. VIEWER role cannot cancel.",
4106
+ inputSchema: {
4107
+ jobId: z7.string().describe("Replay job ID")
4108
+ }
4109
+ },
4110
+ async ({ jobId }) => callApi("POST", `replay/jobs/${jobId}/cancel`)
4111
+ );
4112
+ }
4113
+
4114
+ // src/mcp/tools/history.ts
4115
+ import { z as z8 } from "zod";
3795
4116
  function registerHistoryTools(server, callApi) {
3796
4117
  server.registerTool(
3797
4118
  "lexq_history_list",
@@ -3799,14 +4120,14 @@ function registerHistoryTools(server, callApi) {
3799
4120
  title: "List Execution History",
3800
4121
  description: "List policy execution history. Shows trace ID, group, version, status, match result, and latency.",
3801
4122
  inputSchema: {
3802
- page: z7.number().int().min(0).default(0).describe("Page number"),
3803
- size: z7.number().int().min(1).max(100).default(20).describe("Page size"),
3804
- traceId: z7.string().optional().describe("Filter by trace ID"),
3805
- groupId: z7.string().uuid().optional().describe("Filter by policy group"),
3806
- versionId: z7.string().uuid().optional().describe("Filter by version"),
3807
- status: z7.enum(["SUCCESS", "NO_MATCH", "ERROR", "TIMEOUT"]).optional().describe("Filter by execution status"),
3808
- startDate: z7.string().optional().describe("Start date (yyyy-MM-dd)"),
3809
- endDate: z7.string().optional().describe("End date (yyyy-MM-dd)")
4123
+ page: z8.number().int().min(0).default(0).describe("Page number"),
4124
+ size: z8.number().int().min(1).max(100).default(20).describe("Page size"),
4125
+ traceId: z8.string().optional().describe("Filter by trace ID"),
4126
+ groupId: z8.string().uuid().optional().describe("Filter by policy group"),
4127
+ versionId: z8.string().uuid().optional().describe("Filter by version"),
4128
+ status: z8.enum(["SUCCESS", "NO_MATCH", "ERROR", "TIMEOUT"]).optional().describe("Filter by execution status"),
4129
+ startDate: z8.string().optional().describe("Start date (yyyy-MM-dd)"),
4130
+ endDate: z8.string().optional().describe("End date (yyyy-MM-dd)")
3810
4131
  }
3811
4132
  },
3812
4133
  async ({ page, size, traceId, groupId, versionId, status, startDate, endDate }) => {
@@ -3826,7 +4147,7 @@ function registerHistoryTools(server, callApi) {
3826
4147
  title: "Get Execution Detail",
3827
4148
  description: "Get full execution detail including inputFacts, mutatedFacts, generatedVariables, executionTraces, and decisionTraces.",
3828
4149
  inputSchema: {
3829
- traceId: z7.string().describe("Trace ID from execution history")
4150
+ traceId: z8.string().describe("Trace ID from execution history")
3830
4151
  }
3831
4152
  },
3832
4153
  async ({ traceId }) => callApi("GET", `execution/history/${traceId}`)
@@ -3837,9 +4158,9 @@ function registerHistoryTools(server, callApi) {
3837
4158
  title: "Execution Statistics",
3838
4159
  description: "Get execution KPIs: total executions, success/failure counts, success rate, and average latency.",
3839
4160
  inputSchema: {
3840
- groupId: z7.string().uuid().optional().describe("Filter by policy group"),
3841
- startDate: z7.string().optional().describe("Start date (yyyy-MM-dd)"),
3842
- endDate: z7.string().optional().describe("End date (yyyy-MM-dd)")
4161
+ groupId: z8.string().uuid().optional().describe("Filter by policy group"),
4162
+ startDate: z8.string().optional().describe("Start date (yyyy-MM-dd)"),
4163
+ endDate: z8.string().optional().describe("End date (yyyy-MM-dd)")
3843
4164
  }
3844
4165
  },
3845
4166
  async ({ groupId, startDate, endDate }) => {
@@ -3852,8 +4173,49 @@ function registerHistoryTools(server, callApi) {
3852
4173
  );
3853
4174
  }
3854
4175
 
4176
+ // src/mcp/tools/provenance.ts
4177
+ import { z as z9 } from "zod";
4178
+ function registerProvenanceTools(server, callApi) {
4179
+ server.registerTool(
4180
+ "lexq_provenance_get",
4181
+ {
4182
+ title: "Get Decision Provenance",
4183
+ 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.",
4184
+ inputSchema: {
4185
+ traceId: z9.string().describe("Trace ID of the execution")
4186
+ }
4187
+ },
4188
+ async ({ traceId }) => callApi("GET", `provenance/${traceId}`)
4189
+ );
4190
+ server.registerTool(
4191
+ "lexq_pii_reveals_list",
4192
+ {
4193
+ title: "List PII Reveal Audits",
4194
+ 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.",
4195
+ inputSchema: {
4196
+ page: z9.number().int().min(0).default(0).describe("Page number"),
4197
+ size: z9.number().int().min(1).max(100).default(20).describe("Page size"),
4198
+ traceId: z9.string().optional().describe("Filter by trace ID (exact match)"),
4199
+ revealedBy: z9.string().optional().describe("Filter by operator ID (exact match)"),
4200
+ factKey: z9.string().optional().describe("Filter by fact key (partial match, case-insensitive)"),
4201
+ startDate: z9.string().optional().describe("Start date (yyyy-MM-dd)"),
4202
+ endDate: z9.string().optional().describe("End date (yyyy-MM-dd)")
4203
+ }
4204
+ },
4205
+ async ({ page, size, traceId, revealedBy, factKey, startDate, endDate }) => {
4206
+ const params = paginationParams(page, size);
4207
+ if (traceId) params.traceId = traceId;
4208
+ if (revealedBy) params.revealedBy = revealedBy;
4209
+ if (factKey) params.factKey = factKey;
4210
+ if (startDate) params.startDate = startDate;
4211
+ if (endDate) params.endDate = endDate;
4212
+ return callApi("GET", "provenance/reveal-audits", { params });
4213
+ }
4214
+ );
4215
+ }
4216
+
3855
4217
  // src/mcp/tools/integrations.ts
3856
- import { z as z8 } from "zod";
4218
+ import { z as z10 } from "zod";
3857
4219
  function registerIntegrationTools(server, callApi) {
3858
4220
  server.registerTool(
3859
4221
  "lexq_integrations_list",
@@ -3861,9 +4223,9 @@ function registerIntegrationTools(server, callApi) {
3861
4223
  title: "List Integrations",
3862
4224
  description: "List all external integrations (webhooks, CRM, notification, etc.).",
3863
4225
  inputSchema: {
3864
- page: z8.number().int().min(0).default(0).describe("Page number"),
3865
- size: z8.number().int().min(1).max(100).default(20).describe("Page size"),
3866
- type: z8.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).optional().describe("Filter by integration type")
4226
+ page: z10.number().int().min(0).default(0).describe("Page number"),
4227
+ size: z10.number().int().min(1).max(100).default(20).describe("Page size"),
4228
+ type: z10.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).optional().describe("Filter by integration type")
3867
4229
  }
3868
4230
  },
3869
4231
  async ({ page, size, type }) => {
@@ -3878,7 +4240,7 @@ function registerIntegrationTools(server, callApi) {
3878
4240
  title: "Get Integration",
3879
4241
  description: "Get integration detail by ID.",
3880
4242
  inputSchema: {
3881
- integrationId: z8.string().uuid().describe("Integration ID")
4243
+ integrationId: z10.string().uuid().describe("Integration ID")
3882
4244
  }
3883
4245
  },
3884
4246
  async ({ integrationId }) => callApi("GET", `integrations/${integrationId}`)
@@ -3889,13 +4251,13 @@ function registerIntegrationTools(server, callApi) {
3889
4251
  title: "Save Integration",
3890
4252
  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.",
3891
4253
  inputSchema: {
3892
- id: z8.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
3893
- type: z8.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
3894
- name: z8.string().describe("Integration name"),
3895
- baseUrl: z8.string().describe("Base URL of the external service"),
3896
- credential: z8.string().optional().describe("API key or token for the service"),
3897
- additionalConfig: z8.string().optional().describe("JSON string of additional config key-value pairs"),
3898
- isActive: z8.boolean().default(true).describe("Whether the integration is active")
4254
+ id: z10.string().uuid().optional().describe("Integration ID (omit to create, provide to update)"),
4255
+ type: z10.enum(["COUPON", "POINT", "NOTIFICATION", "CRM", "MESSENGER", "WEBHOOK"]).describe("Integration type"),
4256
+ name: z10.string().describe("Integration name"),
4257
+ baseUrl: z10.string().describe("Base URL of the external service"),
4258
+ credential: z10.string().optional().describe("API key or token for the service"),
4259
+ additionalConfig: z10.string().optional().describe("JSON string of additional config key-value pairs"),
4260
+ isActive: z10.boolean().default(true).describe("Whether the integration is active")
3899
4261
  }
3900
4262
  },
3901
4263
  async ({ additionalConfig, ...rest }) => {
@@ -3910,7 +4272,7 @@ function registerIntegrationTools(server, callApi) {
3910
4272
  title: "Delete Integration",
3911
4273
  description: "Delete an integration by ID.",
3912
4274
  inputSchema: {
3913
- integrationId: z8.string().uuid().describe("Integration ID")
4275
+ integrationId: z10.string().uuid().describe("Integration ID")
3914
4276
  }
3915
4277
  },
3916
4278
  async ({ integrationId }) => callApi("DELETE", `integrations/${integrationId}`)
@@ -3927,7 +4289,7 @@ function registerIntegrationTools(server, callApi) {
3927
4289
  }
3928
4290
 
3929
4291
  // src/mcp/tools/logs.ts
3930
- import { z as z9 } from "zod";
4292
+ import { z as z11 } from "zod";
3931
4293
  function registerLogTools(server, callApi) {
3932
4294
  server.registerTool(
3933
4295
  "lexq_logs_list",
@@ -3935,14 +4297,14 @@ function registerLogTools(server, callApi) {
3935
4297
  title: "List Failure Logs",
3936
4298
  description: "List system failure logs from background tasks (webhook calls, coupon issuance, etc.).",
3937
4299
  inputSchema: {
3938
- page: z9.number().int().min(0).default(0).describe("Page number"),
3939
- size: z9.number().int().min(1).max(100).default(20).describe("Page size"),
3940
- category: z9.enum(TaskCategory).optional().describe("Task category"),
3941
- taskType: z9.enum(TaskType).optional().describe("Task type"),
3942
- status: z9.enum(FailureStatus).optional().describe("Log status"),
3943
- keyword: z9.string().optional().describe("Search in refId, refSubId, errorMessage"),
3944
- startDate: z9.string().optional().describe("Start date (yyyy-MM-dd)"),
3945
- endDate: z9.string().optional().describe("End date (yyyy-MM-dd)")
4300
+ page: z11.number().int().min(0).default(0).describe("Page number"),
4301
+ size: z11.number().int().min(1).max(100).default(20).describe("Page size"),
4302
+ category: z11.enum(TaskCategory).optional().describe("Task category"),
4303
+ taskType: z11.enum(TaskType).optional().describe("Task type"),
4304
+ status: z11.enum(FailureStatus).optional().describe("Log status"),
4305
+ keyword: z11.string().optional().describe("Search in refId, refSubId, errorMessage"),
4306
+ startDate: z11.string().optional().describe("Start date (yyyy-MM-dd)"),
4307
+ endDate: z11.string().optional().describe("End date (yyyy-MM-dd)")
3946
4308
  }
3947
4309
  },
3948
4310
  async ({ page, size, category, taskType, status, keyword, startDate, endDate }) => {
@@ -3962,7 +4324,7 @@ function registerLogTools(server, callApi) {
3962
4324
  title: "Get Failure Log",
3963
4325
  description: "Get failure log detail by ID.",
3964
4326
  inputSchema: {
3965
- logId: z9.string().uuid().describe("Failure log ID")
4327
+ logId: z11.string().uuid().describe("Failure log ID")
3966
4328
  }
3967
4329
  },
3968
4330
  async ({ logId }) => callApi("GET", `failure-logs/${logId}`)
@@ -3973,8 +4335,8 @@ function registerLogTools(server, callApi) {
3973
4335
  title: "Process Failure Log",
3974
4336
  description: "Process a single failure log: RETRY (re-execute with original payload), RESOLVE (mark as manually fixed), or IGNORE (skip intentionally).",
3975
4337
  inputSchema: {
3976
- logId: z9.string().uuid().describe("Failure log ID"),
3977
- action: z9.enum(FailureAction).describe("Action to take")
4338
+ logId: z11.string().uuid().describe("Failure log ID"),
4339
+ action: z11.enum(FailureAction).describe("Action to take")
3978
4340
  }
3979
4341
  },
3980
4342
  async ({ logId, action }) => callApi("POST", `failure-logs/${logId}/actions`, {
@@ -3987,8 +4349,8 @@ function registerLogTools(server, callApi) {
3987
4349
  title: "Bulk Process Failure Logs",
3988
4350
  description: "Process multiple failure logs at once. Provide an array of log IDs and the action.",
3989
4351
  inputSchema: {
3990
- logIds: z9.array(z9.string().uuid()).describe("Array of failure log IDs"),
3991
- action: z9.enum(FailureAction).describe("Action to apply to all logs")
4352
+ logIds: z11.array(z11.string().uuid()).describe("Array of failure log IDs"),
4353
+ action: z11.enum(FailureAction).describe("Action to apply to all logs")
3992
4354
  }
3993
4355
  },
3994
4356
  async ({ logIds, action }) => callApi("POST", "failure-logs/bulk-actions", {
@@ -3998,7 +4360,7 @@ function registerLogTools(server, callApi) {
3998
4360
  }
3999
4361
 
4000
4362
  // src/mcp/tools/webhook-subscriptions.ts
4001
- import { z as z10 } from "zod";
4363
+ import { z as z12 } from "zod";
4002
4364
  function registerWebhookSubscriptionTools(server, callApi) {
4003
4365
  server.registerTool(
4004
4366
  "lexq_webhook_subscriptions_list",
@@ -4006,8 +4368,8 @@ function registerWebhookSubscriptionTools(server, callApi) {
4006
4368
  title: "List Webhook Subscriptions",
4007
4369
  description: "List platform event webhook subscriptions. These receive deployment lifecycle notifications (publish, deploy, rollback, undeploy).",
4008
4370
  inputSchema: {
4009
- page: z10.number().int().min(0).default(0).describe("Page number"),
4010
- size: z10.number().int().min(1).max(100).default(20).describe("Page size")
4371
+ page: z12.number().int().min(0).default(0).describe("Page number"),
4372
+ size: z12.number().int().min(1).max(100).default(20).describe("Page size")
4011
4373
  }
4012
4374
  },
4013
4375
  async ({ page, size }) => {
@@ -4021,7 +4383,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
4021
4383
  title: "Get Webhook Subscription",
4022
4384
  description: "Get webhook subscription detail by ID.",
4023
4385
  inputSchema: {
4024
- id: z10.string().uuid().describe("Webhook subscription ID")
4386
+ id: z12.string().uuid().describe("Webhook subscription ID")
4025
4387
  }
4026
4388
  },
4027
4389
  async ({ id }) => callApi("GET", `webhook-subscriptions/${id}`)
@@ -4032,13 +4394,13 @@ function registerWebhookSubscriptionTools(server, callApi) {
4032
4394
  title: "Save Webhook Subscription",
4033
4395
  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": "..."}).',
4034
4396
  inputSchema: {
4035
- id: z10.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
4036
- name: z10.string().min(1).describe("Subscription name (unique per tenant)"),
4037
- webhookUrl: z10.string().url().describe("Webhook endpoint URL"),
4038
- subscribedEvents: z10.array(z10.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
4039
- payloadFormat: z10.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
4040
- secret: z10.string().optional().describe("HMAC-SHA256 signing secret"),
4041
- isActive: z10.boolean().optional().default(true).describe("Whether the subscription is active")
4397
+ id: z12.string().uuid().optional().describe("Subscription ID (omit to create, provide to update)"),
4398
+ name: z12.string().min(1).describe("Subscription name (unique per tenant)"),
4399
+ webhookUrl: z12.string().url().describe("Webhook endpoint URL"),
4400
+ subscribedEvents: z12.array(z12.enum(PlatformEventType)).min(1).describe("Events to subscribe to"),
4401
+ payloadFormat: z12.enum(WebhookPayloadFormat).optional().default("GENERIC").describe("Payload format"),
4402
+ secret: z12.string().optional().describe("HMAC-SHA256 signing secret"),
4403
+ isActive: z12.boolean().optional().default(true).describe("Whether the subscription is active")
4042
4404
  }
4043
4405
  },
4044
4406
  async ({ ...body }) => callApi("POST", "webhook-subscriptions", { body })
@@ -4049,7 +4411,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
4049
4411
  title: "Delete Webhook Subscription",
4050
4412
  description: "Delete a webhook subscription by ID.",
4051
4413
  inputSchema: {
4052
- id: z10.string().uuid().describe("Webhook subscription ID")
4414
+ id: z12.string().uuid().describe("Webhook subscription ID")
4053
4415
  }
4054
4416
  },
4055
4417
  async ({ id }) => callApi("DELETE", `webhook-subscriptions/${id}`)
@@ -4060,7 +4422,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
4060
4422
  title: "Test Webhook Subscription",
4061
4423
  description: "Send a test event to verify webhook connectivity. Returns the HTTP status code and success/failure message.",
4062
4424
  inputSchema: {
4063
- id: z10.string().uuid().describe("Webhook subscription ID")
4425
+ id: z12.string().uuid().describe("Webhook subscription ID")
4064
4426
  }
4065
4427
  },
4066
4428
  async ({ id }) => callApi("POST", `webhook-subscriptions/${id}/test`)
@@ -4068,7 +4430,7 @@ function registerWebhookSubscriptionTools(server, callApi) {
4068
4430
  }
4069
4431
 
4070
4432
  // src/mcp/tools/domain-templates.ts
4071
- import { z as z11 } from "zod";
4433
+ import { z as z13 } from "zod";
4072
4434
  function registerDomainTemplateTools(server, callApi) {
4073
4435
  server.registerTool(
4074
4436
  "lexq_domain_templates_list",
@@ -4085,7 +4447,7 @@ function registerDomainTemplateTools(server, callApi) {
4085
4447
  title: "Preview Domain Template",
4086
4448
  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.",
4087
4449
  inputSchema: {
4088
- template: z11.string().describe(
4450
+ template: z13.string().describe(
4089
4451
  "Domain template key (e.g. ECOMMERCE). Use lexq_domain_templates_list to see available keys \u2014 currently only ECOMMERCE is ACTIVE."
4090
4452
  )
4091
4453
  }
@@ -4098,8 +4460,8 @@ function registerDomainTemplateTools(server, callApi) {
4098
4460
  title: "Apply Domain Template",
4099
4461
  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.",
4100
4462
  inputSchema: {
4101
- template: z11.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
4102
- customName: z11.string().optional().describe(
4463
+ template: z13.string().describe("Domain template key to apply (e.g. ECOMMERCE)."),
4464
+ customName: z13.string().optional().describe(
4103
4465
  "Optional custom name for the policy group that gets created. If omitted, the template's default name is used."
4104
4466
  )
4105
4467
  }
@@ -4121,7 +4483,9 @@ function registerAllTools(server, callApi) {
4121
4483
  registerFactTools(server, callApi);
4122
4484
  registerDeployTools(server, callApi);
4123
4485
  registerAnalyticsTools(server, callApi);
4486
+ registerReplayTools(server, callApi);
4124
4487
  registerHistoryTools(server, callApi);
4488
+ registerProvenanceTools(server, callApi);
4125
4489
  registerIntegrationTools(server, callApi);
4126
4490
  registerLogTools(server, callApi);
4127
4491
  registerDomainTemplateTools(server, callApi);
@@ -4153,12 +4517,12 @@ async function startMcpServer() {
4153
4517
  function registerServeCommand(program) {
4154
4518
  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(
4155
4519
  "after",
4156
- dedent15`
4520
+ dedent17`
4157
4521
 
4158
4522
  Example:
4159
4523
  $ lexq serve --mcp
4160
4524
 
4161
- Starts a stdio MCP server that exposes 60 tools for policy management.
4525
+ Starts a stdio MCP server that exposes the full LexQ toolset for policy management.
4162
4526
  Used by Claude Desktop, Claude.ai, Cursor, and other MCP-compatible clients.
4163
4527
 
4164
4528
  Claude Desktop config (~/.claude/claude_desktop_config.json):
@@ -4184,11 +4548,11 @@ function registerServeCommand(program) {
4184
4548
 
4185
4549
  // src/commands/domain-templates.ts
4186
4550
  import "commander";
4187
- import dedent16 from "dedent";
4551
+ import dedent18 from "dedent";
4188
4552
  function registerDomainTemplateCommands(program) {
4189
4553
  const templates = program.command("domain-templates").description("Browse and apply domain templates").addHelpText(
4190
4554
  "after",
4191
- dedent16`
4555
+ dedent18`
4192
4556
 
4193
4557
  A domain template is an industry-specific starter pack of fact
4194
4558
  definitions and sample rules. Applying one provisions a ready-to-use
@@ -4236,7 +4600,7 @@ function registerDomainTemplateCommands(program) {
4236
4600
  });
4237
4601
  templates.command("preview").description("Preview what a domain template provisions").requiredOption("--template <key>", "Domain template key (e.g. ECOMMERCE)").addHelpText(
4238
4602
  "after",
4239
- dedent16`
4603
+ dedent18`
4240
4604
 
4241
4605
  Read-only dry run — shows the fact definitions and sample rules the
4242
4606
  template will create. Nothing is provisioned.
@@ -4265,7 +4629,7 @@ function registerDomainTemplateCommands(program) {
4265
4629
  });
4266
4630
  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(
4267
4631
  "after",
4268
- dedent16`
4632
+ dedent18`
4269
4633
 
4270
4634
  Creates the template's fact definitions and a new DRAFT policy group
4271
4635
  populated with its sample rules. Existing facts are skipped — apply is
@@ -4327,7 +4691,10 @@ function createCli() {
4327
4691
  const program = new Command();
4328
4692
  program.name("lexq").description(
4329
4693
  "Command-line interface for the LexQ Decision Operations Platform. Manage policies, run Impact Simulation, and deploy from your terminal."
4330
- ).version(getVersion2(), "-V, --version").option("--format <format>", "Output format: json or table", "json").option("--api-key <key>", "Override stored API key").option("--base-url <url>", "Override API base URL").option("--dry-run", "Preview the HTTP request without executing").option("--verbose", "Show request/response details").option("--no-color", "Disable colored output");
4694
+ ).version(getVersion2(), "-V, --version").option("--format <format>", "Output format: json or table", "json").option("--api-key <key>", "Override stored API key").option(
4695
+ "--base-url <url>",
4696
+ "Override API base URL \u2014 must include the API prefix, e.g. http://localhost:8080/api/v1/partners"
4697
+ ).option("--dry-run", "Preview the HTTP request without executing").option("--verbose", "Show request/response details").option("--no-color", "Disable colored output");
4331
4698
  registerAuthCommands(program);
4332
4699
  registerStatusCommand(program);
4333
4700
  registerGroupCommands(program);
@@ -4338,6 +4705,8 @@ function createCli() {
4338
4705
  registerDeployCommands(program);
4339
4706
  registerAnalyticsCommands(program);
4340
4707
  registerHistoryCommands(program);
4708
+ registerReplayCommands(program);
4709
+ registerProvenanceCommands(program);
4341
4710
  registerIntegrationCommands(program);
4342
4711
  registerLogCommands(program);
4343
4712
  registerWebhookSubscriptionCommands(program);