@moda-ai/cli 1.34.1 → 1.36.0

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.
@@ -648,7 +648,7 @@ function validateConfig() {
648
648
  }
649
649
  }
650
650
 
651
- // ../../../ModaObservability-prompt-sync/packages/harness-snapshot/src/policy.ts
651
+ // ../../packages/harness-snapshot/src/policy.ts
652
652
  var MAX_SCANNED_FILE_BYTES = 512 * 1024;
653
653
  var DEFAULT_MAX_INVENTORY_FILES = 12000;
654
654
  var DEFAULT_MAX_INVENTORY_TEXT_BYTES = 48 * 1024 * 1024;
@@ -790,7 +790,7 @@ function isSecretishPath(path) {
790
790
  return name === ".env" || name.startsWith(".env.") || name.includes("secret") || name.includes("private-key") || name.endsWith(".pem");
791
791
  }
792
792
 
793
- // ../../../ModaObservability-prompt-sync/packages/harness-snapshot/src/redact.ts
793
+ // ../../packages/harness-snapshot/src/redact.ts
794
794
  var REDACTED = "[REDACTED]";
795
795
  var MAX_DEPTH = 256;
796
796
  var SECRET_KEYS = new Set([
@@ -887,7 +887,7 @@ function redactSnapshotText(text) {
887
887
  return { text: parts.join(""), redactionCount };
888
888
  }
889
889
 
890
- // ../../../ModaObservability-prompt-sync/packages/harness-snapshot/src/pack.ts
890
+ // ../../packages/harness-snapshot/src/pack.ts
891
891
  function utf8ByteLength(value) {
892
892
  let bytes = 0;
893
893
  for (let index = 0;index < value.length; index += 1) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  authFetch,
3
3
  getCachedLoginContext
4
- } from "./cli-eggpzt87.js";
4
+ } from "./cli-yz99t7n8.js";
5
5
  import {
6
6
  CliAccessError,
7
7
  CliInputError,
@@ -15,7 +15,7 @@ import {
15
15
  resolveTenantIdFromActiveProfile,
16
16
  saveCliConfig,
17
17
  writeSecretJsonFile
18
- } from "./cli-0h32yvwq.js";
18
+ } from "./cli-drabjmaj.js";
19
19
 
20
20
  // src/init/tenant.ts
21
21
  import { randomUUID } from "node:crypto";
@@ -8,7 +8,7 @@ import {
8
8
  saveProfileConfig,
9
9
  secretFilePathFromRef,
10
10
  writeSecretJsonFile
11
- } from "./cli-0h32yvwq.js";
11
+ } from "./cli-drabjmaj.js";
12
12
 
13
13
  // src/auth-storage.ts
14
14
  import { existsSync, readFileSync, rmSync } from "node:fs";
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-cagbc7wz.js";
11
+ } from "./cli-7qkhy41j.js";
12
12
  import {
13
13
  ApiError,
14
14
  HARNESS_REPORT_APPROVAL_PATH,
@@ -29,7 +29,7 @@ import {
29
29
  summarizeHarness,
30
30
  terminalStyles,
31
31
  validateHarnessReport
32
- } from "./cli-8q7jffnj.js";
32
+ } from "./cli-0p6n391h.js";
33
33
  import {
34
34
  authFetch,
35
35
  clearAuthSession,
@@ -37,7 +37,7 @@ import {
37
37
  loadAuthSession,
38
38
  login,
39
39
  revokeCliSessionToken
40
- } from "./cli-eggpzt87.js";
40
+ } from "./cli-yz99t7n8.js";
41
41
  import {
42
42
  AGENT_ERROR_CODES,
43
43
  CliAccessError,
@@ -84,9 +84,10 @@ import {
84
84
  validateConfig,
85
85
  wasOutputCancelled,
86
86
  writeSecretJsonFile
87
- } from "./cli-0h32yvwq.js";
87
+ } from "./cli-drabjmaj.js";
88
88
 
89
89
  // src/cli.ts
90
+ import { randomUUID as randomUUID2 } from "node:crypto";
90
91
  import { z as z2 } from "zod";
91
92
 
92
93
  // src/schemas.ts
@@ -209,6 +210,70 @@ var ProblemFeedbackSchema = z.object({
209
210
  new_name: z.string().max(255).optional(),
210
211
  actor: z.string().max(255).optional()
211
212
  });
213
+ var SignalSchema = z.object({
214
+ id: z.string().min(1).max(255),
215
+ detections: boolFlag(),
216
+ days_back: z.number().min(1).max(90).optional(),
217
+ limit: z.number().min(1).max(20).optional(),
218
+ offset: z.number().min(0).max(1e4).optional()
219
+ });
220
+ var SIGNAL_LABELS = ["fits", "does_not_fit"];
221
+ var SignalLabelSchema = z.object({
222
+ id: z.string().min(1).max(255),
223
+ conversation_id: z.string().min(1).max(255),
224
+ label: z.enum(SIGNAL_LABELS),
225
+ note: z.string().max(2000).optional(),
226
+ msg_index: z.preprocess((v) => typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v) : v, z.number().int().min(0).optional()),
227
+ msg_dedup_token: z.string().min(1).max(200).optional()
228
+ });
229
+ var DETECTION_VERDICTS = ["correct", "false_positive", "clear"];
230
+ var DetectionReviewSchema = z.object({
231
+ id: z.string().regex(UUID_RE, "Problem id must be a canonical UUID"),
232
+ conversation_id: z.string().min(1).max(255),
233
+ attribution_id: z.string().regex(UUID_RE, "Attribution id must be a UUID"),
234
+ verdict: z.enum(DETECTION_VERDICTS),
235
+ note: z.string().max(2000).optional()
236
+ });
237
+ var FALSE_POSITIVE_KINDS = ["problem", "problem-detection", "quote", "tool", "laziness", "hallucination", "custom-signal"];
238
+ var FalsePositiveSchema = z.object({
239
+ kind: z.enum(FALSE_POSITIVE_KINDS),
240
+ target: z.string().min(1).max(255),
241
+ undo: z.boolean(),
242
+ reason: z.string().max(2000).optional(),
243
+ conversation_id: z.string().min(1).max(255).optional(),
244
+ attribution_id: z.string().regex(UUID_RE, "Attribution id must be a UUID").optional(),
245
+ family: z.string().regex(/^[a-z_]{1,64}$/, "Family must be a lowercase emotion family").optional(),
246
+ turn: z.preprocess((v) => typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v) : v, z.number().int().min(0).optional()),
247
+ quote: z.string().min(1).max(4000).optional(),
248
+ tool: z.string().min(1).max(255).optional(),
249
+ tool_use_id: z.string().min(1).max(255).optional(),
250
+ msg_index: z.preprocess((v) => typeof v === "string" && /^\d+$/.test(v.trim()) ? Number(v) : v, z.number().int().min(0).optional()),
251
+ msg_dedup_token: z.string().min(1).max(200).optional()
252
+ }).superRefine((v, ctx) => {
253
+ const need = (ok, message) => {
254
+ if (!ok)
255
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message });
256
+ };
257
+ if (v.kind === "problem") {
258
+ need(UUID_RE.test(v.target), "Problem id must be a canonical UUID");
259
+ need(!v.undo, "A problem dismissal cannot be undone from the CLI");
260
+ }
261
+ if (v.kind === "problem-detection") {
262
+ need(UUID_RE.test(v.target), "Problem id must be a canonical UUID");
263
+ need(v.conversation_id, "--conversation-id is required for problem-detection");
264
+ need(v.attribution_id, "--attribution-id is required for problem-detection");
265
+ }
266
+ if (v.kind === "quote") {
267
+ need(v.family && v.turn !== undefined && v.quote, "--family, --turn and --quote are required for quote (copy them from moda emotions)");
268
+ }
269
+ if (v.kind === "tool") {
270
+ need(v.tool, "--tool is required for tool (the failing tool name)");
271
+ need(v.tool_use_id || v.msg_index !== undefined, "--tool-use-id or --msg-index is required for tool (from moda tool-failure-detail)");
272
+ }
273
+ if (v.kind === "custom-signal") {
274
+ need(v.conversation_id, "--conversation-id is required for custom-signal");
275
+ }
276
+ });
212
277
  var EMOTION_FAMILIES = [
213
278
  "frustration",
214
279
  "sadness",
@@ -230,6 +295,13 @@ var HallucinationsSchema = z.object({
230
295
  conversation_id: z.string().max(200).optional(),
231
296
  kind: z.enum(["contradicted", "verified"]).optional()
232
297
  });
298
+ var LazinessSchema = z.object({
299
+ days_back: z.number().min(1).max(90).default(7).optional(),
300
+ limit: z.number().min(1).max(50).default(10).optional(),
301
+ offset: z.number().min(0).max(1e4).optional(),
302
+ conversation_id: z.string().max(200).optional(),
303
+ family: z.string().regex(/^[a-z_]{1,64}$/, "Family must be a lowercase laziness family").optional()
304
+ });
233
305
  var StepScoresSchema = z.object({
234
306
  conversation_id: z.string().min(1)
235
307
  });
@@ -5376,12 +5448,21 @@ Commands:
5376
5448
  frustrations Get user frustration detections (legacy single-family; see emotions)
5377
5449
  emotions Multi-family emotion detections (frustration, sadness, confusion, anxiety, trust, positive)
5378
5450
  hallucinations Grounding detections: contradicted/verified outputs with rule breakdown
5451
+ laziness Laziness detections per trace (repeated calls, premature handoffs, ...)
5379
5452
  tool-failures Get tool failure overview
5380
5453
  tool-failure-detail <tool_name> Get per-tool failure detail
5381
5454
  problems Rank cross-signal Problems by root cause (what to fix first)
5382
5455
  problem-impact Live problem/use-case impact (--node-id, --problem-id, --mode, --days-back)
5383
5456
  problem <problem_id> Open one Problem: dossier, or --evidence/--reports/--traces/--feedback
5384
5457
  problem-feedback <problem_id> Mark a Problem fixed, dismiss, rename, or flag a bad attribution
5458
+ false-positive <kind> <target> Mark any detection as a false positive, optional --reason (problem, problem-detection, quote, tool, laziness, hallucination, custom-signal)
5459
+ false-positives [conv_id] Current false-positive marks: all (--kind, --all, --limit) or one trace
5460
+ detection-reviews <conv_id> Problem detections on a trace with their review state
5461
+ detection-review <problem_id> Mark a problem detection correct, a false positive, or clear the review
5462
+ signals List custom signals (name, criteria, status, 7-day stats)
5463
+ signal <signal_id> One custom signal, or --detections for its recent hits
5464
+ trace-signals <conversation_id> Custom signal hits on one trace
5465
+ signal-label <signal_id> Label a trace fits / does_not_fit (false positive) for a signal
5385
5466
  step-scores <conversation_id> Graph-PRM step scores (per-segment curves, first bad step, rollup)
5386
5467
  tail Live-tail new traces/detections as NDJSON (one line per item)
5387
5468
  feedback "<note>" Flag wrong/missing data or CLI quirks to the Moda team
@@ -5485,6 +5566,11 @@ Emotions flags:
5485
5566
  --days-back=N --limit=N Window 1-90 (default 7); page size 1-20 (default 10)
5486
5567
  --offset=N Skip N detections (0-10000)
5487
5568
 
5569
+ Laziness flags:
5570
+ --family=F Narrow to one laziness family
5571
+ --conversation-id=ID One trace's detections
5572
+ --days-back=N --limit=N Window 1-90 (default 7); page size 1-50 (default 10)
5573
+
5488
5574
  Hallucinations flags:
5489
5575
  --kind=contradicted|verified Narrow the detections list
5490
5576
  --conversation-id=ID Scope summary + list to one trace ID
@@ -5498,10 +5584,39 @@ Problem flags (moda problem <id>):
5498
5584
 
5499
5585
  Problem-feedback flags:
5500
5586
  --action=A mark_fixed|dismiss|flag_attribution|rename (required)
5501
- --reason=TEXT Required for dismiss and flag_attribution
5587
+ --reason=TEXT Required for flag_attribution; optional for dismiss
5502
5588
  --attribution-id=UUID Required for flag_attribution (ids come from problem --evidence)
5503
5589
  --new-name=NAME Required for rename
5504
5590
 
5591
+ False-positive flags (moda false-positive <kind> <target>):
5592
+ --reason=TEXT Why it is wrong (optional)
5593
+ --undo Remove an earlier mark (every kind except problem)
5594
+ problem <problem_id> Dismiss the problem: hidden at once, logged for review, folded into reconcile
5595
+ problem-detection <problem_id> One detection on a trace; needs --conversation-id and --attribution-id; teaches the problem
5596
+ quote <conversation_id> A user quote; needs --family, --turn and --quote (from moda emotions); audit only
5597
+ tool <conversation_id> One failing tool call; needs --tool and --tool-use-id (or --msg-index); audit only
5598
+ laziness <conversation_id> Laziness on that trace; audit only
5599
+ hallucination <conversation_id> Hallucinations on that trace; audit only
5600
+ custom-signal <signal_id> A hit on that trace (--conversation-id); saved as does_not_fit so the signal learns
5601
+
5602
+ Detection-review flags (moda detection-review <problem_id>):
5603
+ --conversation-id=ID Trace the detection is on (required)
5604
+ --attribution-id=UUID Detection id (from problem --evidence or detection-reviews; required)
5605
+ --verdict=V correct|false_positive|clear (required; clear undoes a review)
5606
+ --note=TEXT Why (optional; kept with the review)
5607
+
5608
+ Signal flags:
5609
+ --detections moda signal <id>: list recent hits instead of the summary
5610
+ --days-back=N --limit=N --offset=N
5611
+ Hits window 1-90 (default 7); page size 1-20 (default 10)
5612
+
5613
+ Signal-label flags (moda signal-label <signal_id>):
5614
+ --conversation-id=ID Trace to label (required)
5615
+ --label=fits|does_not_fit does_not_fit marks a false positive and retracts the hit (required)
5616
+ --note=TEXT Why (recommended for does_not_fit)
5617
+ --msg-index=N --msg-dedup-token=T
5618
+ Anchor message (from the hit's anchor_msg_index / anchor_dedup_token)
5619
+
5505
5620
  Tail flags:
5506
5621
  --signal=S traces|emotions|all (default traces)
5507
5622
  --interval=N Poll every N seconds (5-3600, default 15)
@@ -5626,6 +5741,11 @@ Examples:
5626
5741
  moda problems --days-back=7
5627
5742
  moda problem <problem_id> --evidence --limit=10
5628
5743
  moda problem-feedback <problem_id> --action=mark_fixed
5744
+ moda detection-reviews <conversation_id>
5745
+ moda detection-review <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=false_positive --note="expected retry"
5746
+ moda signals
5747
+ moda signal <signal_id> --detections --days-back=30
5748
+ moda signal-label <signal_id> --conversation-id=<id> --label=does_not_fit --note="refund was approved"
5629
5749
  moda step-scores <conversation_id>
5630
5750
  moda tail --signal=all --interval=30
5631
5751
  moda frustrations --days-back=14 --limit=20
@@ -6350,7 +6470,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
6350
6470
  throw new CliInputError(`Problem id must be a canonical UUID; got "${params.id}".`);
6351
6471
  }
6352
6472
  const reason = params.reason?.trim() ?? "";
6353
- if ((params.action === "dismiss" || params.action === "flag_attribution") && reason === "") {
6473
+ if (params.action === "flag_attribution" && reason === "") {
6354
6474
  throw new CliInputError(`--reason is required for --action=${params.action}.`);
6355
6475
  }
6356
6476
  if (params.action === "rename" && (params.new_name?.trim() ?? "") === "") {
@@ -6378,6 +6498,205 @@ async function runCommand(command, positional, flags, positionals = positional ?
6378
6498
  context.output.writeData(data);
6379
6499
  break;
6380
6500
  }
6501
+ case "signals": {
6502
+ const data = await callDataAPI("/custom-signals");
6503
+ context.output.writeData(data);
6504
+ break;
6505
+ }
6506
+ case "signal": {
6507
+ if (!positional) {
6508
+ throw new CliInputError("<signal_id> is required", "Usage: moda signal <signal_id> [--detections] [--days-back=N] [--limit=N] [--offset=N]");
6509
+ }
6510
+ const args = flagsToArgs(flags, "id", positional);
6511
+ args.id = positional;
6512
+ const params = SignalSchema.parse(args);
6513
+ const id = encodeURIComponent(params.id);
6514
+ if (params.detections !== true) {
6515
+ const data2 = await callDataAPI(`/custom-signals/${id}`);
6516
+ context.output.writeData(data2);
6517
+ break;
6518
+ }
6519
+ const query = new URLSearchParams;
6520
+ if (params.days_back)
6521
+ query.set("days_back", params.days_back.toString());
6522
+ if (params.limit)
6523
+ query.set("limit", params.limit.toString());
6524
+ if (params.offset !== undefined)
6525
+ query.set("offset", params.offset.toString());
6526
+ const queryString = query.toString() ? `?${query.toString()}` : "";
6527
+ const data = await callDataAPI(`/custom-signals/${id}/detections${queryString}`);
6528
+ context.output.writeData(data);
6529
+ break;
6530
+ }
6531
+ case "trace-signals": {
6532
+ if (!positional) {
6533
+ throw new CliInputError("<conversation_id> is required", "Usage: moda trace-signals <conversation_id>");
6534
+ }
6535
+ const data = await callDataAPI(`/conversations/${encodeURIComponent(positional)}/custom-signals`);
6536
+ context.output.writeData(data);
6537
+ break;
6538
+ }
6539
+ case "signal-label": {
6540
+ const usage = `Usage: moda signal-label <signal_id> --conversation-id=ID --label=${SIGNAL_LABELS.join("|")} [--note=TEXT] [--msg-index=N] [--msg-dedup-token=T]`;
6541
+ if (!positional) {
6542
+ throw new CliInputError("<signal_id> is required", usage);
6543
+ }
6544
+ const params = SignalLabelSchema.parse({
6545
+ id: positional,
6546
+ ...flags["conversation-id"] !== undefined ? { conversation_id: flags["conversation-id"] } : {},
6547
+ ...flags.label !== undefined ? { label: flags.label } : {},
6548
+ ...flags.note !== undefined ? { note: flags.note } : {},
6549
+ ...flags["msg-index"] !== undefined ? { msg_index: flags["msg-index"] } : {},
6550
+ ...flags["msg-dedup-token"] !== undefined ? { msg_dedup_token: flags["msg-dedup-token"] } : {}
6551
+ });
6552
+ const note = params.note?.trim() ?? "";
6553
+ const data = await callDataAPI(`/custom-signals/${encodeURIComponent(params.id)}/labels`, {
6554
+ method: "POST",
6555
+ body: JSON.stringify({
6556
+ conversation_id: params.conversation_id,
6557
+ label: params.label,
6558
+ ...note !== "" ? { note } : {},
6559
+ ...params.msg_index !== undefined ? { msg_index: params.msg_index } : {},
6560
+ ...params.msg_dedup_token ? { msg_dedup_token: params.msg_dedup_token } : {}
6561
+ }),
6562
+ headers: { "Content-Type": "application/json" }
6563
+ }, { retries: 0 });
6564
+ context.output.writeData(data);
6565
+ break;
6566
+ }
6567
+ case "detection-reviews": {
6568
+ if (!positional) {
6569
+ throw new CliInputError("<conversation_id> is required", "Usage: moda detection-reviews <conversation_id> [--attribution-id=UUID]");
6570
+ }
6571
+ const attributionId = flags["attribution-id"];
6572
+ if (attributionId !== undefined && !UUID_RE.test(attributionId)) {
6573
+ throw new CliInputError("--attribution-id must be a UUID.");
6574
+ }
6575
+ const queryString = attributionId ? `?attribution_id=${encodeURIComponent(attributionId)}` : "";
6576
+ const data = await callDataAPI(`/conversations/${encodeURIComponent(positional)}/detection-reviews${queryString}`);
6577
+ context.output.writeData(data);
6578
+ break;
6579
+ }
6580
+ case "detection-review": {
6581
+ const usage = `Usage: moda detection-review <problem_id> --conversation-id=ID --attribution-id=UUID --verdict=${DETECTION_VERDICTS.join("|")} [--note=TEXT]`;
6582
+ if (!positional) {
6583
+ throw new CliInputError("<problem_id> is required", usage);
6584
+ }
6585
+ const params = DetectionReviewSchema.parse({
6586
+ id: positional,
6587
+ ...flags["conversation-id"] !== undefined ? { conversation_id: flags["conversation-id"] } : {},
6588
+ ...flags["attribution-id"] !== undefined ? { attribution_id: flags["attribution-id"] } : {},
6589
+ ...flags.verdict !== undefined ? { verdict: flags.verdict } : {},
6590
+ ...flags.note !== undefined ? { note: flags.note } : {}
6591
+ });
6592
+ const note = params.verdict === "clear" ? "" : params.note?.trim() ?? "";
6593
+ const data = await callDataAPI(`/problems/${params.id}/detection-reviews`, {
6594
+ method: "POST",
6595
+ body: JSON.stringify({
6596
+ conversation_id: params.conversation_id,
6597
+ attribution_id: params.attribution_id,
6598
+ verdict: params.verdict,
6599
+ ...note !== "" ? { note } : {},
6600
+ request_nonce: randomUUID2()
6601
+ }),
6602
+ headers: { "Content-Type": "application/json" }
6603
+ });
6604
+ context.output.writeData(data);
6605
+ break;
6606
+ }
6607
+ case "false-positive": {
6608
+ const usage = `Usage: moda false-positive <${FALSE_POSITIVE_KINDS.join("|")}> <target> --reason=TEXT [--undo]`;
6609
+ const [kind, target] = positionals;
6610
+ if (!kind || !target)
6611
+ throw new CliInputError("<kind> and <target> are required", usage);
6612
+ const params = FalsePositiveSchema.parse({
6613
+ kind,
6614
+ target,
6615
+ undo: flags.undo !== undefined && flags.undo !== "false",
6616
+ ...flags.reason !== undefined ? { reason: flags.reason } : {},
6617
+ ...flags["conversation-id"] !== undefined ? { conversation_id: flags["conversation-id"] } : {},
6618
+ ...flags["attribution-id"] !== undefined ? { attribution_id: flags["attribution-id"] } : {},
6619
+ ...flags.family !== undefined ? { family: flags.family } : {},
6620
+ ...flags.turn !== undefined ? { turn: flags.turn } : {},
6621
+ ...flags.quote !== undefined ? { quote: flags.quote } : {},
6622
+ ...flags.tool !== undefined ? { tool: flags.tool } : {},
6623
+ ...flags["tool-use-id"] !== undefined ? { tool_use_id: flags["tool-use-id"] } : {},
6624
+ ...flags["msg-index"] !== undefined ? { msg_index: flags["msg-index"] } : {},
6625
+ ...flags["msg-dedup-token"] !== undefined ? { msg_dedup_token: flags["msg-dedup-token"] } : {}
6626
+ });
6627
+ const reason = params.reason?.trim() ?? "";
6628
+ const post = (path, body, retries) => callDataAPI(path, { method: "POST", body: JSON.stringify(body), headers: { "Content-Type": "application/json" } }, retries === undefined ? undefined : { retries });
6629
+ let data;
6630
+ switch (params.kind) {
6631
+ case "problem":
6632
+ data = await post(`/problems/${params.target}/feedback`, { action: "dismiss", reason, actor: "moda-cli" }, 0);
6633
+ break;
6634
+ case "problem-detection":
6635
+ data = await post(`/problems/${params.target}/detection-reviews`, {
6636
+ conversation_id: params.conversation_id,
6637
+ attribution_id: params.attribution_id,
6638
+ verdict: params.undo ? "clear" : "false_positive",
6639
+ ...params.undo ? {} : { note: reason },
6640
+ request_nonce: randomUUID2()
6641
+ });
6642
+ break;
6643
+ case "custom-signal":
6644
+ if (params.undo) {
6645
+ data = await callDataAPI(`/custom-signals/${encodeURIComponent(params.target)}/labels/${encodeURIComponent(params.conversation_id ?? "")}`, { method: "DELETE" }, { retries: 0 });
6646
+ break;
6647
+ }
6648
+ data = await post(`/custom-signals/${encodeURIComponent(params.target)}/labels`, {
6649
+ conversation_id: params.conversation_id,
6650
+ label: "does_not_fit",
6651
+ note: reason,
6652
+ ...params.msg_index !== undefined ? { msg_index: params.msg_index } : {},
6653
+ ...params.msg_dedup_token ? { msg_dedup_token: params.msg_dedup_token } : {}
6654
+ }, 0);
6655
+ break;
6656
+ default: {
6657
+ const apiKind = { quote: "frustration_quote", tool: "tool_call", laziness: "laziness", hallucination: "hallucination" }[params.kind];
6658
+ data = await post("/false-positives", {
6659
+ kind: apiKind,
6660
+ verdict: params.undo ? "clear" : "false_positive",
6661
+ ...params.undo ? {} : { note: reason },
6662
+ conversation_id: params.target,
6663
+ ...params.kind === "tool" ? {
6664
+ tool_name: params.tool,
6665
+ ...params.tool_use_id ? { tool_use_id: params.tool_use_id } : { msg_index: params.msg_index }
6666
+ } : {},
6667
+ ...params.kind === "quote" ? { family: params.family, turn: params.turn, quote: params.quote } : {},
6668
+ request_nonce: randomUUID2()
6669
+ });
6670
+ }
6671
+ }
6672
+ context.output.writeData(data);
6673
+ break;
6674
+ }
6675
+ case "false-positives": {
6676
+ if (!positional) {
6677
+ const kinds = { quote: "frustration_quote", tool: "tool_call", laziness: "laziness", hallucination: "hallucination", "custom-signal": "custom_signal" };
6678
+ const query = new URLSearchParams;
6679
+ if (flags.kind !== undefined) {
6680
+ if (!kinds[flags.kind])
6681
+ throw new CliInputError(`--kind must be one of ${Object.keys(kinds).join("|")}`);
6682
+ query.set("kind", kinds[flags.kind]);
6683
+ }
6684
+ if (flags.limit !== undefined) {
6685
+ if (!/^\d+$/.test(flags.limit) || Number(flags.limit) < 1 || Number(flags.limit) > 200)
6686
+ throw new CliInputError("--limit must be 1-200");
6687
+ query.set("limit", flags.limit);
6688
+ }
6689
+ if (flags.all !== undefined && flags.all !== "false")
6690
+ query.set("include_cleared", "true");
6691
+ const queryString = query.toString() ? `?${query.toString()}` : "";
6692
+ const data2 = await callDataAPI(`/false-positives${queryString}`);
6693
+ context.output.writeData(data2);
6694
+ break;
6695
+ }
6696
+ const data = await callDataAPI(`/conversations/${encodeURIComponent(positional)}/false-positives`);
6697
+ context.output.writeData(data);
6698
+ break;
6699
+ }
6381
6700
  case "emotions": {
6382
6701
  const args = flagsToArgs(flags);
6383
6702
  const params = EmotionsSchema.parse(args);
@@ -6395,6 +6714,28 @@ async function runCommand(command, positional, flags, positionals = positional ?
6395
6714
  context.output.writeData(data);
6396
6715
  break;
6397
6716
  }
6717
+ case "laziness": {
6718
+ const args = flagsToArgs(flags);
6719
+ if (flags["conversation-id"] !== undefined) {
6720
+ args.conversation_id = flags["conversation-id"];
6721
+ }
6722
+ const params = LazinessSchema.parse(args);
6723
+ const query = new URLSearchParams;
6724
+ if (params.days_back)
6725
+ query.set("days_back", params.days_back.toString());
6726
+ if (params.limit)
6727
+ query.set("limit", params.limit.toString());
6728
+ if (params.offset !== undefined)
6729
+ query.set("offset", params.offset.toString());
6730
+ if (params.conversation_id)
6731
+ query.set("conversation_id", params.conversation_id);
6732
+ if (params.family)
6733
+ query.set("family", params.family);
6734
+ const queryString = query.toString() ? `?${query.toString()}` : "";
6735
+ const data = await callDataAPI(`/laziness${queryString}`);
6736
+ context.output.writeData(data);
6737
+ break;
6738
+ }
6398
6739
  case "hallucinations": {
6399
6740
  const args = flagsToArgs(flags);
6400
6741
  if (flags["conversation-id"] !== undefined) {
@@ -6580,7 +6921,7 @@ var commandRegistry = createCommandRegistry([
6580
6921
  resetApiRequestCountBeforeRun: true,
6581
6922
  telemetry: "result",
6582
6923
  handler: async (context) => {
6583
- const { runInit } = await import("./index-h6gg66n5.js");
6924
+ const { runInit } = await import("./index-pf2hvadv.js");
6584
6925
  if (context.outputMode === "agent-stream") {
6585
6926
  context.output.writeEvent({
6586
6927
  event: "started",
@@ -6621,7 +6962,7 @@ var commandRegistry = createCommandRegistry([
6621
6962
  resetApiRequestCountBeforeRun: true,
6622
6963
  telemetry: "result-and-error",
6623
6964
  handler: async (context) => {
6624
- const { runProvision } = await import("./provision-c5sx3s32.js");
6965
+ const { runProvision } = await import("./provision-d242y9qd.js");
6625
6966
  return runProvision(context, {
6626
6967
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
6627
6968
  });
@@ -7001,6 +7342,83 @@ var commandRegistry = createCommandRegistry([
7001
7342
  ...dataApiDefaults,
7002
7343
  mutability: "write"
7003
7344
  }),
7345
+ legacyCommand({
7346
+ name: "detection-reviews",
7347
+ description: "List problem detections on a trace with their review state (correct / false_positive / clear)",
7348
+ examples: [
7349
+ "moda detection-reviews <conversation_id>",
7350
+ "moda detection-reviews <conversation_id> --attribution-id=<uuid>"
7351
+ ],
7352
+ ...dataApiDefaults
7353
+ }),
7354
+ legacyCommand({
7355
+ name: "detection-review",
7356
+ description: "Review a problem detection on a trace: mark it correct, a false positive, or clear the review",
7357
+ examples: [
7358
+ 'moda detection-review <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=false_positive --note="expected retry"',
7359
+ "moda detection-review <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=correct",
7360
+ "moda detection-review <problem_id> --conversation-id=<id> --attribution-id=<uuid> --verdict=clear"
7361
+ ],
7362
+ ...dataApiDefaults,
7363
+ mutability: "write"
7364
+ }),
7365
+ legacyCommand({
7366
+ name: "false-positive",
7367
+ description: "Flag a detection as a false positive with a reason: problem, problem-detection, quote, tool, laziness, hallucination, or custom-signal",
7368
+ examples: [
7369
+ 'moda false-positive problem <problem_id> --reason="two unrelated tools"',
7370
+ 'moda false-positive problem-detection <problem_id> --conversation-id=<id> --attribution-id=<uuid> --reason="refund was approved"',
7371
+ 'moda false-positive quote <conversation_id> --family=frustration --turn=3 --quote="this is useless lol" --reason="joking"',
7372
+ 'moda false-positive tool <conversation_id> --tool=send_invoice --tool-use-id=<id> --reason="retried upstream"',
7373
+ 'moda false-positive laziness <conversation_id> --reason="reload after context reset is expected"',
7374
+ "moda false-positive hallucination <conversation_id> --undo",
7375
+ "moda false-positive custom-signal <signal_id> --conversation-id=<id> --undo",
7376
+ 'moda false-positive custom-signal <signal_id> --conversation-id=<id> --reason="refund was approved"'
7377
+ ],
7378
+ ...dataApiDefaults,
7379
+ mutability: "write"
7380
+ }),
7381
+ legacyCommand({
7382
+ name: "false-positives",
7383
+ description: "List current false-positive marks across the workspace, or on one trace (with problem detection reviews)",
7384
+ examples: [
7385
+ "moda false-positives",
7386
+ "moda false-positives --kind=tool --all",
7387
+ "moda false-positives <conversation_id>"
7388
+ ],
7389
+ ...dataApiDefaults
7390
+ }),
7391
+ legacyCommand({
7392
+ name: "signals",
7393
+ description: "List custom signals with criteria, status, and 7-day judged/matched stats",
7394
+ examples: ["moda signals"],
7395
+ ...dataApiDefaults
7396
+ }),
7397
+ legacyCommand({
7398
+ name: "signal",
7399
+ description: "Open one custom signal, or --detections for its recent hits (paged)",
7400
+ examples: [
7401
+ "moda signal <signal_id>",
7402
+ "moda signal <signal_id> --detections --days-back=30 --limit=20"
7403
+ ],
7404
+ ...dataApiDefaults
7405
+ }),
7406
+ legacyCommand({
7407
+ name: "trace-signals",
7408
+ description: "Custom signal hits on one trace",
7409
+ examples: ["moda trace-signals <conversation_id>"],
7410
+ ...dataApiDefaults
7411
+ }),
7412
+ legacyCommand({
7413
+ name: "signal-label",
7414
+ description: "Label a trace for a custom signal: fits, or does_not_fit to mark a false positive",
7415
+ examples: [
7416
+ 'moda signal-label <signal_id> --conversation-id=<id> --label=does_not_fit --note="refund was approved"',
7417
+ "moda signal-label <signal_id> --conversation-id=<id> --label=fits --msg-index=12"
7418
+ ],
7419
+ ...dataApiDefaults,
7420
+ mutability: "write"
7421
+ }),
7004
7422
  legacyCommand({
7005
7423
  name: "emotions",
7006
7424
  description: "Multi-family emotion detections (frustration, sadness, confusion, anxiety, trust, positive)",
@@ -7010,6 +7428,15 @@ var commandRegistry = createCommandRegistry([
7010
7428
  ],
7011
7429
  ...dataApiDefaults
7012
7430
  }),
7431
+ legacyCommand({
7432
+ name: "laziness",
7433
+ description: "Laziness detections per trace: repeated tool calls, premature handoffs, and other lazy patterns",
7434
+ examples: [
7435
+ "moda laziness --days-back=30",
7436
+ "moda laziness --conversation-id=<id>"
7437
+ ],
7438
+ ...dataApiDefaults
7439
+ }),
7013
7440
  legacyCommand({
7014
7441
  name: "hallucinations",
7015
7442
  description: "Grounding detections: contradicted/verified outputs with rule breakdown",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildSourceSnapshot
3
- } from "./cli-8q7jffnj.js";
4
- import"./cli-0h32yvwq.js";
3
+ } from "./cli-0p6n391h.js";
4
+ import"./cli-drabjmaj.js";
5
5
 
6
6
  // src/harness-github-actions.ts
7
7
  var DEFAULT_MODA_GITHUB_URL = "https://moda-github.modas.workers.dev";
@@ -42,8 +42,8 @@ import {
42
42
  writeHarnessAnalyzeResult,
43
43
  writeHarnessArtifacts,
44
44
  writeHumanProgress
45
- } from "./cli-8q7jffnj.js";
46
- import"./cli-0h32yvwq.js";
45
+ } from "./cli-0p6n391h.js";
46
+ import"./cli-drabjmaj.js";
47
47
  export {
48
48
  writeHumanProgress,
49
49
  writeHarnessArtifacts,