@moda-ai/cli 1.55.40 → 1.55.41

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/cli.js CHANGED
@@ -3214,7 +3214,7 @@ async function runAgentCommand(...args) {
3214
3214
  return (await import("./agent-ck6y31n2.js")).runAgentCommand(...args);
3215
3215
  }
3216
3216
  async function runProductionCommand(...args) {
3217
- return (await import("./production-intelligence-hs2sm8t5.js")).runProductionCommand(...args);
3217
+ return (await import("./production-intelligence-e5vsc6cw.js")).runProductionCommand(...args);
3218
3218
  }
3219
3219
  function parseArgs(argv) {
3220
3220
  const args = argv.slice(2);
@@ -452,6 +452,7 @@ async function runProductionCommand(context) {
452
452
  if (context.command === "investigate") {
453
453
  const investigation = await investigateProduction({
454
454
  daysBack,
455
+ daysBackGiven: context.flags["days-back"] !== undefined,
455
456
  cwd: context.cwd,
456
457
  conversationId: context.flags.trace ?? context.flags.conversation,
457
458
  toolName: context.flags.tool
@@ -553,14 +554,16 @@ async function investigateProduction(options) {
553
554
  const overviewEndpoint = `/overview?days_back=${options.daysBack}`;
554
555
  const toolEndpoint = options.toolName ? `/tool-failures/${encodeURIComponent(options.toolName)}?days_back=${options.daysBack}&limit=5` : `/tool-failures?days_back=${options.daysBack}`;
555
556
  const frustrationsEndpoint = `${investigateFrustrationsEndpoint(options.daysBack)}${options.conversationId ? `&conversation_id=${encodeURIComponent(options.conversationId)}` : ""}`;
556
- const problemsEndpoint = `/problems?days_back=${options.daysBack}&limit=${INVESTIGATION_PROBLEM_LIMIT}`;
557
557
  const skipped = { data: null, status: "ok" };
558
- const [overview, tools, frustrations, problems] = await Promise.all([
558
+ const noProblems = { result: skipped, view: null, endpoint: "", warnings: [] };
559
+ const [overview, tools, frustrations, problemsRead] = await Promise.all([
559
560
  safeData(overviewEndpoint),
560
561
  safeData(toolEndpoint),
561
562
  options.failureOnly ? Promise.resolve(skipped) : safeData(frustrationsEndpoint),
562
- options.failureOnly ? Promise.resolve(skipped) : safeData(problemsEndpoint)
563
+ options.failureOnly ? Promise.resolve(noProblems) : loadInvestigationProblems(options.daysBack, options.daysBackGiven === true)
563
564
  ]);
565
+ const problems = problemsRead.result;
566
+ warnings.push(...problemsRead.warnings);
564
567
  const sources = options.failureOnly ? { overview, tools } : { overview, tools, frustrations, problems };
565
568
  for (const [key, result] of Object.entries(sources)) {
566
569
  sourceStatus[key] = result.status;
@@ -586,14 +589,14 @@ async function investigateProduction(options) {
586
589
  findings.push(...toolFailureFindings(tools.data, options.daysBack, hints, evidenceRefs, options.toolName));
587
590
  if (!options.failureOnly) {
588
591
  findings.push(...frustrationFindings(frustrations.data, options.daysBack, evidenceRefs));
589
- findings.push(...problemFindings(problems.data, options.daysBack, evidenceRefs));
592
+ findings.push(...problemFindings(problems.data, problemsRead, options.daysBack, evidenceRefs));
590
593
  }
591
594
  if (findings.length === 0) {
592
595
  findings.push(failedSources.length > 0 ? incompleteDataFinding(failedSources, Object.keys(sources).length) : insufficientDataFinding(overview.data, evidenceRefs));
593
596
  }
594
- findings.sort(compareFindings);
597
+ sortFindingsKeepingProblemOrder(findings);
595
598
  const boundedFindings = capFindings(findings, 8, INVESTIGATION_PROBLEM_SLOTS);
596
- const nextCommands = nextCommandsForFindings(boundedFindings, options.daysBack, !options.failureOnly);
599
+ const nextCommands = nextCommandsForFindings(boundedFindings, options.daysBack, problemsRead.view?.command);
597
600
  const summary = summarizeInvestigation(boundedFindings);
598
601
  if (failedSources.length > 0) {
599
602
  summary.text = `Incomplete: could not load ${failedSources.join(", ")}, so findings cover only ${Object.keys(sources).filter((key) => !failedSources.includes(key)).join(", ")}. ${summary.text}`;
@@ -618,6 +621,7 @@ async function investigateProduction(options) {
618
621
  nextCommands,
619
622
  warnings,
620
623
  sourceStatus,
624
+ ...problemsRead.view ? { problemsView: problemsRead.view } : {},
621
625
  degraded: failedSources.length > 0,
622
626
  failedSources
623
627
  };
@@ -648,6 +652,7 @@ async function askModa(options) {
648
652
  }
649
653
  const investigation = await investigateProduction({
650
654
  daysBack: options.daysBack,
655
+ daysBackGiven: options.daysBackGiven,
651
656
  cwd: options.cwd,
652
657
  question: options.question
653
658
  });
@@ -727,6 +732,7 @@ async function askModaForContext(context, question, daysBack) {
727
732
  return askModa({
728
733
  question,
729
734
  daysBack,
735
+ daysBackGiven: context.flags["days-back"] !== undefined,
730
736
  cwd: context.cwd,
731
737
  profile: context.profile,
732
738
  onNetworkRetry: context.verbose ? () => safeWriteStderr(`[moda] network hiccup reaching Moda Cloud; retrying the buffered ask once…
@@ -975,8 +981,67 @@ function frustrationEvidenceIds(row, anchorIndex) {
975
981
  ids.msg_index = anchorIndex;
976
982
  return ids;
977
983
  }
978
- function problemRows(data) {
984
+ function windowLength(hours) {
985
+ return hours >= 48 && hours % 24 === 0 ? `${hours / 24} days` : `${hours} hours`;
986
+ }
987
+ async function loadInvestigationProblems(daysBack, daysBackGiven) {
988
+ const dreamEndpoint = `/problems/dream?${daysBackGiven ? `days_back=${daysBack}&` : ""}limit=${INVESTIGATION_PROBLEM_LIMIT}`;
989
+ const dream = await safeData(dreamEndpoint);
990
+ const dreamData = asRecord(dream.data);
991
+ if (dream.status === "ok" && dreamData?.available === true) {
992
+ const windowHours2 = asNumber(asRecord(dreamData.run)?.window_hours);
993
+ return {
994
+ result: dream,
995
+ endpoint: dreamEndpoint,
996
+ warnings: [],
997
+ view: {
998
+ view: "dream",
999
+ window: daysBackGiven ? `the last ${daysBack} day(s)` : windowHours2 !== null ? `the dream run's window (${windowLength(windowHours2)}), the dashboard's default range` : "the dream run's window, the dashboard's default range",
1000
+ command: `moda problems list --view=dream${daysBackGiven ? ` --days-back=${daysBack}` : ""}`
1001
+ }
1002
+ };
1003
+ }
1004
+ if (dream.status === "unavailable" && isAuthFailure(dream.error)) {
1005
+ return { result: dream, endpoint: dreamEndpoint, view: null, warnings: [] };
1006
+ }
1007
+ const bookEndpoint = `/problems?days_back=${daysBack}&limit=${INVESTIGATION_PROBLEM_LIMIT}`;
1008
+ const book = await safeData(bookEndpoint);
1009
+ const windowHours = asNumber(asRecord(asRecord(book.data)?.summary)?.window_hours);
1010
+ return {
1011
+ result: book,
1012
+ endpoint: bookEndpoint,
1013
+ warnings: dream.status === "unavailable" ? [`Could not check whether this tenant's dashboard shows the dream grouping (${dream.warning ?? dreamEndpoint}); the Problems below are the Book's ranked list, which may differ from the dashboard's.`] : [],
1014
+ view: {
1015
+ view: "book",
1016
+ window: windowHours !== null && windowHours < daysBack * 24 ? `the last ${windowLength(windowHours)} (the Book's serving window within the last ${daysBack} day(s))` : `the last ${daysBack} day(s)`,
1017
+ command: `moda problems list --view=book --days-back=${daysBack}`
1018
+ }
1019
+ };
1020
+ }
1021
+ function problemRows(data, view) {
979
1022
  const record = asRecord(data);
1023
+ if (view === "dream") {
1024
+ const items = Array.isArray(record?.problems) ? record.problems : [];
1025
+ return items.flatMap((item) => {
1026
+ const row = asRecord(item);
1027
+ const id = asString(row?.problem_id);
1028
+ if (!row || !id)
1029
+ return [];
1030
+ const incidents = numberValue(row.incidents);
1031
+ const tier = asString(asRecord(row.severity)?.tier);
1032
+ return [{
1033
+ id,
1034
+ name: asString(row.name) || id,
1035
+ cause: asString(row.cause_statement) || undefined,
1036
+ lifecycle: asString(row.lifecycle) ?? "open",
1037
+ trust: "dream",
1038
+ affectedConversations: incidents,
1039
+ ...tier ? { severityTier: tier } : {},
1040
+ users: numberValue(row.users),
1041
+ incidents
1042
+ }];
1043
+ }).slice(0, INVESTIGATION_PROBLEM_LIMIT);
1044
+ }
980
1045
  const rows = [];
981
1046
  const push = (items, trust) => {
982
1047
  if (!Array.isArray(items))
@@ -996,8 +1061,7 @@ function problemRows(data) {
996
1061
  lifecycle,
997
1062
  trust,
998
1063
  verification: asString(row.verification_status) ?? undefined,
999
- affectedConversations: numberValue(row.affected_conversations) || numberValue(asRecord(row.evidence_support)?.attribution_count),
1000
- raw: row
1064
+ affectedConversations: numberValue(row.affected_conversations) || numberValue(asRecord(row.evidence_support)?.attribution_count)
1001
1065
  });
1002
1066
  }
1003
1067
  };
@@ -1006,38 +1070,74 @@ function problemRows(data) {
1006
1070
  push(record?.emerging, "emerging");
1007
1071
  return rows.slice(0, INVESTIGATION_PROBLEM_LIMIT);
1008
1072
  }
1009
- function problemFindings(data, daysBack, evidenceRefs) {
1010
- return problemRows(data).map((problem, index) => {
1073
+ function dreamSeverity(tier) {
1074
+ if (tier === "P0" || tier === "P1")
1075
+ return "critical";
1076
+ if (tier === "P3")
1077
+ return "medium";
1078
+ return "high";
1079
+ }
1080
+ function problemFindings(data, read, daysBack, evidenceRefs) {
1081
+ if (!read.view)
1082
+ return [];
1083
+ const view = read.view;
1084
+ return problemRows(data, view.view).map((problem, index) => {
1011
1085
  const evidenceId = `problem:${problem.id}`;
1012
- const trusted = problem.trust === "trusted";
1013
- const trustNote = trusted ? "verified" : problem.trust === "emerging" ? "emerging, not yet ranked" : `untrusted (${problem.verification ?? "unverified"})`;
1086
+ const dream = problem.trust === "dream";
1087
+ const trusted = problem.trust === "trusted" || dream;
1088
+ const trustNote = dream ? `dream grouping${problem.severityTier ? `, ${problem.severityTier}` : ""}` : problem.trust === "trusted" ? "verified" : problem.trust === "emerging" ? "emerging, not yet ranked" : `untrusted (${problem.verification ?? "unverified"})`;
1014
1089
  evidenceRefs.push({
1015
1090
  id: evidenceId,
1016
1091
  kind: "problem",
1017
1092
  label: `Problem "${problem.name}" (${trustNote})`,
1018
- endpoint: `/problems?days_back=${daysBack}&limit=${INVESTIGATION_PROBLEM_LIMIT}`,
1019
- value: { problem_id: problem.id, lifecycle: problem.lifecycle, trust: problem.trust, affected_conversations: problem.affectedConversations }
1093
+ endpoint: read.endpoint,
1094
+ value: dream ? {
1095
+ problem_id: problem.id,
1096
+ view: "dream",
1097
+ rank: index + 1,
1098
+ lifecycle: problem.lifecycle,
1099
+ severity_tier: problem.severityTier ?? null,
1100
+ users: problem.users ?? 0,
1101
+ incidents: problem.incidents ?? 0
1102
+ } : {
1103
+ problem_id: problem.id,
1104
+ view: "book",
1105
+ rank: index + 1,
1106
+ lifecycle: problem.lifecycle,
1107
+ trust: problem.trust,
1108
+ affected_conversations: problem.affectedConversations
1109
+ }
1020
1110
  });
1021
1111
  const affected = problem.affectedConversations;
1112
+ const id = shellArg(problem.id);
1113
+ const dossier = {
1114
+ id: `action_problem_${slug(problem.id)}`,
1115
+ title: `Open the Problem dossier for ${problem.name}.`,
1116
+ rationale: "The dossier has the cause, evidence, affected traces and verification state.",
1117
+ command: `moda problems get ${id}`,
1118
+ mutability: "read",
1119
+ requiresApproval: false
1120
+ };
1121
+ const days = view.command.includes("--days-back=") ? ` --days-back=${daysBack}` : "";
1022
1122
  return {
1023
1123
  id: `finding_problem_${slug(problem.id)}`,
1024
1124
  kind: "problem",
1025
1125
  title: `Problem: ${problem.name}`,
1026
- severity: trusted ? affected >= 10 ? "critical" : "high" : "medium",
1126
+ severity: dream ? dreamSeverity(problem.severityTier) : trusted ? affected >= 10 ? "critical" : "high" : "medium",
1027
1127
  confidence: trusted ? "high" : "low",
1028
- impactScore: (trusted ? 100 : 60) + Math.min(affected, 25) - index,
1029
- rankReason: trusted ? `Rank #${index + 1} verified Problem in \`moda problems\`${affected > 0 ? `, affecting ${affected} trace(s)` : ""}.` : `Listed by \`moda problems\` as ${trustNote}; treat the cause as a hypothesis.`,
1030
- summary: problem.cause ? `${problem.cause}${problem.lifecycle !== "open" ? ` (${problem.lifecycle})` : ""}` : `Problem ${problem.id} is ${problem.lifecycle} in the last ${daysBack} day(s).`,
1128
+ impactScore: (trusted ? 100 : 60) + Math.min(dream ? problem.users ?? 0 : affected, 25) - index,
1129
+ rankReason: dream ? `Rank #${index + 1} in the dream grouping the dashboard shows (\`${view.command}\`)${problem.severityTier ? `, severity ${problem.severityTier}` : ""}: ${problem.users ?? 0} user(s), ${problem.incidents ?? 0} incident(s) in ${view.window}.` : trusted ? `Rank #${index + 1} verified Problem in the Book (\`${view.command}\`)${affected > 0 ? `, affecting ${affected} trace(s)` : ""}.` : `Listed in the Book (\`${view.command}\`) as ${trustNote}; treat the cause as a hypothesis.`,
1130
+ summary: problem.cause ? `${problem.cause}${problem.lifecycle !== "open" ? ` (${problem.lifecycle})` : ""}` : `Problem ${problem.id} is ${problem.lifecycle} in ${view.window}.`,
1031
1131
  evidenceRefIds: [evidenceId],
1032
1132
  likelyLocations: [],
1033
- recommendedActions: [{
1034
- id: `action_problem_${slug(problem.id)}`,
1035
- title: `Open the Problem dossier for ${problem.name}.`,
1036
- rationale: "The dossier has the cause, evidence, affected traces and verification state.",
1037
- command: `moda problems get ${shellArg(problem.id)}`,
1133
+ recommendedActions: dream ? [dossier, {
1134
+ id: `action_problem_dream_${slug(problem.id)}`,
1135
+ title: `Open ${problem.name} as the dashboard shows it.`,
1136
+ rationale: "The dream page has its definition, severity and most serious incidents (dream ids are Book ids).",
1137
+ command: `moda problems get ${id} --view=dream${days}`,
1038
1138
  mutability: "read",
1039
1139
  requiresApproval: false
1040
- }]
1140
+ }] : [dossier]
1041
1141
  };
1042
1142
  });
1043
1143
  }
@@ -1477,6 +1577,10 @@ function renderInvestigationHuman(investigation) {
1477
1577
  const lines = [];
1478
1578
  lines.push("Moda investigation");
1479
1579
  lines.push(`Window: last ${investigation.daysBack} day(s)`);
1580
+ if (investigation.problemsView) {
1581
+ const { view, window, command } = investigation.problemsView;
1582
+ lines.push(`Problems: ${view === "dream" ? "the dream grouping the dashboard shows" : "the Book's ranked list"}, counts over ${window} (${command})`);
1583
+ }
1480
1584
  lines.push("");
1481
1585
  lines.push(investigation.summary.text);
1482
1586
  for (const finding of investigation.findings) {
@@ -1734,11 +1838,11 @@ function summarizeInvestigation(findings) {
1734
1838
  confidence: top.confidence
1735
1839
  };
1736
1840
  }
1737
- function nextCommandsForFindings(findings, daysBack, includeProblems = false) {
1841
+ function nextCommandsForFindings(findings, daysBack, problemsCommand) {
1738
1842
  const limit = 8;
1739
1843
  const primary = [...new Set(findings.map((finding) => finding.recommendedActions[0]?.command).filter(Boolean))];
1740
1844
  const generic = [
1741
- ...includeProblems ? [`moda problems --days-back=${daysBack}`] : [],
1845
+ ...problemsCommand ? [problemsCommand] : [],
1742
1846
  `moda overview --days-back=${daysBack}`,
1743
1847
  "moda doctor --online"
1744
1848
  ];
@@ -1764,6 +1868,15 @@ function capFindings(sorted, limit, problemSlots) {
1764
1868
  const kept = new Set([...reserved, ...others]);
1765
1869
  return sorted.filter((finding) => kept.has(finding));
1766
1870
  }
1871
+ function sortFindingsKeepingProblemOrder(findings) {
1872
+ const listOrder = findings.filter((finding) => finding.kind === "problem");
1873
+ findings.sort(compareFindings);
1874
+ let next = 0;
1875
+ for (let i = 0;i < findings.length; i += 1) {
1876
+ if (findings[i].kind === "problem")
1877
+ findings[i] = listOrder[next++];
1878
+ }
1879
+ }
1767
1880
  function compareFindings(a, b) {
1768
1881
  const severityDelta = severityWeight(b.severity) - severityWeight(a.severity);
1769
1882
  if (severityDelta !== 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.55.40",
3
+ "version": "1.55.41",
4
4
  "description": "CLI for Moda: production monitoring and evals for AI agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "moda.skill_index.v1",
3
- "bundled_at": "2026-10-18T01:43:16.412Z",
4
- "cli_version": "1.55.40",
3
+ "bundled_at": "2026-10-18T02:11:16.441Z",
4
+ "cli_version": "1.55.41",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-cloudflare-think",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: moda-cli
3
- version: 3.0.6
3
+ version: 3.0.7
4
4
  description: Investigate a production AI agent with the Moda CLI (`moda`) - find where something happened in real traces (traces search, then traces get), check agent health, rank what to fix (investigate, problems), and dig into tool failures, user frustration, and hallucinations. Also prompt registry sync/promote, replays, custom signals, and false-positive marking. Use when the user mentions Moda, production traces or conversations of their agent, agent failures, or asks what to fix.
5
5
  ---
6
6
 
@@ -60,7 +60,7 @@ Details: `references/output-protocol.md`.
60
60
  | Where did X happen / traces about Y | `moda traces search "<words>"` (`--mode=keyword` for ids, error codes, exact strings; `--include-tool-io` for tool inputs/outputs) | `moda traces get <conversation_id> --msg-index=<message_index> --window=3` |
61
61
  | Search returned nothing | Read `.warnings` (degraded mode, reason). Try a shorter phrase, `--mode=keyword`, a wider `--time-range` | `moda traces --search="<words>"` |
62
62
  | Is the agent healthy? | `moda overview --days-back=7` | `moda investigate` |
63
- | What to fix first? | `moda investigate` or `moda problems list` (free; the same list the dashboard shows) | `moda problems get <problem_id>` (`--view=dream` on a dream-grouped tenant), then `--evidence` / `--traces` |
63
+ | What to fix first? | `moda investigate` or `moda problems list` (free; both rank the same list the dashboard shows, `data.problemsView` names it) | `moda problems get <problem_id>` (`--view=dream` on a dream-grouped tenant), then `--evidence` / `--traces` |
64
64
  | Why is tool T failing? | `moda detections tools` | `moda detections tools <T>` (use each row's `anchor`) |
65
65
  | How often is tool T called / what is its schema? | `moda tools list` (every tool: calls, failed calls, success rate; `--sort=failure_rate`) | `moda tools get <T>` (registered schema, observed payload fields, usage over time, top failure categories) |
66
66
  | What frustrates users? | `moda detections list --family=frustration --limit=10` | `moda traces get <anchor.conversation_id> --msg-index=<anchor.msg_index>` |
@@ -89,7 +89,7 @@ names the closest values. `moda <noun> <verb> --help` shows which.
89
89
 
90
90
  ## Fixing a Problem
91
91
 
92
- 1. `moda investigate` or `moda problems` to pick one; note its `problem_id`.
92
+ 1. `moda investigate` or `moda problems list` to pick one (both show the dashboard's Problems in its order); note its `problem_id`.
93
93
  2. `moda problems get <problem_id> --evidence` and open two or three anchors with
94
94
  `moda traces get <id> --msg-index=<n>` before deciding on a cause.
95
95
  3. Make the change in code; cite the problem id and trace ids in the commit or PR.
@@ -55,7 +55,11 @@ Data API fallback. Abbreviated envelope:
55
55
  narrows the tool-failure evidence to one tool, and `--trace` (legacy alias
56
56
  `--conversation`) scopes only the frustration evidence (overview, tool
57
57
  failures and Problems stay tenant-wide). Its findings include the top 1–3
58
- Problems from `moda problems`, with `moda problems get <id>` as the next command.
58
+ Problems of the list `moda problems list` shows (the dream grouping when the
59
+ dashboard shows one, otherwise the Book), in the same order, with
60
+ `moda problems get <id>` as the next command. `data.problemsView` names the
61
+ view, the window its counts cover, and the `moda problems list` command that
62
+ lists the same Problems.
59
63
  `moda failures` is the tool-failures-only view.
60
64
 
61
65
  After reporting the top behavioral failure and the harness layer it routes to