@moda-ai/cli 1.55.41 → 1.55.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{agent-ck6y31n2.js → agent-x1gwrrvd.js} +6 -5
  2. package/dist/{cli-xddtv3yn.js → cli-5fhqfx04.js} +1 -39
  3. package/dist/{cli-gt3yq1y5.js → cli-5y9tj6cb.js} +183 -1
  4. package/dist/{cli-f1jfrqgf.js → cli-9a0a3v13.js} +234 -17
  5. package/dist/cli-e0kbye4s.js +39 -0
  6. package/dist/{cli-ypqwcf9n.js → cli-fpd1pfvt.js} +2 -2
  7. package/dist/{cli-a8nxbtpk.js → cli-h62j4jn7.js} +2 -2
  8. package/dist/{cli-4tg0hkbz.js → cli-k5drmy8r.js} +7 -7
  9. package/dist/{cli-azage7s0.js → cli-s1w5phms.js} +3 -3
  10. package/dist/{cli-7bn406mt.js → cli-stfpr2ct.js} +2 -2
  11. package/dist/cli.js +224 -52
  12. package/dist/{doctor-p8sp6cbk.js → doctor-wrgajgev.js} +5 -4
  13. package/dist/{fix-0r974yge.js → fix-7aajsdbf.js} +1 -1
  14. package/dist/{harness-72ejenza.js → harness-1rn1hqzz.js} +4 -3
  15. package/dist/{harness-github-actions-2qc4gg2w.js → harness-github-actions-m95qvgbs.js} +4 -3
  16. package/dist/{index-w7v472w4.js → index-svfzq5rb.js} +7 -6
  17. package/dist/{production-intelligence-e5vsc6cw.js → production-intelligence-7jvfdz6b.js} +22 -5
  18. package/dist/{prompt-attribution-3987hhqz.js → prompt-attribution-hc6mcvee.js} +4 -3
  19. package/dist/prompt-source-mcp.js +4 -3
  20. package/dist/{prompts-ab-gmd10n5d.js → prompts-ab-ws66335h.js} +5 -4
  21. package/dist/{prompts-propose-q626zmm4.js → prompts-propose-a9m0p9nr.js} +5 -4
  22. package/dist/{prompts-ysm9ra72.js → prompts-xe5bgbx8.js} +4 -3
  23. package/dist/{provision-cya5rpxa.js → provision-a9kkfvp7.js} +2 -1
  24. package/dist/{registry-7xz09ry0.js → registry-j2dsfh6v.js} +1 -1
  25. package/dist/{skills-x6yszxxn.js → skills-vveagdwe.js} +4 -3
  26. package/dist/{sync-trjzvam9.js → sync-yy3fgnxe.js} +6 -5
  27. package/package.json +1 -1
  28. package/skills/integration/index.json +2 -2
  29. package/skills/moda-cli/SKILL.md +4 -1
  30. package/skills/moda-cli/references/commands.md +8 -3
  31. package/skills/moda-cli/references/dashboard-parity.md +7 -2
  32. package/skills/moda-cli/references/detections.md +20 -0
  33. package/skills/moda-cli/references/production-intelligence.md +48 -0
package/dist/cli.js CHANGED
@@ -6,34 +6,41 @@ import {
6
6
  asNumber,
7
7
  asRecord,
8
8
  asString,
9
+ assertCompareScope,
9
10
  collectBatchTargets,
10
11
  compactProblemDossier,
11
12
  contextTarget,
12
13
  embeddedWindowSize,
13
14
  frustrationsFromEmotions,
15
+ loadFamilyCompare,
14
16
  mapWithConcurrency,
15
17
  parseBatchTarget,
16
18
  parseTargetLines,
19
+ problemTrendSummary,
17
20
  readIdsFrom,
18
21
  shapeToolCatalogDetail,
19
22
  toolCatalogGetCommand,
20
23
  toolFailureExamplesCommand,
21
24
  trimToolFailureConversations,
22
25
  withFrustrationAnchors,
26
+ withSummaryCompare,
23
27
  withToolFailureAnchors,
24
28
  withoutDetectionWindows
25
- } from "./cli-gt3yq1y5.js";
29
+ } from "./cli-5y9tj6cb.js";
30
+ import {
31
+ createCommandContext
32
+ } from "./cli-5fhqfx04.js";
26
33
  import {
27
- createCommandContext,
28
34
  envOptOutSet,
29
35
  envSwitchedOff,
30
36
  optOutFlagSet,
31
37
  parseBooleanFlagValue,
32
38
  withoutFlag
33
- } from "./cli-xddtv3yn.js";
39
+ } from "./cli-e0kbye4s.js";
34
40
  import {
35
41
  AuditSchema,
36
42
  BOOK_PROBLEM_SORTS,
43
+ CLUSTER_COHORT_TIME_RANGES,
37
44
  COMMAND_FLAG_SPECS,
38
45
  DETECTION_VERDICTS,
39
46
  DREAM_PROBLEM_SORTS,
@@ -68,6 +75,7 @@ import {
68
75
  kebabFlag,
69
76
  manifestFlagFields,
70
77
  renderFlagHelp,
78
+ runCohortCommand,
71
79
  runProblemReopenCommand,
72
80
  runSignalsManagementCommand,
73
81
  runUserCommand,
@@ -79,7 +87,7 @@ import {
79
87
  unknownSignalsSubcommandError,
80
88
  usageLine,
81
89
  writeDryRun
82
- } from "./cli-f1jfrqgf.js";
90
+ } from "./cli-9a0a3v13.js";
83
91
  import {
84
92
  ApiError,
85
93
  callControlAPI,
@@ -1355,7 +1363,7 @@ var CANONICAL_LEAVES = [
1355
1363
  args: "<problem_id>",
1356
1364
  handler: "problem",
1357
1365
  summary: "One Problem: dossier, its --evidence/--reports/--traces/--feedback pages, or --view=dream",
1358
- examples: ["moda problems get <problem_id>", "moda problems get <problem_id> --view=dream", "moda problems get <problem_id> --evidence --limit=10", "moda problems get <problem_id> --traces --family=tool_failure"]
1366
+ examples: ["moda problems get <problem_id>", "moda problems get <problem_id> --view=dream", "moda problems get <problem_id> --evidence --limit=10", "moda problems get <problem_id> --traces --family=tool_failure", "moda problems get <problem_id> --trend --days-back=14"]
1359
1367
  },
1360
1368
  {
1361
1369
  noun: "problems",
@@ -1423,7 +1431,8 @@ var CANONICAL_LEAVES = [
1423
1431
  "moda detections list --family=frustration --limit=10",
1424
1432
  "moda detections list --family=emotion:confusion --days-back=14",
1425
1433
  "moda detections list --family=hallucination --kind=contradicted",
1426
- "moda detections list --family=custom_signal --signal-id=<signal_id>"
1434
+ "moda detections list --family=custom_signal --signal-id=<signal_id>",
1435
+ "moda detections list --family=frustration --compare --days-back=7"
1427
1436
  ]
1428
1437
  },
1429
1438
  {
@@ -1453,6 +1462,69 @@ var CANONICAL_LEAVES = [
1453
1462
  summary: "One tool: calls, recorded schema and payload fields, usage over time, top failure categories",
1454
1463
  examples: ["moda tools get <tool_name>", "moda tools get <tool_name> --days-back=30 --full"]
1455
1464
  },
1465
+ {
1466
+ noun: "cohorts",
1467
+ verb: "list",
1468
+ path: "cohorts list",
1469
+ args: "",
1470
+ handler: "cohort-list",
1471
+ summary: "Built-in and saved user cohorts with their refs and definitions (the dashboard Users page)",
1472
+ examples: ["moda cohorts list", "moda cohorts list --include-archived"]
1473
+ },
1474
+ {
1475
+ noun: "cohorts",
1476
+ verb: "get",
1477
+ path: "cohorts get",
1478
+ args: "<ref>",
1479
+ handler: "cohort-get",
1480
+ summary: "One cohort: name, definition and revision (ref: builtin:<key> or a saved cohort id)",
1481
+ examples: ["moda cohorts get builtin:power_users", "moda cohorts get <cohort_id>"]
1482
+ },
1483
+ {
1484
+ noun: "cohorts",
1485
+ verb: "fields",
1486
+ path: "cohorts fields",
1487
+ args: "",
1488
+ handler: "cohort-fields",
1489
+ summary: "The fields and operators a cohort definition can use",
1490
+ examples: ["moda cohorts fields"]
1491
+ },
1492
+ {
1493
+ noun: "cohorts",
1494
+ verb: "preview",
1495
+ path: "cohorts preview",
1496
+ args: "",
1497
+ handler: "cohort-preview",
1498
+ summary: "Member count and a sample of users for an unsaved definition",
1499
+ examples: ["moda cohorts preview --file=cohort.json", `moda cohorts preview --definition='{"version":1,"window":"30d","where":{"field":"tool_failure_count","op":"gte","value":3}}'`]
1500
+ },
1501
+ {
1502
+ noun: "cohorts",
1503
+ verb: "compare",
1504
+ path: "cohorts compare",
1505
+ args: "<cohort_a> <cohort_b>",
1506
+ handler: "cohort-compare",
1507
+ summary: "Two cohorts side by side: size, health, top topics and failure mix",
1508
+ examples: ["moda cohorts compare builtin:new_this_week builtin:power_users --days=30"]
1509
+ },
1510
+ {
1511
+ noun: "cohorts",
1512
+ verb: "create",
1513
+ path: "cohorts create",
1514
+ args: "",
1515
+ handler: "cohort-create",
1516
+ summary: "Save a cohort from a definition (write; --dry-run prints the request)",
1517
+ examples: ['moda cohorts create --name="Enterprise" --file=cohort.json --dry-run']
1518
+ },
1519
+ {
1520
+ noun: "cohorts",
1521
+ verb: "update",
1522
+ path: "cohorts update",
1523
+ args: "<cohort_id>",
1524
+ handler: "cohort-update",
1525
+ summary: "Edit a saved cohort: name, definition, description, pin, color (write; --dry-run)",
1526
+ examples: ['moda cohorts update <cohort_id> --file=cohort.json --note="exclude trials" --dry-run']
1527
+ },
1456
1528
  {
1457
1529
  noun: "review",
1458
1530
  verb: "",
@@ -1486,12 +1558,13 @@ var CANONICAL_LEAVES = [
1486
1558
  examples: ["moda skills push --dry-run", "moda skills push"]
1487
1559
  }
1488
1560
  ];
1489
- var CANONICAL_NOUNS = ["traces", "problems", "detections", "tools", "review", "reviews", "skills"];
1561
+ var CANONICAL_NOUNS = ["traces", "problems", "detections", "tools", "cohorts", "review", "reviews", "skills"];
1490
1562
  var NOUN_SUMMARIES = {
1491
1563
  traces: "Traces (agent runs): list, search, read, findings, world state, raw spans",
1492
1564
  problems: "Problems ranked by root cause, their evidence, impact and lifecycle",
1493
1565
  detections: "Detections of every family, and the tool failure leaderboard",
1494
1566
  tools: "The Tool catalog: every tool with calls and success rate, and one tool's schema, usage and failure categories",
1567
+ cohorts: "User cohorts: list, read, preview and compare them, and save or edit one; scope lists with --cohort",
1495
1568
  review: "Mark a detection a false positive, correct, or clear the mark",
1496
1569
  reviews: "List false-positive marks and detection reviews",
1497
1570
  skills: "Generate, pull, push, and install agent skills"
@@ -3175,31 +3248,31 @@ function leafManifestFields(leaf, commands) {
3175
3248
  import { fileURLToPath } from "url";
3176
3249
  import { realpathSync } from "fs";
3177
3250
  async function runPromptsCommand(...args) {
3178
- return (await import("./prompts-ysm9ra72.js")).runPromptsCommand(...args);
3251
+ return (await import("./prompts-xe5bgbx8.js")).runPromptsCommand(...args);
3179
3252
  }
3180
3253
  async function runSkillsCommand(...args) {
3181
- return (await import("./skills-x6yszxxn.js")).runSkillsCommand(...args);
3254
+ return (await import("./skills-vveagdwe.js")).runSkillsCommand(...args);
3182
3255
  }
3183
3256
  async function runFixCommand(...args) {
3184
- return (await import("./fix-0r974yge.js")).runFixCommand(...args);
3257
+ return (await import("./fix-7aajsdbf.js")).runFixCommand(...args);
3185
3258
  }
3186
3259
  async function runFixesCommand(...args) {
3187
- return (await import("./fix-0r974yge.js")).runFixesCommand(...args);
3260
+ return (await import("./fix-7aajsdbf.js")).runFixesCommand(...args);
3188
3261
  }
3189
3262
  async function runHarnessCommand(...args) {
3190
- return (await import("./harness-72ejenza.js")).runHarnessCommand(...args);
3263
+ return (await import("./harness-1rn1hqzz.js")).runHarnessCommand(...args);
3191
3264
  }
3192
3265
  async function runSyncCommand(...args) {
3193
- return (await import("./sync-trjzvam9.js")).runSyncCommand(...args);
3266
+ return (await import("./sync-yy3fgnxe.js")).runSyncCommand(...args);
3194
3267
  }
3195
3268
  async function runRegistryCommand(...args) {
3196
- return (await import("./registry-7xz09ry0.js")).runRegistryCommand(...args);
3269
+ return (await import("./registry-j2dsfh6v.js")).runRegistryCommand(...args);
3197
3270
  }
3198
3271
  async function runDoctorCommand(...args) {
3199
- return (await import("./doctor-p8sp6cbk.js")).runDoctorCommand(...args);
3272
+ return (await import("./doctor-wrgajgev.js")).runDoctorCommand(...args);
3200
3273
  }
3201
3274
  async function runStatusCommand(...args) {
3202
- return (await import("./doctor-p8sp6cbk.js")).runStatusCommand(...args);
3275
+ return (await import("./doctor-wrgajgev.js")).runStatusCommand(...args);
3203
3276
  }
3204
3277
  async function runAuthCommand(...args) {
3205
3278
  return (await import("./profile-commands-se9947c1.js")).runAuthCommand(...args);
@@ -3211,10 +3284,10 @@ async function runProfilesCommand(...args) {
3211
3284
  return (await import("./profile-commands-se9947c1.js")).runProfilesCommand(...args);
3212
3285
  }
3213
3286
  async function runAgentCommand(...args) {
3214
- return (await import("./agent-ck6y31n2.js")).runAgentCommand(...args);
3287
+ return (await import("./agent-x1gwrrvd.js")).runAgentCommand(...args);
3215
3288
  }
3216
3289
  async function runProductionCommand(...args) {
3217
- return (await import("./production-intelligence-e5vsc6cw.js")).runProductionCommand(...args);
3290
+ return (await import("./production-intelligence-7jvfdz6b.js")).runProductionCommand(...args);
3218
3291
  }
3219
3292
  function parseArgs(argv) {
3220
3293
  const args = argv.slice(2);
@@ -3515,6 +3588,7 @@ Production data:
3515
3588
  clusters Browse or search topic clusters
3516
3589
  cluster-traces <node_id> List traces in a cluster
3517
3590
  users / user <user_id> End users with health and activity (the dashboard Users page)
3591
+ cohorts list|get|compare User cohorts; --cohort=<ref> scopes problems, traces, detections, clusters
3518
3592
  tail Live-tail new traces/detections as NDJSON (one line per item)
3519
3593
 
3520
3594
  Traces, Problems, detections, tools and review (moda <noun> --help lists the verbs):
@@ -4858,10 +4932,7 @@ async function runProblemsList(flags, context, command) {
4858
4932
  ...listVersionWarning(params, listVersion),
4859
4933
  ...truncated ? [`--limit=${limit} also capped ${Object.entries(truncated).map(([block, count]) => `${block} (${count} more)`).join(", ")}; raise --limit (max ${PROBLEMS_MAX_LIMIT}) or open dashboard_url for the rest.`] : []
4860
4934
  ];
4861
- context.output.writeData(withLink, {
4862
- ...warnings.length > 0 ? { warnings } : {},
4863
- ...nextCommands.length > 0 ? { nextCommands } : {}
4864
- });
4935
+ context.output.writeData(withLink, withCohortScope(params.cohort, payload, { warnings, nextCommands }));
4865
4936
  }
4866
4937
  function writeDreamProblemsList(context, params, dream, page) {
4867
4938
  const problems = Array.isArray(dream.problems) ? dream.problems : [];
@@ -4903,7 +4974,7 @@ function writeDreamProblemsList(context, params, dream, page) {
4903
4974
  ...!page.daysGiven && windowHours !== undefined ? [`Counts cover the dream run's window (${windowHours} hours), the dashboard's default range; pass --days-back to choose another.`] : [],
4904
4975
  ...listVersionWarning(params, listVersion)
4905
4976
  ];
4906
- context.output.writeData(data, { warnings, ...nextCommands.length > 0 ? { nextCommands } : {} });
4977
+ context.output.writeData(data, { warnings, ...withCohortScope(params.cohort, dream, { warnings, nextCommands }) });
4907
4978
  }
4908
4979
  var DREAM_INCIDENTS_DEFAULT_LIMIT = 3;
4909
4980
  async function runDreamProblem(params, context, command) {
@@ -5008,6 +5079,27 @@ function setTraceFilters(query2, params, keys) {
5008
5079
  query2.set(key, String(value));
5009
5080
  }
5010
5081
  }
5082
+ function cohortScope(cohort, data) {
5083
+ if (!cohort || cohort === "all")
5084
+ return { warnings: [] };
5085
+ if (asString(asRecord(data)?.cohort) !== cohort) {
5086
+ return {
5087
+ warnings: [`--cohort=${cohort} was sent, but the response does not confirm the cohort scope, so these results may cover every user.`]
5088
+ };
5089
+ }
5090
+ return { line: `Scoped to cohort ${cohort}: only its members' traces count (moda cohorts get ${shellArg(cohort)} shows who is in it).`, warnings: [] };
5091
+ }
5092
+ function withCohortScope(cohort, data, options = {}) {
5093
+ const scope = cohortScope(cohort, data);
5094
+ const summary = [options.summary, scope.line].filter(Boolean).join(" ");
5095
+ const warnings = [...options.warnings ?? [], ...scope.warnings];
5096
+ const merged = {
5097
+ ...summary ? { summary } : {},
5098
+ ...warnings.length > 0 ? { warnings } : {},
5099
+ ...options.nextCommands && options.nextCommands.length > 0 ? { nextCommands: options.nextCommands } : {}
5100
+ };
5101
+ return Object.keys(merged).length > 0 ? merged : undefined;
5102
+ }
5011
5103
  function countingSummary(data) {
5012
5104
  const record = asRecord(data);
5013
5105
  const counting = asString(record?.counting);
@@ -5702,8 +5794,11 @@ async function runCommand(command, positional, flags, positionals = positional ?
5702
5794
  case "clusters": {
5703
5795
  const args = flagsToArgs(flags);
5704
5796
  const params = parseSpecFlags("clusters", args, command);
5797
+ if (params.cohort && params.cohort !== "all" && params.time_range && !CLUSTER_COHORT_TIME_RANGES.includes(params.time_range)) {
5798
+ throw new CliInputError(`--cohort reads the Topics tree windows, so --time-range must be ${CLUSTER_COHORT_TIME_RANGES.join(", ")} (got ${params.time_range}). Nothing was sent.`);
5799
+ }
5705
5800
  if (params.search !== undefined || params.node_id !== undefined) {
5706
- rejectInapplicableFlags(command, params, ["parent_id", "time_range"], "to a cluster search (--search/--node-id); they page the cluster hierarchy");
5801
+ rejectInapplicableFlags(command, params, params.cohort ? ["parent_id"] : ["parent_id", "time_range"], "to a cluster search (--search/--node-id); they page the cluster hierarchy");
5707
5802
  const query3 = new URLSearchParams;
5708
5803
  if (params.search !== undefined)
5709
5804
  query3.set("q", params.search);
@@ -5713,11 +5808,15 @@ async function runCommand(command, positional, flags, positionals = positional ?
5713
5808
  query3.set("mode", params.mode);
5714
5809
  if (params.limit)
5715
5810
  query3.set("limit", params.limit.toString());
5811
+ if (params.cohort)
5812
+ query3.set("cohort", params.cohort);
5813
+ if (params.cohort && params.time_range)
5814
+ query3.set("time_range", params.time_range);
5716
5815
  const data2 = await callDataAPI(`/task-clusters/search?${query3.toString()}`);
5717
5816
  const record = asRecord(data2) ?? {};
5718
5817
  const matches = Array.isArray(record.matches) ? record.matches : [];
5719
- const warnings = params.node_id !== undefined && matches.length === 0 ? [`No cluster matched node id "${params.node_id}" for this search. ${NOT_FOUND_HINTS.cluster}`] : [];
5720
- context.output.writeData(data2, warnings.length > 0 ? { warnings } : undefined);
5818
+ const warnings = params.node_id !== undefined && matches.length === 0 ? [`No cluster matched node id "${params.node_id}" for this search${params.cohort ? " within the cohort" : ""}. ${NOT_FOUND_HINTS.cluster}`] : [];
5819
+ context.output.writeData(data2, withCohortScope(params.cohort, data2, { warnings }));
5721
5820
  break;
5722
5821
  }
5723
5822
  rejectInapplicableFlags(command, params, ["mode", "limit"], "without --search or --node-id; they shape a cluster search");
@@ -5726,9 +5825,11 @@ async function runCommand(command, positional, flags, positionals = positional ?
5726
5825
  query2.set("parent_id", params.parent_id);
5727
5826
  if (params.time_range)
5728
5827
  query2.set("time_range", params.time_range);
5828
+ if (params.cohort)
5829
+ query2.set("cohort", params.cohort);
5729
5830
  const queryString = query2.toString() ? `?${query2.toString()}` : "";
5730
5831
  const data = await callDataAPI(`/clusters${queryString}`);
5731
- context.output.writeData(data);
5832
+ context.output.writeData(data, withCohortScope(params.cohort, data));
5732
5833
  break;
5733
5834
  }
5734
5835
  case "cluster-traces":
@@ -5775,6 +5876,8 @@ async function runCommand(command, positional, flags, positionals = positional ?
5775
5876
  query2.set("outcome", params.outcome);
5776
5877
  if (params.sort)
5777
5878
  query2.set("sort_by", params.sort);
5879
+ if (params.cohort)
5880
+ query2.set("cohort", params.cohort);
5778
5881
  if (params.limit)
5779
5882
  query2.set("limit", params.limit.toString());
5780
5883
  if (params.offset !== undefined)
@@ -5800,7 +5903,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
5800
5903
  }
5801
5904
  }
5802
5905
  searchWarnings.push(...traceStateWarnings(data));
5803
- const writeOptions = searchWarnings.length > 0 ? { warnings: searchWarnings } : undefined;
5906
+ const writeOptions = withCohortScope(params.cohort, raw, { warnings: searchWarnings });
5804
5907
  if (params.include_world_state === true) {
5805
5908
  const enriched = asRecord(data) ?? {};
5806
5909
  const list = Array.isArray(enriched.conversations) ? enriched.conversations : [];
@@ -5962,6 +6065,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
5962
6065
  case "frustrations": {
5963
6066
  const args = flagsToArgs(flags);
5964
6067
  const params = parseSpecFlags("frustrations", args, command);
6068
+ const compare = params.compare === true ? await loadFamilyCompare("frustrations", params) : undefined;
5965
6069
  const query2 = new URLSearchParams;
5966
6070
  if (params.days_back)
5967
6071
  query2.set("days_back", params.days_back.toString());
@@ -5972,10 +6076,9 @@ async function runCommand(command, positional, flags, positionals = positional ?
5972
6076
  if (readsPerTrace("frustrations", params, ["user_id", "conversation_id", "conversation_ids", "ids_from", "include_window"])) {
5973
6077
  query2.set("family", "frustration");
5974
6078
  setTraceFilters(query2, params, TRACE_GRAIN_FLAGS.frustrations);
5975
- const data2 = await callDataAPI(`/emotions?${query2.toString()}`);
6079
+ const data2 = withSummaryCompare(await callDataAPI(`/emotions?${query2.toString()}`), compare);
5976
6080
  const warnings = processingDisabledWarning(data2, "frustration");
5977
- const summary = countingSummary(data2);
5978
- context.output.writeData(data2, { ...summary ? { summary } : {}, ...warnings.length > 0 ? { warnings } : {} });
6081
+ context.output.writeData(data2, withCohortScope(params.cohort, data2, { summary: countingSummary(data2), warnings }));
5979
6082
  break;
5980
6083
  }
5981
6084
  if (params.user_id)
@@ -5991,22 +6094,33 @@ async function runCommand(command, positional, flags, positionals = positional ?
5991
6094
  if (agentMode && !includeWindow)
5992
6095
  query2.set("include_context", "0");
5993
6096
  const data = frustrationsFromEmotions(await callDataAPI(`/emotions?${query2.toString()}`));
5994
- const anchored = withFrustrationAnchors(data);
6097
+ const anchored = withSummaryCompare(withFrustrationAnchors(data), compare);
5995
6098
  await printWithOptionalWindows(agentMode && !includeWindow ? withoutDetectionWindows(anchored, "frustrations", "conversation") : anchored, "frustrations", includeWindow, params.window ?? 1, context.output, processingDisabledWarning(data, "frustration"), agentMode && includeWindow ? (payload) => withoutRepeatedFrustrationWindow(payload) : undefined, [], countingSummary(data));
5996
6099
  break;
5997
6100
  }
5998
6101
  case "tool-failures": {
5999
6102
  const args = flagsToArgs(flags);
6000
6103
  const params = parseSpecFlags("tool-failures", args, command);
6001
- const queryString = params.days_back ? `?days_back=${params.days_back}` : "";
6002
- const data = await callDataAPI(`/tool-failures${queryString}`);
6104
+ const tfQuery = new URLSearchParams;
6105
+ if (params.days_back)
6106
+ tfQuery.set("days_back", String(params.days_back));
6107
+ if (params.cohort)
6108
+ tfQuery.set("cohort", params.cohort);
6109
+ const queryString = tfQuery.toString() ? `?${tfQuery.toString()}` : "";
6110
+ if (params.compare === true)
6111
+ assertCompareScope("tool-failures", params);
6112
+ const [listed, compare] = await Promise.all([
6113
+ callDataAPI(`/tool-failures${queryString}`),
6114
+ params.compare === true ? loadFamilyCompare("tool-failures", params) : Promise.resolve(undefined)
6115
+ ]);
6116
+ const data = withSummaryCompare(listed, compare);
6003
6117
  const daysBack = params.days_back ?? 7;
6004
6118
  const failing = (Array.isArray(asRecord(data)?.tools) ? asRecord(data).tools : []).map((row) => asString(asRecord(row)?.tool_name)).filter((name) => Boolean(name)).slice(0, TOOL_FAILURE_CATALOG_LINKS);
6005
6119
  const nextCommands = failing.flatMap((name, index) => [
6006
6120
  ...index === 0 ? [{ command: toolFailureExamplesCommand(name, daysBack), purpose: `Read failure examples of ${name}, the tool with the most failures.`, mutability: "read", requires_approval: false }] : [],
6007
6121
  { command: toolCatalogGetCommand(name, daysBack), purpose: `Calls, recorded schema, usage over time and failure categories of ${name}.`, mutability: "read", requires_approval: false }
6008
6122
  ]);
6009
- context.output.writeData(data, nextCommands.length > 0 ? { nextCommands } : undefined);
6123
+ context.output.writeData(data, withCohortScope(params.cohort, data, { nextCommands }));
6010
6124
  break;
6011
6125
  }
6012
6126
  case "tool-catalog": {
@@ -6205,11 +6319,21 @@ async function runCommand(command, positional, flags, positionals = positional ?
6205
6319
  }
6206
6320
  const args = flagsToArgs(flags, "id", positional);
6207
6321
  const params = parseSpecFlags("problem", args, command);
6322
+ if (params.trend === true) {
6323
+ rejectInapplicableFlags(command, params, ["evidence", "reports", "traces", "conversations", "feedback", "full", "limit", "cursor", "family", "door", "view", "offset"], "with --trend; it reads the Problem's per-day history (only --days-back applies)");
6324
+ const query3 = params.days_back ? `?days_back=${params.days_back}` : "";
6325
+ const data2 = await callDataAPI(`/problems/${encodeURIComponent(params.id)}/history${query3}`);
6326
+ context.output.writeData(data2, {
6327
+ summary: problemTrendSummary(data2),
6328
+ nextCommands: [{ command: `moda problems get ${shellArg(params.id)}`, purpose: "Open the Problem dossier.", mutability: "read", requires_approval: false }]
6329
+ });
6330
+ break;
6331
+ }
6208
6332
  if (params.view === "dream") {
6209
6333
  await runDreamProblem(params, context, command);
6210
6334
  break;
6211
6335
  }
6212
- rejectInapplicableFlags(command, params, ["offset", "days_back"], "without --view=dream; the dossier pages page with --limit and --cursor");
6336
+ rejectInapplicableFlags(command, params, ["offset", "days_back"], "without --view=dream or --trend; the dossier pages page with --limit and --cursor");
6213
6337
  const traceView = params.traces === true || params.conversations === true;
6214
6338
  const views = ["evidence", "reports", "conversations", "feedback"].filter((view2) => view2 === "conversations" ? traceView : params[view2] === true);
6215
6339
  if (views.length > 1) {
@@ -6403,7 +6527,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
6403
6527
  const params = parseSpecFlags("signal", args, command);
6404
6528
  const id = encodeURIComponent(params.id);
6405
6529
  if (params.detections !== true) {
6406
- rejectInapplicableFlags(command, params, ["days_back", "limit", "offset"], "to the signal summary; they page its hits (--detections)");
6530
+ rejectInapplicableFlags(command, params, ["days_back", "limit", "offset", "cohort"], "to the signal summary; they page its hits (--detections)");
6407
6531
  const data2 = await callDataAPI(`/custom-signals/${id}`);
6408
6532
  context.output.writeData(data2);
6409
6533
  break;
@@ -6415,10 +6539,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
6415
6539
  query2.set("limit", params.limit.toString());
6416
6540
  if (params.offset !== undefined)
6417
6541
  query2.set("offset", params.offset.toString());
6542
+ if (params.cohort)
6543
+ query2.set("cohort", params.cohort);
6418
6544
  const queryString = query2.toString() ? `?${query2.toString()}` : "";
6419
6545
  const data = await callDataAPI(`/custom-signals/${id}/detections${queryString}`);
6420
6546
  const warnings = customSignalProcessingWarning(data, "detections");
6421
- context.output.writeData(data, warnings.length > 0 ? { warnings } : undefined);
6547
+ context.output.writeData(data, withCohortScope(params.cohort, data, { warnings }));
6422
6548
  break;
6423
6549
  }
6424
6550
  case "trace-signals": {
@@ -6636,6 +6762,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
6636
6762
  case "emotions": {
6637
6763
  const args = flagsToArgs(flags);
6638
6764
  const params = parseSpecFlags("emotions", args, command);
6765
+ const compare = params.compare === true ? await loadFamilyCompare("emotions", params) : undefined;
6639
6766
  const query2 = new URLSearchParams;
6640
6767
  if (params.days_back)
6641
6768
  query2.set("days_back", params.days_back.toString());
@@ -6647,10 +6774,9 @@ async function runCommand(command, positional, flags, positionals = positional ?
6647
6774
  query2.set("family", params.family);
6648
6775
  if (readsPerTrace("emotions", params, ["user_id", "conversation_id", "conversation_ids", "ids_from", "include_window"])) {
6649
6776
  setTraceFilters(query2, params, TRACE_GRAIN_FLAGS.emotions);
6650
- const data2 = await callDataAPI(`/emotions?${query2.toString()}`);
6777
+ const data2 = withSummaryCompare(await callDataAPI(`/emotions?${query2.toString()}`), compare);
6651
6778
  const warnings = processingDisabledWarning(data2, "emotion");
6652
- const summary2 = countingSummary(data2);
6653
- context.output.writeData(data2, { ...summary2 ? { summary: summary2 } : {}, ...warnings.length > 0 ? { warnings } : {} });
6779
+ context.output.writeData(data2, withCohortScope(params.cohort, data2, { summary: countingSummary(data2), warnings }));
6654
6780
  break;
6655
6781
  }
6656
6782
  if (params.user_id)
@@ -6664,7 +6790,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
6664
6790
  if (agentMode && params.include_window !== true)
6665
6791
  query2.set("include_context", "0");
6666
6792
  const queryString = query2.toString() ? `?${query2.toString()}` : "";
6667
- const raw = await callDataAPI(`/emotions${queryString}`);
6793
+ const raw = withSummaryCompare(await callDataAPI(`/emotions${queryString}`), compare);
6668
6794
  const processingWarnings = processingDisabledWarning(raw, "emotion");
6669
6795
  const record = asRecord(raw);
6670
6796
  const data = agentMode && params.include_window !== true && record ? withoutDetectionWindows(record, "detections", "conversation_context") : raw;
@@ -6690,8 +6816,10 @@ async function runCommand(command, positional, flags, positionals = positional ?
6690
6816
  if (params.family)
6691
6817
  query2.set("family", params.family);
6692
6818
  if (params.conversation_id) {
6693
- rejectInapplicableFlags(command, params, ["search", "status", "sort", "order"], "to one trace's detections (--conversation-id); they filter and order the trace list");
6819
+ rejectInapplicableFlags(command, params, ["search", "status", "sort", "order", "cohort"], "to one trace's detections (--conversation-id); they filter and order the trace list");
6694
6820
  }
6821
+ if (params.cohort)
6822
+ query2.set("cohort", params.cohort);
6695
6823
  if (params.search)
6696
6824
  query2.set("search", params.search);
6697
6825
  if (params.status)
@@ -6701,9 +6829,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
6701
6829
  if (params.order)
6702
6830
  query2.set("order", params.order);
6703
6831
  const queryString = query2.toString() ? `?${query2.toString()}` : "";
6704
- const data = await callDataAPI(`/laziness${queryString}`);
6705
- const summary = countingSummary(data);
6706
- context.output.writeData(data, summary ? { summary } : undefined);
6832
+ if (params.compare === true)
6833
+ assertCompareScope("laziness", params);
6834
+ const [listed, compare] = await Promise.all([
6835
+ callDataAPI(`/laziness${queryString}`),
6836
+ params.compare === true ? loadFamilyCompare("laziness", params) : Promise.resolve(undefined)
6837
+ ]);
6838
+ const data = withSummaryCompare(listed, compare);
6839
+ context.output.writeData(data, withCohortScope(params.cohort, data, { summary: countingSummary(data) }));
6707
6840
  break;
6708
6841
  }
6709
6842
  case "hallucinations": {
@@ -6728,9 +6861,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
6728
6861
  query2.set("kind", params.kind);
6729
6862
  }
6730
6863
  const queryString = query2.toString() ? `?${query2.toString()}` : "";
6731
- const data = await callDataAPI(`/hallucinations${queryString}`);
6732
- const summary = countingSummary(data);
6733
- context.output.writeData(data, summary ? { summary } : undefined);
6864
+ if (params.compare === true)
6865
+ assertCompareScope("hallucinations", params);
6866
+ const [listed, compare] = await Promise.all([
6867
+ callDataAPI(`/hallucinations${queryString}`),
6868
+ params.compare === true ? loadFamilyCompare("hallucinations", params) : Promise.resolve(undefined)
6869
+ ]);
6870
+ const data = withSummaryCompare(listed, compare);
6871
+ context.output.writeData(data, withCohortScope(params.cohort, data, { summary: countingSummary(data) }));
6734
6872
  break;
6735
6873
  }
6736
6874
  case "step-scores": {
@@ -6919,7 +7057,7 @@ var commandRegistry = createCommandRegistry([
6919
7057
  resetApiRequestCountBeforeRun: true,
6920
7058
  telemetry: "result-and-error",
6921
7059
  handler: async (context) => {
6922
- const { runInit } = await import("./index-w7v472w4.js");
7060
+ const { runInit } = await import("./index-svfzq5rb.js");
6923
7061
  if (context.outputMode === "agent-stream") {
6924
7062
  context.output.writeEvent({
6925
7063
  event: "started",
@@ -6969,7 +7107,7 @@ var commandRegistry = createCommandRegistry([
6969
7107
  resetApiRequestCountBeforeRun: true,
6970
7108
  telemetry: "result-and-error",
6971
7109
  handler: async (context) => {
6972
- const { runProvision } = await import("./provision-cya5rpxa.js");
7110
+ const { runProvision } = await import("./provision-a9kkfvp7.js");
6973
7111
  return runProvision(context, {
6974
7112
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
6975
7113
  });
@@ -7233,6 +7371,40 @@ var commandRegistry = createCommandRegistry([
7233
7371
  telemetry: "result-and-error",
7234
7372
  handler: runUserCommand
7235
7373
  },
7374
+ {
7375
+ name: "cohorts",
7376
+ description: NOUN_SUMMARIES.cohorts,
7377
+ examples: [...leavesOf("cohorts").flatMap((leaf) => leaf.examples.slice(0, 1))],
7378
+ subcommands: treeSubcommandHelp("cohorts"),
7379
+ auth: "api-key",
7380
+ outputModes: ["human", "json", "agent"],
7381
+ mutability: "write",
7382
+ validateConfigBeforeRun: false,
7383
+ resetApiRequestCountBeforeRun: false,
7384
+ telemetry: "result-and-error",
7385
+ handler: canonicalNounHandler
7386
+ },
7387
+ ...[
7388
+ ["cohort-list", "Built-in and saved user cohorts", "read"],
7389
+ ["cohort-get", "One cohort: name, definition and revision", "read"],
7390
+ ["cohort-fields", "The fields and operators a cohort definition can use", "read"],
7391
+ ["cohort-preview", "Member count and sample users for an unsaved definition", "read"],
7392
+ ["cohort-compare", "Two cohorts side by side: size, health, top topics, failure mix", "read"],
7393
+ ["cohort-create", "Save a cohort from a definition", "write"],
7394
+ ["cohort-update", "Edit a saved cohort", "write"]
7395
+ ].map(([name, description, mutability]) => ({
7396
+ name,
7397
+ description,
7398
+ examples: [...CANONICAL_LEAVES.find((leaf) => leaf.handler === name)?.examples ?? []],
7399
+ hidden: true,
7400
+ auth: "api-key",
7401
+ outputModes: ["human", "json", "agent"],
7402
+ mutability,
7403
+ validateConfigBeforeRun: false,
7404
+ resetApiRequestCountBeforeRun: false,
7405
+ telemetry: "result-and-error",
7406
+ handler: runCohortCommand
7407
+ })),
7236
7408
  {
7237
7409
  name: "replays",
7238
7410
  description: "Read replay results: sets, run history, per-seed verdicts, and playout transcripts",
@@ -7349,7 +7521,7 @@ var commandRegistry = createCommandRegistry([
7349
7521
  legacyCommand({
7350
7522
  name: "overview",
7351
7523
  description: "Harness health briefing from production data",
7352
- examples: ["moda overview --days-back=30", "moda --agent overview"],
7524
+ examples: ["moda overview --days-back=30", "moda --agent overview", "moda overview --compare --since=2026-10-17T14:00:00Z # after a deploy against the same span before it", "moda overview --series --days-back=14"],
7353
7525
  ...dataApiDefaults
7354
7526
  }),
7355
7527
  legacyCommand({
@@ -3,13 +3,14 @@ import {
3
3
  promptsDiscoveredCheck,
4
4
  runDoctorCommand,
5
5
  runStatusCommand
6
- } from "./cli-a8nxbtpk.js";
6
+ } from "./cli-h62j4jn7.js";
7
7
  import"./cli-pmzh2qjy.js";
8
- import"./cli-4tg0hkbz.js";
8
+ import"./cli-k5drmy8r.js";
9
9
  import"./cli-mm48br00.js";
10
10
  import"./cli-xq45etg1.js";
11
- import"./cli-xddtv3yn.js";
12
- import"./cli-f1jfrqgf.js";
11
+ import"./cli-5fhqfx04.js";
12
+ import"./cli-e0kbye4s.js";
13
+ import"./cli-9a0a3v13.js";
13
14
  import"./cli-0xc5htv7.js";
14
15
  import"./cli-t0pb7qbs.js";
15
16
  import"./cli-k4tqwah4.js";
@@ -5,7 +5,7 @@ import {
5
5
  } from "./cli-daqkdnm1.js";
6
6
  import {
7
7
  assertFlags
8
- } from "./cli-f1jfrqgf.js";
8
+ } from "./cli-9a0a3v13.js";
9
9
  import {
10
10
  callControlAPI
11
11
  } from "./cli-0xc5htv7.js";
@@ -40,15 +40,16 @@ import {
40
40
  writeHarnessAnalyzeResult,
41
41
  writeHarnessArtifacts,
42
42
  writeHumanProgress
43
- } from "./cli-azage7s0.js";
43
+ } from "./cli-s1w5phms.js";
44
44
  import"./cli-xq45etg1.js";
45
45
  import"./cli-97fnztpy.js";
46
46
  import {
47
47
  HARNESS_ANALYZE_CONCERNS
48
48
  } from "./cli-cesds766.js";
49
49
  import"./cli-qpq84eee.js";
50
- import"./cli-xddtv3yn.js";
51
- import"./cli-f1jfrqgf.js";
50
+ import"./cli-5fhqfx04.js";
51
+ import"./cli-e0kbye4s.js";
52
+ import"./cli-9a0a3v13.js";
52
53
  import"./cli-0xc5htv7.js";
53
54
  import"./cli-t0pb7qbs.js";
54
55
  import"./cli-k4tqwah4.js";
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  buildSourceSnapshot
3
- } from "./cli-azage7s0.js";
3
+ } from "./cli-s1w5phms.js";
4
4
  import"./cli-xq45etg1.js";
5
5
  import"./cli-97fnztpy.js";
6
6
  import"./cli-cesds766.js";
7
7
  import"./cli-qpq84eee.js";
8
- import"./cli-xddtv3yn.js";
9
- import"./cli-f1jfrqgf.js";
8
+ import"./cli-5fhqfx04.js";
9
+ import"./cli-e0kbye4s.js";
10
+ import"./cli-9a0a3v13.js";
10
11
  import"./cli-0xc5htv7.js";
11
12
  import {
12
13
  recordApiRequest