@moda-ai/cli 1.55.38 → 1.55.40

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 (39) hide show
  1. package/dist/{agent-9r58v0bc.js → agent-ck6y31n2.js} +8 -8
  2. package/dist/{cli-3n040fp2.js → cli-0xc5htv7.js} +1 -1
  3. package/dist/{cli-tdcx8yz9.js → cli-1z1sx8dg.js} +3 -3
  4. package/dist/{cli-dmnayt2r.js → cli-4tg0hkbz.js} +8 -8
  5. package/dist/{cli-9g5kzsg6.js → cli-77ekrx7d.js} +1 -1
  6. package/dist/{cli-93926afn.js → cli-7bn406mt.js} +4 -4
  7. package/dist/{cli-xtyszvq1.js → cli-a8nxbtpk.js} +5 -5
  8. package/dist/{cli-skvvwp2q.js → cli-azage7s0.js} +5 -5
  9. package/dist/{cli-xk630wjq.js → cli-daqkdnm1.js} +1 -1
  10. package/dist/{cli-xheg54qc.js → cli-f1jfrqgf.js} +39 -7
  11. package/dist/{cli-n311b8g0.js → cli-gt3yq1y5.js} +2 -2
  12. package/dist/{cli-sk1p71wr.js → cli-pmzh2qjy.js} +1 -1
  13. package/dist/{cli-rcjhfts4.js → cli-t0pb7qbs.js} +66 -16
  14. package/dist/{cli-b7nyw498.js → cli-xddtv3yn.js} +1 -1
  15. package/dist/{cli-nh23q6ay.js → cli-ypqwcf9n.js} +5 -5
  16. package/dist/cli.js +587 -52
  17. package/dist/{doctor-xq5bjvpq.js → doctor-p8sp6cbk.js} +7 -7
  18. package/dist/{fix-kvszavm1.js → fix-0r974yge.js} +4 -4
  19. package/dist/{harness-qatnwxrz.js → harness-72ejenza.js} +5 -5
  20. package/dist/{harness-github-actions-drbkcb8d.js → harness-github-actions-2qc4gg2w.js} +5 -5
  21. package/dist/{index-y5jnz2t2.js → index-w7v472w4.js} +11 -11
  22. package/dist/{production-intelligence-j55vaw6t.js → production-intelligence-hs2sm8t5.js} +3 -3
  23. package/dist/{profile-commands-skdxe17v.js → profile-commands-se9947c1.js} +3 -3
  24. package/dist/{prompt-attribution-zq38actx.js → prompt-attribution-3987hhqz.js} +5 -5
  25. package/dist/prompt-source-mcp.js +5 -5
  26. package/dist/{prompts-ab-yd2dawe3.js → prompts-ab-gmd10n5d.js} +7 -7
  27. package/dist/{prompts-propose-11dj9ked.js → prompts-propose-q626zmm4.js} +7 -7
  28. package/dist/{prompts-cwqn8a57.js → prompts-ysm9ra72.js} +5 -5
  29. package/dist/{provision-ghca9d54.js → provision-cya5rpxa.js} +5 -5
  30. package/dist/{registry-76sngjw6.js → registry-7xz09ry0.js} +3 -3
  31. package/dist/{skills-btn9r48v.js → skills-x6yszxxn.js} +5 -5
  32. package/dist/{sync-ye7nxeen.js → sync-trjzvam9.js} +7 -7
  33. package/package.json +1 -1
  34. package/skills/integration/index.json +2 -2
  35. package/skills/moda-cli/SKILL.md +7 -3
  36. package/skills/moda-cli/references/commands.md +3 -2
  37. package/skills/moda-cli/references/dashboard-parity.md +2 -0
  38. package/skills/moda-cli/references/detections.md +29 -0
  39. package/skills/moda-cli/references/production-intelligence.md +1 -1
package/dist/cli.js CHANGED
@@ -13,6 +13,8 @@ import {
13
13
  frustrationsFromEmotions,
14
14
  mapWithConcurrency,
15
15
  parseBatchTarget,
16
+ parseTargetLines,
17
+ readIdsFrom,
16
18
  shapeToolCatalogDetail,
17
19
  toolCatalogGetCommand,
18
20
  toolFailureExamplesCommand,
@@ -20,7 +22,7 @@ import {
20
22
  withFrustrationAnchors,
21
23
  withToolFailureAnchors,
22
24
  withoutDetectionWindows
23
- } from "./cli-n311b8g0.js";
25
+ } from "./cli-gt3yq1y5.js";
24
26
  import {
25
27
  createCommandContext,
26
28
  envOptOutSet,
@@ -28,17 +30,22 @@ import {
28
30
  optOutFlagSet,
29
31
  parseBooleanFlagValue,
30
32
  withoutFlag
31
- } from "./cli-b7nyw498.js";
33
+ } from "./cli-xddtv3yn.js";
32
34
  import {
33
35
  AuditSchema,
36
+ BOOK_PROBLEM_SORTS,
34
37
  COMMAND_FLAG_SPECS,
35
38
  DETECTION_VERDICTS,
39
+ DREAM_PROBLEM_SORTS,
36
40
  DetectionReviewSchema,
37
41
  EMOTION_FAMILIES,
38
42
  FALSE_POSITIVE_KINDS,
39
43
  FEEDBACK_USAGE,
40
44
  FalsePositiveSchema,
41
45
  LAZINESS_FAMILIES,
46
+ MAX_CONVERSATION_IDS_FILTER,
47
+ PROBLEMS_DEFAULT_LIMIT,
48
+ PROBLEMS_MAX_LIMIT,
42
49
  PROBLEM_FEEDBACK_ACTIONS,
43
50
  ProblemFeedbackSchema,
44
51
  ProblemImpactSchema,
@@ -48,6 +55,7 @@ import {
48
55
  SignalLabelSchema,
49
56
  StepScoresSchema,
50
57
  TRACE_GRAIN_FLAGS,
58
+ TraceFindingsSchema,
51
59
  UUID_RE,
52
60
  assertFlags,
53
61
  assertPositionals,
@@ -71,12 +79,12 @@ import {
71
79
  unknownSignalsSubcommandError,
72
80
  usageLine,
73
81
  writeDryRun
74
- } from "./cli-xheg54qc.js";
82
+ } from "./cli-f1jfrqgf.js";
75
83
  import {
76
84
  ApiError,
77
85
  callControlAPI,
78
86
  callDataAPI
79
- } from "./cli-3n040fp2.js";
87
+ } from "./cli-0xc5htv7.js";
80
88
  import {
81
89
  AGENT_ERROR_CODES,
82
90
  EXIT_AUTH_REQUIRED,
@@ -113,7 +121,7 @@ import {
113
121
  shellArg,
114
122
  validateConfig,
115
123
  wasOutputCancelled
116
- } from "./cli-rcjhfts4.js";
124
+ } from "./cli-t0pb7qbs.js";
117
125
  import {
118
126
  CliAccessError,
119
127
  CliAuthError,
@@ -136,6 +144,175 @@ import {
136
144
  import { randomUUID as randomUUID3 } from "node:crypto";
137
145
  import { z } from "zod";
138
146
 
147
+ // src/trace-findings.ts
148
+ var FINDINGS_FAMILIES = ["emotion", "laziness", "hallucination", "tool_failure", "custom_signal"];
149
+ var FINDINGS_DEFAULT_LIMIT = 3;
150
+ var NEXT_COMMANDS_SINGLE = 8;
151
+ var NEXT_COMMANDS_BATCH = 2;
152
+ var TEXT_CHARS = 160;
153
+ function familyState(entry) {
154
+ const count = asNumber(entry.count) ?? 0;
155
+ if (count > 0)
156
+ return "found";
157
+ const enabled = asRecord(entry.detector)?.enabled;
158
+ if (enabled === false || entry.processed === false)
159
+ return "not_run";
160
+ if (entry.processed === true)
161
+ return "none";
162
+ return "unknown";
163
+ }
164
+ function clip(value, max = TEXT_CHARS) {
165
+ const text = asString(value);
166
+ if (!text)
167
+ return;
168
+ return text.length <= max ? text : `${text.slice(0, max - 3)}...`;
169
+ }
170
+ function stripUndefined(row) {
171
+ return Object.fromEntries(Object.entries(row).filter(([, value]) => value !== undefined && value !== ""));
172
+ }
173
+ function compactRow(family, row) {
174
+ const msgIndex = asNumber(row.msg_index);
175
+ switch (family) {
176
+ case "emotion": {
177
+ const quote = asRecord(Array.isArray(row.user_quotes) ? row.user_quotes[0] : undefined);
178
+ return stripUndefined({
179
+ msg_index: msgIndex,
180
+ family: row.family,
181
+ polarity: row.polarity,
182
+ score: row.score,
183
+ trajectory: row.trajectory,
184
+ cause: clip(row.primary_cause),
185
+ quote: clip(quote?.quote)
186
+ });
187
+ }
188
+ case "laziness":
189
+ return stripUndefined({ msg_index: msgIndex, family: row.family, pattern: row.pattern_id, confidence: row.confidence, snippet: clip(row.snippet) });
190
+ case "hallucination":
191
+ return stripUndefined({ msg_index: msgIndex, rule: row.rule_id, confidence: row.confidence, claim: clip(row.offending_substring), evidence: clip(row.evidence_quote) });
192
+ case "tool_failure":
193
+ return stripUndefined({ msg_index: msgIndex, tool: row.tool_name, tool_use_id: row.tool_use_id, category: row.category, error: clip(row.error_message) });
194
+ case "custom_signal":
195
+ return stripUndefined({ msg_index: msgIndex, signal: row.signal_name, signal_id: row.signal_id, confidence: row.confidence, rationale: clip(row.rationale) });
196
+ }
197
+ }
198
+ function anchorCommand(conversationId, msgIndex) {
199
+ return `moda traces get ${shellArg(contextTarget(conversationId))} --msg-index=${msgIndex} --window=2`;
200
+ }
201
+ function compactFindings(raw, opts) {
202
+ const record = asRecord(raw) ?? {};
203
+ const conversationId = asString(record.conversation_id) ?? "";
204
+ const families = asRecord(record.families) ?? {};
205
+ const out = {};
206
+ const anchors = [];
207
+ const byFamily = {};
208
+ const states = {};
209
+ for (const family of FINDINGS_FAMILIES) {
210
+ const entry = asRecord(families[family]);
211
+ if (!entry)
212
+ continue;
213
+ const rows = (Array.isArray(entry.detections) ? entry.detections : []).map((row) => asRecord(row)).filter((row) => row !== null);
214
+ const count = asNumber(entry.count) ?? rows.length;
215
+ const state = familyState(entry);
216
+ const detector = asRecord(entry.detector) ?? {};
217
+ const top = rows.slice(0, opts.limit).map((row) => compactRow(family, row));
218
+ for (const row of top) {
219
+ const msgIndex = asNumber(row.msg_index);
220
+ if (msgIndex !== null)
221
+ anchors.push({ family, msgIndex, label: String(row.family ?? row.tool ?? row.signal ?? row.rule ?? family) });
222
+ }
223
+ byFamily[family] = count;
224
+ states[family] = state;
225
+ out[family] = stripUndefined({
226
+ state,
227
+ count,
228
+ ...opts.batch ? {} : { enabled: detector.enabled ?? null, processed: entry.processed ?? null },
229
+ ...family === "hallucination" && asNumber(entry.verified_claims) ? { verified_claims: entry.verified_claims } : {},
230
+ top: opts.batch && top.length === 0 ? undefined : top,
231
+ more: count > top.length ? count - top.length : undefined
232
+ });
233
+ }
234
+ const problemsBlock = asRecord(record.problems) ?? {};
235
+ const problemRows = (Array.isArray(problemsBlock.problems) ? problemsBlock.problems : []).map((row) => asRecord(row)).filter((row) => row !== null);
236
+ const problemCount = asNumber(problemsBlock.count) ?? problemRows.length;
237
+ const problems = stripUndefined({
238
+ count: problemCount,
239
+ processed: problemsBlock.processed ?? null,
240
+ top: problemRows.slice(0, opts.limit).map((row) => stripUndefined({
241
+ problem_id: row.problem_id,
242
+ title: clip(row.title),
243
+ attribution_count: row.attribution_count,
244
+ lifecycle: row.lifecycle ?? undefined,
245
+ in_book: row.in_book
246
+ })),
247
+ more: problemCount > Math.min(problemRows.length, opts.limit) ? problemCount - Math.min(problemRows.length, opts.limit) : undefined
248
+ });
249
+ const total = Object.values(byFamily).reduce((sum, value) => sum + value, 0);
250
+ const data = opts.batch ? { detections: total, families: out, problems } : {
251
+ conversation_id: conversationId,
252
+ window: "all",
253
+ summary: { detections: total, by_family: byFamily, problems: problemCount },
254
+ families: out,
255
+ problems
256
+ };
257
+ const nextCommands = [];
258
+ const seen = new Set;
259
+ for (const anchor of anchors) {
260
+ if (seen.has(anchor.msgIndex))
261
+ continue;
262
+ seen.add(anchor.msgIndex);
263
+ nextCommands.push({
264
+ command: anchorCommand(conversationId, anchor.msgIndex),
265
+ purpose: `Read message ${anchor.msgIndex} (${anchor.family}: ${anchor.label}) with 2 messages either side.`,
266
+ mutability: "read",
267
+ requires_approval: false
268
+ });
269
+ if (nextCommands.length >= (opts.batch ? NEXT_COMMANDS_BATCH : NEXT_COMMANDS_SINGLE))
270
+ break;
271
+ }
272
+ if (!opts.batch) {
273
+ const topProblem = asString(problemRows[0]?.problem_id);
274
+ if (topProblem) {
275
+ nextCommands.push({
276
+ command: `moda problems get ${shellArg(topProblem)}`,
277
+ purpose: "Open the Problem this trace is most evidence for.",
278
+ mutability: "read",
279
+ requires_approval: false
280
+ });
281
+ }
282
+ }
283
+ const notRun = FINDINGS_FAMILIES.filter((family) => states[family] === "not_run");
284
+ const unknown = FINDINGS_FAMILIES.filter((family) => states[family] === "unknown");
285
+ const found = FINDINGS_FAMILIES.filter((family) => states[family] === "found").map((family) => `${byFamily[family]} ${family}`);
286
+ const summary = `${conversationId}: ${total} detection${total === 1 ? "" : "s"}` + (found.length > 0 ? ` (${found.join(", ")})` : "") + `, evidence for ${problemCount} Problem${problemCount === 1 ? "" : "s"}. No time window.`;
287
+ const warnings = [];
288
+ if (notRun.length > 0) {
289
+ warnings.push(`Not run on this trace (detector off or not processed yet): ${notRun.join(", ")}. No detections there does not mean clean.`);
290
+ }
291
+ if (unknown.length > 0 && total === 0) {
292
+ warnings.push(`Nothing flagged, and ${unknown.join(", ")} record nothing for a clean trace, so whether they ran is not known.`);
293
+ }
294
+ return {
295
+ data,
296
+ options: {
297
+ summary,
298
+ ...warnings.length > 0 && !opts.batch ? { warnings } : {},
299
+ ...nextCommands.length > 0 ? { nextCommands } : {}
300
+ }
301
+ };
302
+ }
303
+ function batchNotRunWarning(results) {
304
+ const notRun = results.flatMap((result) => {
305
+ const families = asRecord(asRecord(result.data)?.families);
306
+ if (!result.ok || !families)
307
+ return [];
308
+ const names = FINDINGS_FAMILIES.filter((family) => asRecord(families[family])?.state === "not_run");
309
+ return names.length > 0 ? [`${result.target} (${names.join(", ")})`] : [];
310
+ });
311
+ if (notRun.length === 0)
312
+ return [];
313
+ return [`Families not run (detector off or trace not processed yet), so no detections there does not mean clean: ${notRun.join("; ")}.`];
314
+ }
315
+
139
316
  // src/replays.ts
140
317
  var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
141
318
  var PLAYOUT_ID_RE = /^[0-9a-f]{64}$/i;
@@ -1141,6 +1318,18 @@ var CANONICAL_LEAVES = [
1141
1318
  summary: "A trace's world state (slots, threads, events)",
1142
1319
  examples: ["moda traces state <conversation_id> --summary-only", "moda traces state <conversation_id> --snapshot --msg-index=12"]
1143
1320
  },
1321
+ {
1322
+ noun: "traces",
1323
+ verb: "findings",
1324
+ path: "traces findings",
1325
+ args: "<conversation_id>...",
1326
+ handler: "trace-findings",
1327
+ summary: "Everything flagged on a trace (every detector family and its Problems), no time window; up to 25 ids per call",
1328
+ examples: [
1329
+ "moda traces findings <conversation_id>",
1330
+ "moda traces findings <id1> <id2> <id3> # batch: up to 25 ids (or --ids-from=-|<file>)"
1331
+ ]
1332
+ },
1144
1333
  {
1145
1334
  noun: "traces",
1146
1335
  verb: "spans",
@@ -1156,8 +1345,8 @@ var CANONICAL_LEAVES = [
1156
1345
  path: "problems list",
1157
1346
  args: "",
1158
1347
  handler: "problems",
1159
- summary: "Rank cross-signal Problems by root cause (what to fix first)",
1160
- examples: ["moda problems list", "moda problems list --days-back=7 --limit=10"]
1348
+ summary: "The Problems the dashboard shows, ranked (the dream grouping when published, else the Book)",
1349
+ examples: ["moda problems list", "moda problems list --sort=users --limit=20", "moda problems list --view=book --days-back=7 --offset=10 --list-version=<list_version>"]
1161
1350
  },
1162
1351
  {
1163
1352
  noun: "problems",
@@ -1165,8 +1354,8 @@ var CANONICAL_LEAVES = [
1165
1354
  path: "problems get",
1166
1355
  args: "<problem_id>",
1167
1356
  handler: "problem",
1168
- summary: "One Problem: dossier, or its --evidence/--reports/--traces/--feedback pages",
1169
- examples: ["moda problems get <problem_id>", "moda problems get <problem_id> --evidence --limit=10", "moda problems get <problem_id> --traces --family=tool_failure"]
1357
+ 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"]
1170
1359
  },
1171
1360
  {
1172
1361
  noun: "problems",
@@ -1299,7 +1488,7 @@ var CANONICAL_LEAVES = [
1299
1488
  ];
1300
1489
  var CANONICAL_NOUNS = ["traces", "problems", "detections", "tools", "review", "reviews", "skills"];
1301
1490
  var NOUN_SUMMARIES = {
1302
- traces: "Traces (agent runs): list, search, read, world state, raw spans",
1491
+ traces: "Traces (agent runs): list, search, read, findings, world state, raw spans",
1303
1492
  problems: "Problems ranked by root cause, their evidence, impact and lifecycle",
1304
1493
  detections: "Detections of every family, and the tool failure leaderboard",
1305
1494
  tools: "The Tool catalog: every tool with calls and success rate, and one tool's schema, usage and failure categories",
@@ -1410,6 +1599,10 @@ function routeDetectionsList(leaf, rest, flags) {
1410
1599
  throw inputError(`'moda detections list' takes no arguments, but got '${rest.join(" ")}'.`, DETECTIONS_LIST_USAGE, "unexpected_positional");
1411
1600
  }
1412
1601
  const label = `detections list --family=${family}`;
1602
+ const severalTraces = ["conversation-ids", "conversation_ids", "ids-from", "ids_from"].find((key) => flags[key] !== undefined);
1603
+ if (severalTraces && (family === "custom_signal" || family === "tool_failure")) {
1604
+ throw inputError(`--${severalTraces.replace(/_/g, "-")} is not available with --family=${family}. Read several traces' ${family === "custom_signal" ? "custom signal hits" : "tool failures"} with 'moda traces findings <id> <id> ...' (up to 25 ids).`, DETECTIONS_LIST_USAGE, `inapplicable_flag:${severalTraces.replace(/_/g, "-")}`, { field: `--${severalTraces.replace(/_/g, "-")}`, suggestedCommands: ["moda traces findings --help"] });
1605
+ }
1413
1606
  if (family === "custom_signal") {
1414
1607
  const signalId = flags["signal-id"];
1415
1608
  const traceId = flags["conversation-id"];
@@ -2982,46 +3175,46 @@ function leafManifestFields(leaf, commands) {
2982
3175
  import { fileURLToPath } from "url";
2983
3176
  import { realpathSync } from "fs";
2984
3177
  async function runPromptsCommand(...args) {
2985
- return (await import("./prompts-cwqn8a57.js")).runPromptsCommand(...args);
3178
+ return (await import("./prompts-ysm9ra72.js")).runPromptsCommand(...args);
2986
3179
  }
2987
3180
  async function runSkillsCommand(...args) {
2988
- return (await import("./skills-btn9r48v.js")).runSkillsCommand(...args);
3181
+ return (await import("./skills-x6yszxxn.js")).runSkillsCommand(...args);
2989
3182
  }
2990
3183
  async function runFixCommand(...args) {
2991
- return (await import("./fix-kvszavm1.js")).runFixCommand(...args);
3184
+ return (await import("./fix-0r974yge.js")).runFixCommand(...args);
2992
3185
  }
2993
3186
  async function runFixesCommand(...args) {
2994
- return (await import("./fix-kvszavm1.js")).runFixesCommand(...args);
3187
+ return (await import("./fix-0r974yge.js")).runFixesCommand(...args);
2995
3188
  }
2996
3189
  async function runHarnessCommand(...args) {
2997
- return (await import("./harness-qatnwxrz.js")).runHarnessCommand(...args);
3190
+ return (await import("./harness-72ejenza.js")).runHarnessCommand(...args);
2998
3191
  }
2999
3192
  async function runSyncCommand(...args) {
3000
- return (await import("./sync-ye7nxeen.js")).runSyncCommand(...args);
3193
+ return (await import("./sync-trjzvam9.js")).runSyncCommand(...args);
3001
3194
  }
3002
3195
  async function runRegistryCommand(...args) {
3003
- return (await import("./registry-76sngjw6.js")).runRegistryCommand(...args);
3196
+ return (await import("./registry-7xz09ry0.js")).runRegistryCommand(...args);
3004
3197
  }
3005
3198
  async function runDoctorCommand(...args) {
3006
- return (await import("./doctor-xq5bjvpq.js")).runDoctorCommand(...args);
3199
+ return (await import("./doctor-p8sp6cbk.js")).runDoctorCommand(...args);
3007
3200
  }
3008
3201
  async function runStatusCommand(...args) {
3009
- return (await import("./doctor-xq5bjvpq.js")).runStatusCommand(...args);
3202
+ return (await import("./doctor-p8sp6cbk.js")).runStatusCommand(...args);
3010
3203
  }
3011
3204
  async function runAuthCommand(...args) {
3012
- return (await import("./profile-commands-skdxe17v.js")).runAuthCommand(...args);
3205
+ return (await import("./profile-commands-se9947c1.js")).runAuthCommand(...args);
3013
3206
  }
3014
3207
  async function runConfigCommand(...args) {
3015
- return (await import("./profile-commands-skdxe17v.js")).runConfigCommand(...args);
3208
+ return (await import("./profile-commands-se9947c1.js")).runConfigCommand(...args);
3016
3209
  }
3017
3210
  async function runProfilesCommand(...args) {
3018
- return (await import("./profile-commands-skdxe17v.js")).runProfilesCommand(...args);
3211
+ return (await import("./profile-commands-se9947c1.js")).runProfilesCommand(...args);
3019
3212
  }
3020
3213
  async function runAgentCommand(...args) {
3021
- return (await import("./agent-9r58v0bc.js")).runAgentCommand(...args);
3214
+ return (await import("./agent-ck6y31n2.js")).runAgentCommand(...args);
3022
3215
  }
3023
3216
  async function runProductionCommand(...args) {
3024
- return (await import("./production-intelligence-j55vaw6t.js")).runProductionCommand(...args);
3217
+ return (await import("./production-intelligence-hs2sm8t5.js")).runProductionCommand(...args);
3025
3218
  }
3026
3219
  function parseArgs(argv) {
3027
3220
  const args = argv.slice(2);
@@ -4146,7 +4339,7 @@ var WINDOW_FLAG_HINTS = {
4146
4339
  var COMMAND_FLAG_HINTS = {
4147
4340
  problems: {
4148
4341
  family: "'moda problems' ranks every family together. To filter by family use 'moda problems unexplained --family=<family>' (signals no Problem explains yet) or 'moda problems get <problem_id> --traces --family=<family>' (one Problem's traces).",
4149
- cursor: "'moda problems' has no cursor; 'moda problems unexplained --cursor=<next_cursor>' pages the unexplained signals."
4342
+ cursor: "'moda problems list' pages with --offset=<next_offset> --list-version=<list_version>; 'moda problems unexplained --cursor=<next_cursor>' pages the unexplained signals."
4150
4343
  }
4151
4344
  };
4152
4345
  function parseCommandFlags(schema, args, command, options = {}) {
@@ -4447,7 +4640,7 @@ function rejectInapplicableFlags(command, params, keys, why) {
4447
4640
  const shown = canonicalCommandText(`moda ${command}`);
4448
4641
  throw new CliInputError(`${names.join(", ")} ${given.length > 1 ? "do" : "does"} not apply ${why}, so nothing was sent.`, `Drop ${names.join(", ")}, or pick the mode that uses ${given.length > 1 ? "them" : "it"}. See '${shown} --help'.`, `inapplicable_flag:${names[0].slice(2)}`, { field: names[0], suggestedCommands: [`${shown} --help`] });
4449
4642
  }
4450
- var BATCH_POSITIONAL_COMMANDS = new Set(["context", "audit", "trace"]);
4643
+ var BATCH_POSITIONAL_COMMANDS = new Set(["context", "audit", "trace", "trace-findings"]);
4451
4644
  function assertPositionalArity(command, positionals) {
4452
4645
  const spec = flagSpecFor(command);
4453
4646
  const expected = spec?.positionals ?? [];
@@ -4521,6 +4714,260 @@ async function runProblemsRemainder(flags, context) {
4521
4714
  const queryString = query2.toString() ? `?${query2.toString()}` : "";
4522
4715
  context.output.writeData(await callDataAPI(`/problems/remainder${queryString}`));
4523
4716
  }
4717
+ var isDreamOnlySort = (sort) => sort === "severity" || sort === "silent";
4718
+ var isBookOnlySort = (sort) => sort === "signals" || sort === "problems" || (sort?.startsWith("signal:") ?? false);
4719
+ function problemsListCommand(params, view, page, daysGiven) {
4720
+ const parts = ["moda problems list", `--view=${view}`];
4721
+ if (daysGiven && params.days_back !== undefined)
4722
+ parts.push(`--days-back=${params.days_back}`);
4723
+ parts.push(`--limit=${params.limit ?? PROBLEMS_DEFAULT_LIMIT}`);
4724
+ if (params.sort)
4725
+ parts.push(`--sort=${shellArg(params.sort)}`);
4726
+ if (params.dir)
4727
+ parts.push(`--dir=${params.dir}`);
4728
+ if (params.cohort)
4729
+ parts.push(`--cohort=${shellArg(params.cohort)}`);
4730
+ parts.push(`--offset=${page.offset}`, `--list-version=${page.listVersion}`);
4731
+ return parts.join(" ");
4732
+ }
4733
+ function listVersionWarning(params, served) {
4734
+ if (!params.list_version || typeof served !== "string" || served === params.list_version)
4735
+ return [];
4736
+ return [
4737
+ `The list changed since --list-version=${params.list_version} was read (this page is list_version=${served}). Offsets of the two lists do not line up: restart from the first page (drop --offset and --list-version).`
4738
+ ];
4739
+ }
4740
+ function hierarchyForPage(payload) {
4741
+ const hierarchy = asRecord(payload.hierarchy);
4742
+ if (!hierarchy || !Array.isArray(hierarchy.macros))
4743
+ return payload;
4744
+ const shown = new Set([...Array.isArray(payload.problems) ? payload.problems : [], ...Array.isArray(payload.untrusted) ? payload.untrusted : []].map((row) => asRecord(row)?.problem_id).filter((id) => typeof id === "string"));
4745
+ const macros = hierarchy.macros.map((macro) => {
4746
+ const record = asRecord(macro);
4747
+ if (!record || !Array.isArray(record.children))
4748
+ return macro;
4749
+ return { ...record, children: record.children.filter((id) => typeof id === "string" && shown.has(id)) };
4750
+ });
4751
+ return { ...payload, hierarchy: { ...hierarchy, macros, children_listed: "this page only; child_count is the full size" } };
4752
+ }
4753
+ async function runProblemsList(flags, context, command) {
4754
+ const args = flagsToArgs(flags);
4755
+ const params = parseSpecFlags("problems", args, command);
4756
+ const view = params.view ?? "auto";
4757
+ const daysGiven = args.days_back !== undefined;
4758
+ const limit = params.limit ?? PROBLEMS_DEFAULT_LIMIT;
4759
+ const offset = params.offset ?? 0;
4760
+ if (view === "book" && isDreamOnlySort(params.sort)) {
4761
+ throw new CliInputError(`--sort=${params.sort} orders the dream view; the Book sorts by ${BOOK_PROBLEM_SORTS.join(", ")} or signal:<family>.`);
4762
+ }
4763
+ if (view === "dream" && isBookOnlySort(params.sort)) {
4764
+ throw new CliInputError(`--sort=${params.sort} orders the Book; the dream view sorts by rank, ${DREAM_PROBLEM_SORTS.join(", ")}.`);
4765
+ }
4766
+ if (params.list_version && params.offset === undefined) {
4767
+ throw new CliInputError("--list-version pages a list: pass it with the --offset of the next page (next_offset).");
4768
+ }
4769
+ if (view !== "book") {
4770
+ const query3 = new URLSearchParams;
4771
+ if (daysGiven && params.days_back !== undefined)
4772
+ query3.set("days_back", String(params.days_back));
4773
+ if (params.cohort)
4774
+ query3.set("cohort", params.cohort);
4775
+ const bookOnly = isBookOnlySort(params.sort);
4776
+ query3.set("limit", String(bookOnly ? 1 : limit));
4777
+ if (!bookOnly) {
4778
+ if (offset > 0)
4779
+ query3.set("offset", String(offset));
4780
+ if (params.list_version)
4781
+ query3.set("list_version", params.list_version);
4782
+ if (params.sort && params.sort !== "rank")
4783
+ query3.set("sort", params.sort);
4784
+ if (params.dir)
4785
+ query3.set("dir", params.dir);
4786
+ }
4787
+ const dream = asRecord(await callDataAPI(`/problems/dream?${query3.toString()}`)) ?? {};
4788
+ if (dream.available === true) {
4789
+ if (bookOnly) {
4790
+ throw new CliInputError(`This tenant's dashboard shows the dream grouping of its Problems, which --sort=${params.sort} does not order.`, `Use --sort=rank|${DREAM_PROBLEM_SORTS.join("|")} for the dream view, or --view=book --sort=${params.sort} for the Book's ranked list.`);
4791
+ }
4792
+ writeDreamProblemsList(context, params, dream, { offset, limit, daysGiven });
4793
+ return;
4794
+ }
4795
+ if (view === "dream") {
4796
+ context.output.writeData({ available: false, view: "dream", dashboard_url: `${MODA_BASE_URL}/dashboard/problems` }, {
4797
+ warnings: ["This tenant has no dream grouping published; the dashboard shows the Book. List it with: moda problems list --view=book"],
4798
+ nextCommands: [{ command: "moda problems list --view=book", purpose: "List the Book's ranked Problems (what the dashboard shows for this tenant).", mutability: "read", requires_approval: false }]
4799
+ });
4800
+ return;
4801
+ }
4802
+ }
4803
+ if (isDreamOnlySort(params.sort)) {
4804
+ throw new CliInputError(`This tenant has no dream grouping published, so its Problems are the Book's ranked list, which --sort=${params.sort} does not order.`, `Use --sort=${BOOK_PROBLEM_SORTS.join("|")} or signal:<family>.`);
4805
+ }
4806
+ const query2 = new URLSearchParams;
4807
+ query2.set("days_back", String(params.days_back ?? 30));
4808
+ query2.set("limit", String(limit));
4809
+ if (offset > 0)
4810
+ query2.set("offset", String(offset));
4811
+ if (params.list_version)
4812
+ query2.set("list_version", params.list_version);
4813
+ if (params.sort)
4814
+ query2.set("sort", params.sort);
4815
+ if (params.dir)
4816
+ query2.set("dir", params.dir);
4817
+ if (params.cohort)
4818
+ query2.set("cohort", params.cohort);
4819
+ const data = await callDataAPI(`/problems?${query2.toString()}`);
4820
+ const { payload: capped, truncated } = capProblemsBlocks(asRecord(data) ?? {}, limit);
4821
+ const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
4822
+ const payload = agentMode ? hierarchyForPage(capped) : capped;
4823
+ const problems = Array.isArray(payload.problems) ? payload.problems : [];
4824
+ const untrusted = Array.isArray(payload.untrusted) ? payload.untrusted : [];
4825
+ const nextOffset = typeof payload.next_offset === "number" ? payload.next_offset : null;
4826
+ const listVersion = typeof payload.list_version === "string" ? payload.list_version : undefined;
4827
+ const withLink = {
4828
+ view: "book",
4829
+ ...payload,
4830
+ pagination: {
4831
+ offset,
4832
+ limit,
4833
+ returned: problems.length,
4834
+ untrusted_returned: untrusted.length,
4835
+ has_more: nextOffset !== null,
4836
+ next_offset: nextOffset,
4837
+ ...listVersion ? { list_version: listVersion } : {}
4838
+ },
4839
+ ...truncated ? { truncated } : {},
4840
+ dashboard_url: `${MODA_BASE_URL}/dashboard/problems`
4841
+ };
4842
+ const nextCommands = [];
4843
+ if (nextOffset !== null && listVersion) {
4844
+ nextCommands.push({
4845
+ command: problemsListCommand(params, "book", { offset: nextOffset, listVersion }, daysGiven),
4846
+ purpose: `Read the next page of the Book (problems and untrusted from row ${nextOffset}).`,
4847
+ mutability: "read",
4848
+ requires_approval: false,
4849
+ nextPage: true
4850
+ });
4851
+ }
4852
+ const top = asRecord(problems[0]) ?? asRecord(untrusted[0]);
4853
+ if (typeof top?.problem_id === "string") {
4854
+ nextCommands.push({ command: `moda problems get ${shellArg(top.problem_id)}`, purpose: "Open the first Problem on this page.", mutability: "read", requires_approval: false });
4855
+ }
4856
+ const warnings = [
4857
+ ...serverWarnings(payload),
4858
+ ...listVersionWarning(params, listVersion),
4859
+ ...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
+ ];
4861
+ context.output.writeData(withLink, {
4862
+ ...warnings.length > 0 ? { warnings } : {},
4863
+ ...nextCommands.length > 0 ? { nextCommands } : {}
4864
+ });
4865
+ }
4866
+ function writeDreamProblemsList(context, params, dream, page) {
4867
+ const problems = Array.isArray(dream.problems) ? dream.problems : [];
4868
+ const nextOffset = typeof dream.next_offset === "number" ? dream.next_offset : null;
4869
+ const listVersion = typeof dream.list_version === "string" ? dream.list_version : undefined;
4870
+ const run = asRecord(dream.run);
4871
+ const { offset: _offset, limit: _limit, next_offset: _next, list_version: _version, ...rest } = dream;
4872
+ const data = {
4873
+ ...rest,
4874
+ pagination: {
4875
+ offset: page.offset,
4876
+ limit: page.limit,
4877
+ returned: problems.length,
4878
+ total: asNumber(asRecord(dream.summary)?.problems) ?? null,
4879
+ has_more: nextOffset !== null,
4880
+ next_offset: nextOffset,
4881
+ ...listVersion ? { list_version: listVersion } : {}
4882
+ },
4883
+ dashboard_url: `${MODA_BASE_URL}/dashboard/problems`
4884
+ };
4885
+ const nextCommands = [];
4886
+ if (nextOffset !== null && listVersion) {
4887
+ nextCommands.push({
4888
+ command: problemsListCommand(params, "dream", { offset: nextOffset, listVersion }, page.daysGiven),
4889
+ purpose: `Read the next page of the dream grouping (from row ${nextOffset}).`,
4890
+ mutability: "read",
4891
+ requires_approval: false,
4892
+ nextPage: true
4893
+ });
4894
+ }
4895
+ const top = asRecord(problems[0]);
4896
+ if (typeof top?.problem_id === "string") {
4897
+ const id = shellArg(top.problem_id);
4898
+ nextCommands.push({ command: `moda problems get ${id} --view=dream`, purpose: "Open the first Problem as the dashboard shows it: definition, severity and its most serious incidents.", mutability: "read", requires_approval: false }, { command: `moda problems get ${id} --evidence`, purpose: "Page the evidence behind it (dream problem ids are Book ids).", mutability: "read", requires_approval: false });
4899
+ }
4900
+ const windowHours = asNumber(run?.window_hours);
4901
+ const warnings = [
4902
+ "This tenant's dashboard shows the dream grouping of its Problems, so this is that list, in the dashboard table's order. The Book's ranked list is --view=book.",
4903
+ ...!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
+ ...listVersionWarning(params, listVersion)
4905
+ ];
4906
+ context.output.writeData(data, { warnings, ...nextCommands.length > 0 ? { nextCommands } : {} });
4907
+ }
4908
+ var DREAM_INCIDENTS_DEFAULT_LIMIT = 3;
4909
+ async function runDreamProblem(params, context, command) {
4910
+ const others = ["evidence", "reports", "traces", "conversations", "feedback", "full"].filter((key) => params[key] === true);
4911
+ if (others.length > 0) {
4912
+ throw new CliInputError(`--view=dream is its own page; it does not combine with ${others.map((key) => `--${key === "conversations" ? "traces" : key}`).join(", ")}.`, `Usage: moda problems get <problem_id> --view=dream [--limit=N] [--offset=N] [--days-back=N], or drop --view=dream for the dossier and its pages.`);
4913
+ }
4914
+ rejectInapplicableFlags(command, params, ["cursor", "family", "door"], "to --view=dream; it pages incidents with --limit and --offset");
4915
+ if (!UUID_RE.test(params.id)) {
4916
+ throw new CliInputError(`--view=dream requires a canonical problem UUID; got "${params.id}". Run \`moda problems list\` to list problem ids.`);
4917
+ }
4918
+ const limit = params.limit ?? DREAM_INCIDENTS_DEFAULT_LIMIT;
4919
+ const offset = params.offset ?? 0;
4920
+ const query2 = new URLSearchParams({ limit: String(limit) });
4921
+ if (offset > 0)
4922
+ query2.set("offset", String(offset));
4923
+ if (params.days_back !== undefined)
4924
+ query2.set("days_back", String(params.days_back));
4925
+ const id = shellArg(params.id);
4926
+ const data = asRecord(await callDataAPI(`/problems/dream/${encodeURIComponent(params.id)}?${query2.toString()}`).catch((error) => {
4927
+ if (error instanceof ApiError && error.statusCode === 404) {
4928
+ throwIfNotFound("problem", params.id, true, `Problem "${params.id}" is not in this tenant's dream grouping; read its dossier with: moda problems get ${id}`);
4929
+ }
4930
+ throw error;
4931
+ })) ?? {};
4932
+ if (data.available !== true) {
4933
+ context.output.writeData({ available: false, view: "dream", problem_id: params.id }, {
4934
+ warnings: [`This tenant has no dream grouping published; read the problem's dossier with: moda problems get ${id}`],
4935
+ nextCommands: [{ command: `moda problems get ${id}`, purpose: "Read the dossier (what the dashboard shows for this tenant).", mutability: "read", requires_approval: false }]
4936
+ });
4937
+ return;
4938
+ }
4939
+ const incidents = Array.isArray(data.incidents) ? data.incidents : [];
4940
+ const nextOffset = typeof data.next_offset === "number" ? data.next_offset : null;
4941
+ const { offset: _offset, limit: _limit, next_offset: _next, ...rest } = data;
4942
+ const nextCommands = [];
4943
+ if (nextOffset !== null) {
4944
+ const days = params.days_back !== undefined ? ` --days-back=${params.days_back}` : "";
4945
+ nextCommands.push({
4946
+ command: `moda problems get ${id} --view=dream --limit=${limit} --offset=${nextOffset}${days}`,
4947
+ purpose: `Read the next incidents of this problem (from incident ${nextOffset}).`,
4948
+ mutability: "read",
4949
+ requires_approval: false,
4950
+ nextPage: true
4951
+ });
4952
+ }
4953
+ const first = asRecord(incidents[0]);
4954
+ if (typeof first?.conversation_id === "string") {
4955
+ nextCommands.push({ command: `moda traces get ${shellArg(first.conversation_id)}`, purpose: "Open the trace of its most serious incident.", mutability: "read", requires_approval: false });
4956
+ }
4957
+ nextCommands.push({ command: `moda problems get ${id} --evidence`, purpose: "Page the Book evidence behind the same problem (dream ids are Book ids).", mutability: "read", requires_approval: false });
4958
+ context.output.writeData({
4959
+ ...rest,
4960
+ pagination: {
4961
+ offset,
4962
+ limit,
4963
+ returned: incidents.length,
4964
+ total: asNumber(data.incidents_total) ?? null,
4965
+ has_more: nextOffset !== null,
4966
+ next_offset: nextOffset
4967
+ },
4968
+ dashboard_url: `${MODA_BASE_URL}/dashboard/problems/${encodeURIComponent(params.id)}`
4969
+ }, { nextCommands });
4970
+ }
4524
4971
  function processingDisabledWarning(data, family) {
4525
4972
  const processing = asRecord(asRecord(data)?.processing);
4526
4973
  const label = family === "emotion" ? "Emotion" : "Frustration";
@@ -5152,6 +5599,79 @@ async function runContextBatch(command, positionals, flags, context) {
5152
5599
  });
5153
5600
  return writeBatchResult(command, context, targets, results, inputWarnings, rest);
5154
5601
  }
5602
+ var CONVERSATION_IDS_USAGE = `Usage: moda detections list --family=emotion|frustration|laziness|hallucination --conversation-ids=<id>,<id>,... | --ids-from=-|<file> (up to ${MAX_CONVERSATION_IDS_FILTER} ids)`;
5603
+ async function conversationIdsFilter(params, context) {
5604
+ if (params.conversation_ids === undefined && params.ids_from === undefined)
5605
+ return;
5606
+ if (params.conversation_ids !== undefined && params.ids_from !== undefined) {
5607
+ throw new CliInputError("Pass --conversation-ids or --ids-from, not both.", CONVERSATION_IDS_USAGE, "flag_conflict");
5608
+ }
5609
+ if (params.conversation_id !== undefined) {
5610
+ throw new CliInputError("--conversation-id reads one trace and --conversation-ids/--ids-from several; pass one of them.", CONVERSATION_IDS_USAGE, "flag_conflict");
5611
+ }
5612
+ let ids;
5613
+ if (params.ids_from !== undefined) {
5614
+ ids = parseTargetLines(await readIdsFrom(params.ids_from, CONVERSATION_IDS_USAGE), false, CONVERSATION_IDS_USAGE).map((target) => target.id);
5615
+ } else {
5616
+ const text = params.conversation_ids.trim();
5617
+ let parsed = undefined;
5618
+ if (text.startsWith("[")) {
5619
+ try {
5620
+ parsed = JSON.parse(text);
5621
+ } catch {
5622
+ throw new CliInputError("--conversation-ids starts with [ but is not a JSON array of ids.", CONVERSATION_IDS_USAGE, "bad_value:conversation-ids");
5623
+ }
5624
+ }
5625
+ ids = Array.isArray(parsed) ? parsed.map((id) => String(id).trim()) : text.split(",").map((id) => id.trim());
5626
+ }
5627
+ const unique = [...new Set(ids.filter((id) => id !== ""))];
5628
+ if (unique.length === 0) {
5629
+ throw new CliInputError("No trace ids: --conversation-ids/--ids-from gave none.", CONVERSATION_IDS_USAGE, "missing_value:conversation-ids");
5630
+ }
5631
+ if (unique.length > MAX_CONVERSATION_IDS_FILTER) {
5632
+ throw new CliInputError(`Got ${unique.length} trace ids; the detection lists take at most ${MAX_CONVERSATION_IDS_FILTER} per call, so nothing was read. Split them into calls of ${MAX_CONVERSATION_IDS_FILTER} or fewer.`, CONVERSATION_IDS_USAGE, "batch_too_large");
5633
+ }
5634
+ const withComma = unique.find((id) => id.includes(","));
5635
+ if (withComma !== undefined) {
5636
+ throw new CliInputError(`Trace id ${JSON.stringify(withComma)} has a comma, which the several-trace filter cannot carry. Read it with --conversation-id instead.`, CONVERSATION_IDS_USAGE, "bad_value:conversation-ids");
5637
+ }
5638
+ if (params.ids_from !== undefined && context.invocation) {
5639
+ for (const key of IDS_FROM_KEYS)
5640
+ delete context.invocation.flags[key];
5641
+ context.invocation.flags["conversation-ids"] = unique.join(",");
5642
+ }
5643
+ return unique;
5644
+ }
5645
+ var TRACE_FINDINGS_USAGE = `Usage: moda traces findings <conversation_id> [<conversation_id> ...] | --ids-from=-|<file> [--limit=N] (max ${BATCH_MAX_TARGETS} ids)`;
5646
+ async function runTraceFindings(command, positionals, flags, context) {
5647
+ const { idsFrom, rest } = splitIdsFrom(flags);
5648
+ if (positionals.length === 0 && idsFrom === undefined) {
5649
+ throw new CliInputError("<conversation_id> is required", TRACE_FINDINGS_USAGE, "missing_positional");
5650
+ }
5651
+ const targets = await collectBatchTargets(positionals, idsFrom, { anchors: false, usage: TRACE_FINDINGS_USAGE, command });
5652
+ const base = parseCommandFlags(TraceFindingsSchema, flagsToArgs(rest, "conversation_id", targets[0].id), command, { positionalKey: "conversation_id" });
5653
+ const limit = base.limit ?? FINDINGS_DEFAULT_LIMIT;
5654
+ const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
5655
+ const full = context.output.mode === "json";
5656
+ const batch = isBatchRead(positionals, flags);
5657
+ const read = async (id, budget) => {
5658
+ const raw = await callDataAPI(`/conversations/${encodeURIComponent(id)}/findings`);
5659
+ if (full)
5660
+ return { data: raw, options: {} };
5661
+ let item = compactFindings(raw, { limit, batch });
5662
+ for (let shown = limit - 1;budget && shown >= 0 && !budget.fits(item.data, item.options); shown -= 1) {
5663
+ item = compactFindings(raw, { limit: shown, batch });
5664
+ }
5665
+ return item;
5666
+ };
5667
+ if (!batch) {
5668
+ const item = await read(targets[0].id, agentMode ? { fits: (data, options) => agentEnvelopeFits(command, data, options), maxBytes: agentMaxOutputBytes() } : undefined);
5669
+ context.output.writeData(item.data, item.options);
5670
+ return 0;
5671
+ }
5672
+ const results = await readBatchTargets(targets, agentMode, (target, budget) => read(target.id, budget));
5673
+ return writeBatchResult("traces findings", context, targets, results, full ? [] : batchNotRunWarning(results), rest);
5674
+ }
5155
5675
  async function runAuditBatch(command, positionals, flags, context) {
5156
5676
  const { idsFrom, rest } = splitIdsFrom(flags);
5157
5677
  if (rest.cursor !== undefined || Object.keys(rest).some((key) => key.replace(/_/g, "-") === "span-offset")) {
@@ -5423,6 +5943,8 @@ async function runCommand(command, positional, flags, positionals = positional ?
5423
5943
  context.output.writeData(item.data, item.options);
5424
5944
  break;
5425
5945
  }
5946
+ case "trace-findings":
5947
+ return runTraceFindings(command, positionals, flags, context);
5426
5948
  case "audit":
5427
5949
  case "trace": {
5428
5950
  if (isBatchRead(positionals, flags))
@@ -5447,7 +5969,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
5447
5969
  query2.set("limit", params.limit.toString());
5448
5970
  if (params.offset !== undefined)
5449
5971
  query2.set("offset", params.offset.toString());
5450
- if (readsPerTrace("frustrations", params, ["user_id", "conversation_id", "include_window"])) {
5972
+ if (readsPerTrace("frustrations", params, ["user_id", "conversation_id", "conversation_ids", "ids_from", "include_window"])) {
5451
5973
  query2.set("family", "frustration");
5452
5974
  setTraceFilters(query2, params, TRACE_GRAIN_FLAGS.frustrations);
5453
5975
  const data2 = await callDataAPI(`/emotions?${query2.toString()}`);
@@ -5460,6 +5982,9 @@ async function runCommand(command, positional, flags, positionals = positional ?
5460
5982
  query2.set("user_id", params.user_id);
5461
5983
  if (params.conversation_id)
5462
5984
  query2.set("conversation_id", params.conversation_id);
5985
+ const frustrationIds = await conversationIdsFilter(params, context);
5986
+ if (frustrationIds)
5987
+ query2.set("conversation_ids", frustrationIds.join(","));
5463
5988
  query2.set("family", "frustration");
5464
5989
  const includeWindow = params.include_window === true;
5465
5990
  const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
@@ -5665,25 +6190,9 @@ async function runCommand(command, positional, flags, positionals = positional ?
5665
6190
  break;
5666
6191
  }
5667
6192
  if (positional) {
5668
- throw new CliInputError(`Unknown problems subcommand '${positional}'.`, "Usage: moda problems [--days-back=N] [--limit=N] | moda problems unexplained [--family=F] [--days-back=N] [--limit=N] [--cursor=TOKEN]. For one Problem use 'moda problems get <id>'.");
6193
+ throw new CliInputError(`Unknown problems subcommand '${positional}'.`, "Usage: moda problems list [--view=auto|book|dream] [--days-back=N] [--limit=N] [--offset=N --list-version=V] [--sort=S] [--cohort=REF] | moda problems unexplained [--family=F] [--days-back=N] [--limit=N] [--cursor=TOKEN]. For one Problem use 'moda problems get <id>'.");
5669
6194
  }
5670
- const args = flagsToArgs(flags);
5671
- const params = parseSpecFlags("problems", args, command);
5672
- const query2 = new URLSearchParams;
5673
- if (params.days_back)
5674
- query2.set("days_back", params.days_back.toString());
5675
- if (params.limit)
5676
- query2.set("limit", params.limit.toString());
5677
- const queryString = query2.toString() ? `?${query2.toString()}` : "";
5678
- const data = await callDataAPI(`/problems${queryString}`);
5679
- const { payload, truncated } = capProblemsBlocks(asRecord(data) ?? {}, params.limit ?? 25);
5680
- const withLink = {
5681
- ...payload,
5682
- ...truncated ? { truncated } : {},
5683
- dashboard_url: `${MODA_BASE_URL}/dashboard/problems`
5684
- };
5685
- const warnings = [...serverWarnings(payload), ...truncated ? [`--limit=${params.limit ?? 25} also capped ${Object.entries(truncated).map(([block, count]) => `${block} (${count} more)`).join(", ")}; raise --limit (max 25) or open dashboard_url for the rest.`] : []];
5686
- context.output.writeData(withLink, warnings.length > 0 ? { warnings } : undefined);
6195
+ await runProblemsList(flags, context, command);
5687
6196
  break;
5688
6197
  }
5689
6198
  case "problem": {
@@ -5696,6 +6205,11 @@ async function runCommand(command, positional, flags, positionals = positional ?
5696
6205
  }
5697
6206
  const args = flagsToArgs(flags, "id", positional);
5698
6207
  const params = parseSpecFlags("problem", args, command);
6208
+ if (params.view === "dream") {
6209
+ await runDreamProblem(params, context, command);
6210
+ break;
6211
+ }
6212
+ rejectInapplicableFlags(command, params, ["offset", "days_back"], "without --view=dream; the dossier pages page with --limit and --cursor");
5699
6213
  const traceView = params.traces === true || params.conversations === true;
5700
6214
  const views = ["evidence", "reports", "conversations", "feedback"].filter((view2) => view2 === "conversations" ? traceView : params[view2] === true);
5701
6215
  if (views.length > 1) {
@@ -6131,7 +6645,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
6131
6645
  query2.set("offset", params.offset.toString());
6132
6646
  if (params.family)
6133
6647
  query2.set("family", params.family);
6134
- if (readsPerTrace("emotions", params, ["user_id", "conversation_id", "include_window"])) {
6648
+ if (readsPerTrace("emotions", params, ["user_id", "conversation_id", "conversation_ids", "ids_from", "include_window"])) {
6135
6649
  setTraceFilters(query2, params, TRACE_GRAIN_FLAGS.emotions);
6136
6650
  const data2 = await callDataAPI(`/emotions?${query2.toString()}`);
6137
6651
  const warnings = processingDisabledWarning(data2, "emotion");
@@ -6143,6 +6657,9 @@ async function runCommand(command, positional, flags, positionals = positional ?
6143
6657
  query2.set("user_id", params.user_id);
6144
6658
  if (params.conversation_id)
6145
6659
  query2.set("conversation_id", params.conversation_id);
6660
+ const emotionIds = await conversationIdsFilter(params, context);
6661
+ if (emotionIds)
6662
+ query2.set("conversation_ids", emotionIds.join(","));
6146
6663
  const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
6147
6664
  if (agentMode && params.include_window !== true)
6148
6665
  query2.set("include_context", "0");
@@ -6167,6 +6684,9 @@ async function runCommand(command, positional, flags, positionals = positional ?
6167
6684
  query2.set("offset", params.offset.toString());
6168
6685
  if (params.conversation_id)
6169
6686
  query2.set("conversation_id", params.conversation_id);
6687
+ const lazinessIds = await conversationIdsFilter(params, context);
6688
+ if (lazinessIds)
6689
+ query2.set("conversation_ids", lazinessIds.join(","));
6170
6690
  if (params.family)
6171
6691
  query2.set("family", params.family);
6172
6692
  if (params.conversation_id) {
@@ -6196,11 +6716,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
6196
6716
  query2.set("limit", params.limit.toString());
6197
6717
  if (params.offset !== undefined)
6198
6718
  query2.set("offset", params.offset.toString());
6199
- if (readsPerTrace("hallucinations", params, ["conversation_id", "kind"])) {
6719
+ if (readsPerTrace("hallucinations", params, ["conversation_id", "conversation_ids", "ids_from", "kind"])) {
6200
6720
  setTraceFilters(query2, params, TRACE_GRAIN_FLAGS.hallucinations);
6201
6721
  } else {
6202
6722
  if (params.conversation_id)
6203
6723
  query2.set("conversation_id", params.conversation_id);
6724
+ const hallucinationIds = await conversationIdsFilter(params, context);
6725
+ if (hallucinationIds)
6726
+ query2.set("conversation_ids", hallucinationIds.join(","));
6204
6727
  if (params.kind)
6205
6728
  query2.set("kind", params.kind);
6206
6729
  }
@@ -6396,7 +6919,7 @@ var commandRegistry = createCommandRegistry([
6396
6919
  resetApiRequestCountBeforeRun: true,
6397
6920
  telemetry: "result-and-error",
6398
6921
  handler: async (context) => {
6399
- const { runInit } = await import("./index-y5jnz2t2.js");
6922
+ const { runInit } = await import("./index-w7v472w4.js");
6400
6923
  if (context.outputMode === "agent-stream") {
6401
6924
  context.output.writeEvent({
6402
6925
  event: "started",
@@ -6446,7 +6969,7 @@ var commandRegistry = createCommandRegistry([
6446
6969
  resetApiRequestCountBeforeRun: true,
6447
6970
  telemetry: "result-and-error",
6448
6971
  handler: async (context) => {
6449
- const { runProvision } = await import("./provision-ghca9d54.js");
6972
+ const { runProvision } = await import("./provision-cya5rpxa.js");
6450
6973
  return runProvision(context, {
6451
6974
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
6452
6975
  });
@@ -6934,6 +7457,17 @@ var commandRegistry = createCommandRegistry([
6934
7457
  ],
6935
7458
  ...dataApiDefaults
6936
7459
  }),
7460
+ legacyCommand({
7461
+ name: "trace-findings",
7462
+ description: "Everything flagged on a trace: every detector family with its message anchors, and the Problems it is evidence for (no time window)",
7463
+ examples: [
7464
+ "moda traces findings <conversation_id>",
7465
+ "moda traces findings <conversation_id> --limit=10 # more rows per family (counts always cover all)",
7466
+ "moda traces findings <id1> <id2> <id3> # batch: up to 25 ids, one envelope (or --ids-from=-|<file>)"
7467
+ ],
7468
+ hidden: true,
7469
+ ...dataApiDefaults
7470
+ }),
6937
7471
  legacyCommand({
6938
7472
  name: "frustrations",
6939
7473
  description: "User frustration detections: the frustration family of `emotions` (same data as the dashboard), legacy field names",
@@ -6962,7 +7496,7 @@ var commandRegistry = createCommandRegistry([
6962
7496
  legacyCommand({
6963
7497
  name: "problems",
6964
7498
  description: "Rank cross-signal Problems by root cause (what to fix first)",
6965
- examples: ["moda problems list", "moda problems list --days-back=7 --limit=10", "moda problems get <problem_id> --evidence", "moda problems unexplained --family=emotion"],
7499
+ examples: ["moda problems list", "moda problems list --view=book --sort=users --limit=20", "moda problems get <problem_id> --view=dream", "moda problems get <problem_id> --evidence", "moda problems unexplained --family=emotion"],
6966
7500
  subcommands: treeSubcommandHelp("problems"),
6967
7501
  defaultSubcommand: "list",
6968
7502
  ...dataApiDefaults
@@ -6984,14 +7518,15 @@ var commandRegistry = createCommandRegistry([
6984
7518
  }),
6985
7519
  legacyCommand({
6986
7520
  name: "problem",
6987
- description: "Open one Problem: dossier, or --evidence/--reports/--traces/--feedback pages (--conversations is a legacy alias of --traces)",
7521
+ description: "Open one Problem: dossier, --evidence/--reports/--traces/--feedback pages (--conversations is a legacy alias of --traces), or --view=dream",
6988
7522
  examples: [
6989
7523
  "moda problems get <problem_id>",
6990
7524
  "moda problems get <problem_id> --full # whole dossier (agent output defaults to a compact one)",
6991
7525
  "moda problems get <problem_id> --evidence --limit=10",
6992
7526
  "moda problems get <problem_id> --reports",
6993
7527
  "moda problems get <problem_id> --traces --family=tool_failure",
6994
- "moda problems get <problem_id> --feedback"
7528
+ "moda problems get <problem_id> --feedback",
7529
+ "moda problems get <problem_id> --view=dream --limit=3 --offset=3 # the dream page: definition, severity, incidents"
6995
7530
  ],
6996
7531
  ...dataApiDefaults
6997
7532
  }),