@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.
- package/dist/{agent-9r58v0bc.js → agent-ck6y31n2.js} +8 -8
- package/dist/{cli-3n040fp2.js → cli-0xc5htv7.js} +1 -1
- package/dist/{cli-tdcx8yz9.js → cli-1z1sx8dg.js} +3 -3
- package/dist/{cli-dmnayt2r.js → cli-4tg0hkbz.js} +8 -8
- package/dist/{cli-9g5kzsg6.js → cli-77ekrx7d.js} +1 -1
- package/dist/{cli-93926afn.js → cli-7bn406mt.js} +4 -4
- package/dist/{cli-xtyszvq1.js → cli-a8nxbtpk.js} +5 -5
- package/dist/{cli-skvvwp2q.js → cli-azage7s0.js} +5 -5
- package/dist/{cli-xk630wjq.js → cli-daqkdnm1.js} +1 -1
- package/dist/{cli-xheg54qc.js → cli-f1jfrqgf.js} +39 -7
- package/dist/{cli-n311b8g0.js → cli-gt3yq1y5.js} +2 -2
- package/dist/{cli-sk1p71wr.js → cli-pmzh2qjy.js} +1 -1
- package/dist/{cli-rcjhfts4.js → cli-t0pb7qbs.js} +66 -16
- package/dist/{cli-b7nyw498.js → cli-xddtv3yn.js} +1 -1
- package/dist/{cli-nh23q6ay.js → cli-ypqwcf9n.js} +5 -5
- package/dist/cli.js +587 -52
- package/dist/{doctor-xq5bjvpq.js → doctor-p8sp6cbk.js} +7 -7
- package/dist/{fix-kvszavm1.js → fix-0r974yge.js} +4 -4
- package/dist/{harness-qatnwxrz.js → harness-72ejenza.js} +5 -5
- package/dist/{harness-github-actions-drbkcb8d.js → harness-github-actions-2qc4gg2w.js} +5 -5
- package/dist/{index-y5jnz2t2.js → index-w7v472w4.js} +11 -11
- package/dist/{production-intelligence-j55vaw6t.js → production-intelligence-hs2sm8t5.js} +3 -3
- package/dist/{profile-commands-skdxe17v.js → profile-commands-se9947c1.js} +3 -3
- package/dist/{prompt-attribution-zq38actx.js → prompt-attribution-3987hhqz.js} +5 -5
- package/dist/prompt-source-mcp.js +5 -5
- package/dist/{prompts-ab-yd2dawe3.js → prompts-ab-gmd10n5d.js} +7 -7
- package/dist/{prompts-propose-11dj9ked.js → prompts-propose-q626zmm4.js} +7 -7
- package/dist/{prompts-cwqn8a57.js → prompts-ysm9ra72.js} +5 -5
- package/dist/{provision-ghca9d54.js → provision-cya5rpxa.js} +5 -5
- package/dist/{registry-76sngjw6.js → registry-7xz09ry0.js} +3 -3
- package/dist/{skills-btn9r48v.js → skills-x6yszxxn.js} +5 -5
- package/dist/{sync-ye7nxeen.js → sync-trjzvam9.js} +7 -7
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +7 -3
- package/skills/moda-cli/references/commands.md +3 -2
- package/skills/moda-cli/references/dashboard-parity.md +2 -0
- package/skills/moda-cli/references/detections.md +29 -0
- 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-
|
|
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-
|
|
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-
|
|
82
|
+
} from "./cli-f1jfrqgf.js";
|
|
75
83
|
import {
|
|
76
84
|
ApiError,
|
|
77
85
|
callControlAPI,
|
|
78
86
|
callDataAPI
|
|
79
|
-
} from "./cli-
|
|
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-
|
|
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: "
|
|
1160
|
-
examples: ["moda problems list", "moda problems list --days-back=7 --
|
|
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,
|
|
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-
|
|
3178
|
+
return (await import("./prompts-ysm9ra72.js")).runPromptsCommand(...args);
|
|
2986
3179
|
}
|
|
2987
3180
|
async function runSkillsCommand(...args) {
|
|
2988
|
-
return (await import("./skills-
|
|
3181
|
+
return (await import("./skills-x6yszxxn.js")).runSkillsCommand(...args);
|
|
2989
3182
|
}
|
|
2990
3183
|
async function runFixCommand(...args) {
|
|
2991
|
-
return (await import("./fix-
|
|
3184
|
+
return (await import("./fix-0r974yge.js")).runFixCommand(...args);
|
|
2992
3185
|
}
|
|
2993
3186
|
async function runFixesCommand(...args) {
|
|
2994
|
-
return (await import("./fix-
|
|
3187
|
+
return (await import("./fix-0r974yge.js")).runFixesCommand(...args);
|
|
2995
3188
|
}
|
|
2996
3189
|
async function runHarnessCommand(...args) {
|
|
2997
|
-
return (await import("./harness-
|
|
3190
|
+
return (await import("./harness-72ejenza.js")).runHarnessCommand(...args);
|
|
2998
3191
|
}
|
|
2999
3192
|
async function runSyncCommand(...args) {
|
|
3000
|
-
return (await import("./sync-
|
|
3193
|
+
return (await import("./sync-trjzvam9.js")).runSyncCommand(...args);
|
|
3001
3194
|
}
|
|
3002
3195
|
async function runRegistryCommand(...args) {
|
|
3003
|
-
return (await import("./registry-
|
|
3196
|
+
return (await import("./registry-7xz09ry0.js")).runRegistryCommand(...args);
|
|
3004
3197
|
}
|
|
3005
3198
|
async function runDoctorCommand(...args) {
|
|
3006
|
-
return (await import("./doctor-
|
|
3199
|
+
return (await import("./doctor-p8sp6cbk.js")).runDoctorCommand(...args);
|
|
3007
3200
|
}
|
|
3008
3201
|
async function runStatusCommand(...args) {
|
|
3009
|
-
return (await import("./doctor-
|
|
3202
|
+
return (await import("./doctor-p8sp6cbk.js")).runStatusCommand(...args);
|
|
3010
3203
|
}
|
|
3011
3204
|
async function runAuthCommand(...args) {
|
|
3012
|
-
return (await import("./profile-commands-
|
|
3205
|
+
return (await import("./profile-commands-se9947c1.js")).runAuthCommand(...args);
|
|
3013
3206
|
}
|
|
3014
3207
|
async function runConfigCommand(...args) {
|
|
3015
|
-
return (await import("./profile-commands-
|
|
3208
|
+
return (await import("./profile-commands-se9947c1.js")).runConfigCommand(...args);
|
|
3016
3209
|
}
|
|
3017
3210
|
async function runProfilesCommand(...args) {
|
|
3018
|
-
return (await import("./profile-commands-
|
|
3211
|
+
return (await import("./profile-commands-se9947c1.js")).runProfilesCommand(...args);
|
|
3019
3212
|
}
|
|
3020
3213
|
async function runAgentCommand(...args) {
|
|
3021
|
-
return (await import("./agent-
|
|
3214
|
+
return (await import("./agent-ck6y31n2.js")).runAgentCommand(...args);
|
|
3022
3215
|
}
|
|
3023
3216
|
async function runProductionCommand(...args) {
|
|
3024
|
-
return (await import("./production-intelligence-
|
|
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'
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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 --
|
|
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,
|
|
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
|
}),
|