@moda-ai/cli 1.49.0 → 1.52.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.
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-6xndg2h4.js";
11
+ } from "./cli-ha40mgy1.js";
12
12
  import {
13
13
  ApiError,
14
14
  HARNESS_REPORT_APPROVAL_PATH,
@@ -37,7 +37,7 @@ import {
37
37
  terminalStyles,
38
38
  unknownSignalsSubcommandError,
39
39
  validateHarnessReport
40
- } from "./cli-m3bg9yyh.js";
40
+ } from "./cli-he2wb4hd.js";
41
41
  import"./cli-ssfq84k5.js";
42
42
  import {
43
43
  authFetch,
@@ -46,7 +46,7 @@ import {
46
46
  loadAuthSession,
47
47
  login,
48
48
  revokeCliSessionToken
49
- } from "./cli-c64d2jrw.js";
49
+ } from "./cli-kxwm7y62.js";
50
50
  import {
51
51
  AGENT_ERROR_CODES,
52
52
  CliAccessError,
@@ -97,7 +97,7 @@ import {
97
97
  validateConfig,
98
98
  wasOutputCancelled,
99
99
  writeSecretJsonFile
100
- } from "./cli-bqhs6sk7.js";
100
+ } from "./cli-ac3p2byq.js";
101
101
  import {
102
102
  __require
103
103
  } from "./cli-0v6na3yp.js";
@@ -150,11 +150,20 @@ var WorldStateSchema = z.object({
150
150
  format: z.enum(["frames", "fold-inputs"]).optional(),
151
151
  message_count: z.number().min(1).max(1e6).optional()
152
152
  });
153
+ var lenientBoolFlag = () => z.preprocess((val) => {
154
+ const text = String(val).toLowerCase();
155
+ if (["true", "1", "yes", "on"].includes(text))
156
+ return true;
157
+ if (["false", "0", "no", "off"].includes(text))
158
+ return false;
159
+ return val;
160
+ }, z.boolean()).optional();
153
161
  var ContextSchema = z.object({
154
- conversation_id: z.string(),
155
- msg_index: z.number().min(0).optional(),
162
+ conversation_id: z.string().min(1),
163
+ msg_index: z.number().int().min(0).optional(),
156
164
  window: z.number().int().min(0).optional(),
157
- all: boolFlag(),
165
+ metadata_only: lenientBoolFlag(),
166
+ all: lenientBoolFlag(),
158
167
  from: z.number().int().min(0).optional(),
159
168
  max_messages: z.number().int().min(1).max(5000).optional()
160
169
  });
@@ -547,8 +556,16 @@ function buildFrustrationAnchor(row) {
547
556
  if (firstQuote) {
548
557
  signal = asString(firstQuote.signal);
549
558
  const quote = asString(firstQuote.quote);
550
- if (quote !== null)
551
- quote_preview = quote.slice(0, 200);
559
+ if (quote !== null) {
560
+ let end = 0;
561
+ let points = 0;
562
+ for (const ch of quote) {
563
+ if (points++ === 200)
564
+ break;
565
+ end += ch.length;
566
+ }
567
+ quote_preview = quote.slice(0, end);
568
+ }
552
569
  }
553
570
  return {
554
571
  kind: "frustration",
@@ -624,7 +641,6 @@ function withFrustrationAnchors(response) {
624
641
  });
625
642
  return { ...response, frustrations };
626
643
  }
627
- var TOOL_FAILURE_TEXT_LIMIT = 300;
628
644
  var TOOL_FAILURE_CONTEXT_RADIUS = 2;
629
645
  function shellWord(value) {
630
646
  return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
@@ -674,26 +690,6 @@ function trimToolFailureConversations(response, opts) {
674
690
  });
675
691
  return { ...response, examples };
676
692
  }
677
- function compactToolFailureExamples(response, maxChars = TOOL_FAILURE_TEXT_LIMIT) {
678
- if (!Array.isArray(response.examples))
679
- return { data: response, truncated: 0 };
680
- let truncated = 0;
681
- const cap = (value) => {
682
- if (typeof value === "string") {
683
- if (value.length <= maxChars)
684
- return value;
685
- truncated += 1;
686
- return `${value.slice(0, maxChars)}… [truncated ${value.length - maxChars} chars]`;
687
- }
688
- if (Array.isArray(value))
689
- return value.map(cap);
690
- if (value && typeof value === "object") {
691
- return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, cap(child)]));
692
- }
693
- return value;
694
- };
695
- return { data: { ...response, examples: response.examples.map(cap) }, truncated };
696
- }
697
693
  function withToolFailureAnchors(response) {
698
694
  if (!Array.isArray(response.examples))
699
695
  return response;
@@ -3547,6 +3543,11 @@ function stripTelemetryFlag(flags) {
3547
3543
  function isTelemetryDisabled(flags) {
3548
3544
  return flags[TELEMETRY_FLAG] === "true" || process.env.MODA_CLI_TELEMETRY === "0" || process.env.DO_NOT_TRACK === "1";
3549
3545
  }
3546
+ var VALIDATION_REASON_MAX = 128;
3547
+ function sanitizeValidationReason(reason) {
3548
+ const cleaned = reason?.replace(/[^A-Za-z0-9_.:-]/g, "_").slice(0, VALIDATION_REASON_MAX);
3549
+ return cleaned ? cleaned : undefined;
3550
+ }
3550
3551
  function sanitizedFlagNames(flags) {
3551
3552
  return Object.keys(stripTelemetryFlag(flags)).sort();
3552
3553
  }
@@ -3554,6 +3555,7 @@ async function sendCliUsageTelemetry(input) {
3554
3555
  if (!input.apiKey || isTelemetryDisabled(input.flags)) {
3555
3556
  return;
3556
3557
  }
3558
+ const validationReason = sanitizeValidationReason(input.validationReason);
3557
3559
  const controller = new AbortController;
3558
3560
  const timeout = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
3559
3561
  if (typeof timeout.unref === "function") {
@@ -3571,6 +3573,7 @@ async function sendCliUsageTelemetry(input) {
3571
3573
  command: input.command.slice(0, 64),
3572
3574
  status: input.status,
3573
3575
  error_type: input.errorType,
3576
+ ...validationReason ? { validation_reason: validationReason } : {},
3574
3577
  exit_code: input.exitCode,
3575
3578
  duration_ms: Math.max(0, Math.round(input.durationMs)),
3576
3579
  api_request_count: Math.max(0, Math.round(input.apiRequestCount)),
@@ -5385,7 +5388,6 @@ var MODA_DIR = ".moda";
5385
5388
  var HARNESS_PATH = join4(MODA_DIR, "harness.json");
5386
5389
  var HARNESS_REPORT_MARKDOWN_PATH = join4(MODA_DIR, "HARNESS_REPORT.md");
5387
5390
  var HARNESS_SUMMARY_PATH = join4(MODA_DIR, "HARNESS.md");
5388
- var MAX_AGENT_EVIDENCE_BYTES = 24000;
5389
5391
  async function runAgentCommand(context) {
5390
5392
  const subcommand2 = context.positionals[0] ?? "context";
5391
5393
  switch (subcommand2) {
@@ -5738,7 +5740,7 @@ function expandEvidenceRef(ref, loaded, cwd) {
5738
5740
  label: artifact.description ?? artifact.path,
5739
5741
  path: artifact.path
5740
5742
  },
5741
- value: readBoundedModaArtifact(cwd, artifact.path),
5743
+ value: readModaArtifact(cwd, artifact.path),
5742
5744
  artifact
5743
5745
  };
5744
5746
  }
@@ -5769,11 +5771,11 @@ function readCitationExcerpt(cwd, citation) {
5769
5771
  return citation.excerpt;
5770
5772
  const text = readFileSync5(absolute, "utf8");
5771
5773
  if (citation.line && citation.line > 0) {
5772
- return text.split(/\r?\n/)[citation.line - 1]?.slice(0, 1000) ?? citation.excerpt;
5774
+ return text.split(/\r?\n/)[citation.line - 1] ?? citation.excerpt;
5773
5775
  }
5774
- return text.slice(0, 1000);
5776
+ return text;
5775
5777
  }
5776
- function readBoundedModaArtifact(cwd, artifactPath) {
5778
+ function readModaArtifact(cwd, artifactPath) {
5777
5779
  if (!artifactPath.startsWith(".moda/")) {
5778
5780
  throw new CliInputError(`Artifact '${artifactPath}' is outside .moda and cannot be expanded directly.`, "Use a citation evidence ref for source-file evidence.");
5779
5781
  }
@@ -5781,12 +5783,10 @@ function readBoundedModaArtifact(cwd, artifactPath) {
5781
5783
  if (!absolute || !existsSync4(absolute))
5782
5784
  throw missingEvidence(`artifact:${artifactPath}`);
5783
5785
  const size = statSync2(absolute).size;
5784
- const text = readFileSync5(absolute, "utf8");
5785
5786
  return {
5786
5787
  path: artifactPath,
5787
5788
  bytes: size,
5788
- truncated: size > MAX_AGENT_EVIDENCE_BYTES,
5789
- text: text.slice(0, MAX_AGENT_EVIDENCE_BYTES)
5789
+ text: readFileSync5(absolute, "utf8")
5790
5790
  };
5791
5791
  }
5792
5792
  function safeResolve(cwd, path) {
@@ -6679,7 +6679,7 @@ function frustrationFindings(data, daysBack, evidenceRefs) {
6679
6679
  id: "action_frustration_window",
6680
6680
  title: "Read the frustration window.",
6681
6681
  rationale: "The user quote and nearby turns reveal what confused or blocked the user.",
6682
- command: conversationId ? `moda context ${conversationId}${anchorIndex !== null ? ` --msg-index=${anchorIndex}` : ""} --window=3` : `moda frustrations --days-back=${daysBack} --include-window`,
6682
+ command: conversationId ? `moda context ${shellWord(conversationId)}${anchorIndex !== null ? ` --msg-index=${anchorIndex}` : ""} --window=3` : `moda frustrations --days-back=${daysBack} --include-window`,
6683
6683
  mutability: "read",
6684
6684
  requiresApproval: false
6685
6685
  }]
@@ -7282,7 +7282,7 @@ function toolRows(data, scopedTool) {
7282
7282
  function firstQuote(row) {
7283
7283
  const quotes = Array.isArray(row?.user_quotes) ? row.user_quotes : [];
7284
7284
  const first = asRecord(quotes[0]);
7285
- return asString(first?.quote)?.slice(0, 160) ?? undefined;
7285
+ return asString(first?.quote) ?? undefined;
7286
7286
  }
7287
7287
  function likelyToolLocation(toolName, hints) {
7288
7288
  const normalized = normalizeKey(toolName);
@@ -7595,7 +7595,10 @@ async function finishRun(context, outcome) {
7595
7595
  context.output.close();
7596
7596
  if (wasOutputCancelled())
7597
7597
  return EXIT_CANCELLED;
7598
- return outcome.exitCode ?? EXIT_OK;
7598
+ const exitCode = outcome.exitCode ?? EXIT_OK;
7599
+ if (context.output.hasOutputError() && (exitCode === EXIT_OK || exitCode === EXIT_DEGRADED))
7600
+ return EXIT_ERROR;
7601
+ return exitCode;
7599
7602
  }
7600
7603
  if (outcome.kind === "cancelled") {
7601
7604
  context.output.writeCancelled(outcome.reason ?? "Command cancelled.");
@@ -7659,6 +7662,8 @@ function buildManifest(commands) {
7659
7662
  "MODA_AUTH_TIMEOUT_MS",
7660
7663
  "MODA_HEARTBEAT_MS",
7661
7664
  "MODA_AGENT_MAX_OUTPUT_BYTES",
7665
+ "MODA_AGENT_OUTPUT_DIR",
7666
+ "MODA_AGENT_OUTPUT_RETENTION_HOURS",
7662
7667
  "MODA_CLI_TELEMETRY",
7663
7668
  "MODA_CLI_UPDATE_CHECK",
7664
7669
  "MODA_CLI_TIPS"
@@ -7761,7 +7766,7 @@ function parseArgs(argv) {
7761
7766
  if (!endOfOptions && arg === "--" && command !== "") {
7762
7767
  endOfOptions = true;
7763
7768
  } else if (!endOfOptions && arg.startsWith("--")) {
7764
- i = parseFlag(args, i, flags, command === "", bareFlags);
7769
+ i = parseFlag(args, i, flags, command === "", bareFlags, command);
7765
7770
  } else if (!endOfOptions && command !== "" && /^-[A-Za-z]/.test(arg)) {
7766
7771
  shortFlags.push(arg);
7767
7772
  } else if (command === "") {
@@ -7801,7 +7806,10 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
7801
7806
  "tui",
7802
7807
  "no-tui"
7803
7808
  ]);
7804
- function parseFlag(args, index, flags, beforeCommand, bareFlags) {
7809
+ var COMMAND_BOOLEAN_FLAGS = {
7810
+ context: new Set(["all", "metadata-only"])
7811
+ };
7812
+ function parseFlag(args, index, flags, beforeCommand, bareFlags, command = "") {
7805
7813
  const set = (key2, value, bare) => {
7806
7814
  flags[key2] = value;
7807
7815
  const at = bareFlags.indexOf(key2);
@@ -7822,6 +7830,10 @@ function parseFlag(args, index, flags, beforeCommand, bareFlags) {
7822
7830
  set(key, "true", true);
7823
7831
  return index;
7824
7832
  }
7833
+ if (!beforeCommand && COMMAND_BOOLEAN_FLAGS[command]?.has(key.replace(/_/g, "-")) && next !== "true" && next !== "false") {
7834
+ set(key, "true", true);
7835
+ return index;
7836
+ }
7825
7837
  if (next !== undefined && !next.startsWith("--")) {
7826
7838
  set(key, next, false);
7827
7839
  return index + 1;
@@ -7914,7 +7926,7 @@ Commands:
7914
7926
  traces --sort=longest Sort traces: recent (default), longest, slowest
7915
7927
  search "<query>" Search trace messages (keyword/semantic/hybrid)
7916
7928
  world-state <conversation_id> Get a trace's world state (slots/threads/events)
7917
- context <conversation_id> Get windowed trace context
7929
+ context <conversation_id> Get windowed trace context (--metadata-only: summary + counts only)
7918
7930
  context <conversation_id> --all Whole transcript in order (first 500; --from=N --max-messages=N to continue)
7919
7931
  audit <conversation_id|trace_id> Raw span audit (spans, hierarchy, orphans, duplicates)
7920
7932
  frustrations Frustration detections (same data as emotions --family=frustration)
@@ -7922,8 +7934,7 @@ Commands:
7922
7934
  hallucinations Grounding detections: contradicted/verified outputs with rule breakdown
7923
7935
  laziness Laziness detections per trace (repeated calls, premature handoffs, ...)
7924
7936
  tool-failures Get tool failure overview
7925
- tool-failure-detail <tool_name> Get per-tool failure detail (--limit=N examples, 1-20, default 5;
7926
- long example text is cut to 300 chars)
7937
+ tool-failure-detail <tool_name> Get per-tool failure detail (--limit=N examples, 1-20, default 5)
7927
7938
  problems Rank cross-signal Problems by root cause (what to fix first)
7928
7939
  problems remainder Signals no Problem explains yet (--family, --cursor)
7929
7940
  problem-impact Live problem/use-case impact (--node-id, --problem-id, --mode, --days-back)
@@ -8748,7 +8759,7 @@ function parseCommandFlags(schema, args, command, options = {}) {
8748
8759
  ]);
8749
8760
  const unknown = Object.keys(args).filter((key) => !known.has(key));
8750
8761
  if (unknown.includes("dry_run")) {
8751
- throw new CliInputError(`'moda ${command}' has no dry-run mode, so nothing was sent.`, `Remove --dry-run to run it for real. Commands with a real dry-run: sync, registry push, harness sync/delete, prompts sync/decide, skills sync/policy, signals <write subcommands>, problem-reopen.`);
8762
+ throw new CliInputError(`'moda ${command}' has no dry-run mode, so nothing was sent.`, `Remove --dry-run to run it for real. Commands with a real dry-run: sync, registry push, harness sync/delete, prompts sync/decide, skills sync/policy, signals <write subcommands>, problem-reopen.`, "unsupported_dry_run");
8752
8763
  }
8753
8764
  if (unknown.length > 0) {
8754
8765
  const flagKeys = schemaKeys.filter((key) => key !== options.positionalKey);
@@ -8763,7 +8774,7 @@ function parseCommandFlags(schema, args, command, options = {}) {
8763
8774
  return close ? `Did you mean --${close.candidate.replace(/_/g, "-")}?` : "";
8764
8775
  }).filter(Boolean);
8765
8776
  const names = unknown.map((key) => `--${key.replace(/_/g, "-")}`).join(", ");
8766
- throw new CliInputError(`Unknown flag${unknown.length > 1 ? "s" : ""} ${names} for 'moda ${command}'.`, [...hints, `Valid flags: ${valid.join(", ") || "(none)"}. Run 'moda ${command} --help'.`].join(" "));
8777
+ throw new CliInputError(`Unknown flag${unknown.length > 1 ? "s" : ""} ${names} for 'moda ${command}'.`, [...hints, `Valid flags: ${valid.join(", ") || "(none)"}. Run 'moda ${command} --help'.`].join(" "), `unknown_flag:${unknown[0].replace(/_/g, "-")}`);
8767
8778
  }
8768
8779
  return schema.parse(args);
8769
8780
  }
@@ -8953,7 +8964,7 @@ function assertHandParsedFlags(command, flags, positionals) {
8953
8964
  } catch (error) {
8954
8965
  if (command !== "feedback" || !(error instanceof CliInputError))
8955
8966
  throw error;
8956
- throw new CliInputError(/nothing was sent/i.test(error.message) ? error.message : `${error.message} Nothing was sent.`, `${error.usage ?? ""} Id flags (--<name>-id=ID) are attached as refs; put other detail in the note. ${FEEDBACK_USAGE}`.trim());
8967
+ throw new CliInputError(/nothing was sent/i.test(error.message) ? error.message : `${error.message} Nothing was sent.`, `${error.usage ?? ""} Id flags (--<name>-id=ID) are attached as refs; put other detail in the note. ${FEEDBACK_USAGE}`.trim(), error.reason);
8957
8968
  }
8958
8969
  }
8959
8970
  var PROFILE_MANAGING_COMMANDS = new Set(["profiles", "auth", "init", "provision", "status", "config", "keys", "manifest", "agent", "doctor"]);
@@ -8987,6 +8998,66 @@ function processingDisabledWarning(data, family) {
8987
8998
  `${family === "emotion" ? "Emotion" : "Frustration"} analysis is disabled for this tenant (${setting} = false), ` + "so zero results do NOT mean users are not frustrated. Enable it in Settings → Account → Signal processing; " + "only traces ingested after enabling are analyzed."
8988
8999
  ];
8989
9000
  }
9001
+ var CONTEXT_USAGE = "Usage: moda context <conversation_id> [--msg-index=N] [--window=0-5 | --metadata-only]";
9002
+ var CONTEXT_ALL_USAGE = "Usage: moda context <conversation_id> --all [--from=N] [--max-messages=N]";
9003
+ var CONTEXT_MAX_WINDOW = 5;
9004
+ var EMPTY_ANCHOR_VALUES = new Set(["", "null", "undefined", "none"]);
9005
+ function parseContextRequest(positional, rawFlags) {
9006
+ const flags = {};
9007
+ const flagIds = [];
9008
+ for (const [rawKey, value] of Object.entries(rawFlags)) {
9009
+ const key = rawKey.replace(/_/g, "-");
9010
+ if (key === "conversation-id") {
9011
+ if (value !== "true")
9012
+ flagIds.push(value);
9013
+ } else if (flags[key] !== undefined && flags[key] !== value) {
9014
+ throw new CliInputError(`Got two different values for --${key} (both spellings were passed), so nothing was read.`, CONTEXT_USAGE, "flag_conflict");
9015
+ } else {
9016
+ flags[key] = value;
9017
+ }
9018
+ }
9019
+ const flagId = flagIds[0];
9020
+ const warnings = [];
9021
+ if (new Set([...positional !== undefined ? [positional] : [], ...flagIds]).size > 1) {
9022
+ throw new CliInputError("Got two different trace ids (from the positional and --conversation-id), so nothing was read.", CONTEXT_USAGE, "flag_conflict");
9023
+ }
9024
+ const conversationId = positional ?? flagId;
9025
+ if (!conversationId) {
9026
+ throw new CliInputError("<conversation_id> is required", CONTEXT_USAGE, "missing_positional");
9027
+ }
9028
+ const msgIndex = flags["msg-index"];
9029
+ if (msgIndex !== undefined && EMPTY_ANCHOR_VALUES.has(msgIndex.trim().toLowerCase())) {
9030
+ delete flags["msg-index"];
9031
+ warnings.push(`--msg-index had no value (${JSON.stringify(msgIndex)}), so the window is centered on the middle of the trace. ` + "Trace-level search hits have no message_index; pass one only when the hit has it.");
9032
+ }
9033
+ const args = { ...flagsToArgs(flags), conversation_id: conversationId };
9034
+ const params = parseCommandFlags(ContextSchema, args, "context", { positionalKey: "conversation_id" });
9035
+ if (params.metadata_only === true && params.window !== undefined) {
9036
+ throw new CliInputError("--metadata-only reads no messages, so it cannot be combined with --window; drop --window.", CONTEXT_USAGE, "flag_conflict");
9037
+ }
9038
+ if (params.all === true) {
9039
+ if (params.msg_index !== undefined || flags.window !== undefined || params.metadata_only === true) {
9040
+ throw new CliInputError("--all reads the whole trace; drop --msg-index/--window/--metadata-only.", CONTEXT_ALL_USAGE, "flag_conflict");
9041
+ }
9042
+ } else if (params.from !== undefined || params.max_messages !== undefined) {
9043
+ throw new CliInputError("--from and --max-messages page the whole trace and need --all.", CONTEXT_ALL_USAGE, "flag_conflict");
9044
+ }
9045
+ return { params, warnings };
9046
+ }
9047
+ async function readContextMetadataOnly(contextPath, query2) {
9048
+ try {
9049
+ return await callDataAPI(`${contextPath}?${query2.toString()}`);
9050
+ } catch (error) {
9051
+ if (!(error instanceof ApiError) || error.statusCode !== 400 || !/metadata_only/.test(error.message))
9052
+ throw error;
9053
+ const legacy = new URLSearchParams(query2);
9054
+ legacy.delete("metadata_only");
9055
+ legacy.set("window", "0");
9056
+ const data = asRecord(await callDataAPI(`${contextPath}?${legacy.toString()}`)) ?? {};
9057
+ const ctx = asRecord(data.context) ?? {};
9058
+ return { ...data, metadata_only: true, context: { ...ctx, messages: [] } };
9059
+ }
9060
+ }
8990
9061
  var FULL_TRANSCRIPT_DEFAULT_MAX_MESSAGES = 500;
8991
9062
  var CONTEXT_RANGE_PAGE_SIZE = 200;
8992
9063
  var LEGACY_WINDOW_MAX_MESSAGES = 100;
@@ -9013,7 +9084,6 @@ function pageMessages(page) {
9013
9084
  const messages = asRecord(page.context)?.messages ?? page.messages;
9014
9085
  return (Array.isArray(messages) ? messages : []).map((m) => asRecord(m) ?? {});
9015
9086
  }
9016
- var CONTEXT_MAX_WINDOW = 5;
9017
9087
  var CONTEXT_ALL_MAX_MESSAGES = 5000;
9018
9088
  async function readFullTranscript(conversationId, opts = {}) {
9019
9089
  const start = opts.from ?? 0;
@@ -9372,40 +9442,37 @@ async function runCommand(command, positional, flags, positionals = positional ?
9372
9442
  break;
9373
9443
  }
9374
9444
  case "context": {
9375
- if (!positional) {
9376
- throw new CliInputError("<conversation_id> is required", "Usage: moda context <conversation_id> [--msg-index=N] [--window=N]");
9377
- }
9378
- const args = flagsToArgs(flags, "conversation_id", positional);
9379
- const params = parseCommandFlags(ContextSchema, args, command, { positionalKey: "conversation_id" });
9445
+ const { params, warnings: inputWarnings } = parseContextRequest(positional, flags);
9380
9446
  if (params.all === true) {
9381
- if (params.msg_index !== undefined || flags.window !== undefined) {
9382
- throw new CliInputError("--all reads the whole trace; drop --msg-index/--window.", "Usage: moda context <conversation_id> --all [--from=N] [--max-messages=N]");
9383
- }
9384
9447
  const agentMode = context.output.mode === "agent" || context.output.mode === "agent-stream";
9385
9448
  const full = await readFullTranscript(params.conversation_id, {
9386
9449
  from: params.from,
9387
9450
  maxMessages: params.max_messages,
9388
9451
  fits: agentMode ? (page) => agentEnvelopeFits(command, page.data, { warnings: page.warnings, nextCommands: page.nextCommands }) : undefined
9389
9452
  });
9453
+ const fullWarnings = [...inputWarnings, ...full.warnings];
9390
9454
  context.output.writeData(full.data, {
9391
- ...full.warnings.length > 0 ? { warnings: full.warnings } : {},
9455
+ ...fullWarnings.length > 0 ? { warnings: fullWarnings } : {},
9392
9456
  ...full.nextCommands.length > 0 ? { nextCommands: full.nextCommands } : {}
9393
9457
  });
9394
9458
  break;
9395
9459
  }
9396
- if (params.from !== undefined || params.max_messages !== undefined) {
9397
- throw new CliInputError("--from and --max-messages page the whole trace and need --all.", "Usage: moda context <conversation_id> --all [--from=N] [--max-messages=N]");
9398
- }
9399
9460
  const query2 = new URLSearchParams;
9400
9461
  if (params.msg_index !== undefined)
9401
9462
  query2.set("msg_index", params.msg_index.toString());
9463
+ if (params.metadata_only === true)
9464
+ query2.set("metadata_only", "1");
9402
9465
  const window = params.window === undefined ? undefined : Math.min(params.window, CONTEXT_MAX_WINDOW);
9403
9466
  if (window !== undefined)
9404
9467
  query2.set("window", window.toString());
9405
9468
  const queryString = query2.toString() ? `?${query2.toString()}` : "";
9406
- const data = await callDataAPI(`/conversations/${params.conversation_id}/context${queryString}`);
9469
+ const contextPath = `/conversations/${encodeURIComponent(params.conversation_id)}/context`;
9470
+ const data = params.metadata_only === true ? await readContextMetadataOnly(contextPath, query2) : await callDataAPI(`${contextPath}${queryString}`);
9407
9471
  const ctxRecord = asRecord(data) ?? {};
9408
- const warnings = notFoundWarning("trace", params.conversation_id, asNumber(ctxRecord.total_messages) === 0);
9472
+ const warnings = [
9473
+ ...inputWarnings,
9474
+ ...notFoundWarning("trace", params.conversation_id, asNumber(ctxRecord.total_messages) === 0)
9475
+ ];
9409
9476
  const nextCommands = [];
9410
9477
  if (params.window !== undefined && params.window > CONTEXT_MAX_WINDOW) {
9411
9478
  const center = asNumber(asRecord(ctxRecord.context)?.center_index) ?? params.msg_index ?? 0;
@@ -9531,11 +9598,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
9531
9598
  trimmed = { ...trimmed, subtypes: ranked.slice(0, TOOL_FAILURE_AGENT_SUBTYPES), subtypes_total: subtypes.length };
9532
9599
  out.push(`Showing the ${TOOL_FAILURE_AGENT_SUBTYPES} most frequent of ${subtypes.length} failure subtypes; --json lists all of them.`);
9533
9600
  }
9534
- const { data: compacted, truncated } = compactToolFailureExamples(trimmed);
9535
- if (truncated > 0) {
9536
- out.push(`${truncated} long text field(s) in examples were cut to ${TOOL_FAILURE_TEXT_LIMIT} characters to keep this output small. For the full text, run each example's \`context_command\` (\`moda context <conversation_id> --msg-index=<msg_index>\`).`);
9537
- }
9538
- return compacted;
9601
+ return trimmed;
9539
9602
  });
9540
9603
  break;
9541
9604
  }
@@ -10163,7 +10226,7 @@ var commandRegistry = createCommandRegistry([
10163
10226
  resetApiRequestCountBeforeRun: true,
10164
10227
  telemetry: "result",
10165
10228
  handler: async (context) => {
10166
- const { runInit } = await import("./index-pdg9t77n.js");
10229
+ const { runInit } = await import("./index-pykaaezb.js");
10167
10230
  if (context.outputMode === "agent-stream") {
10168
10231
  context.output.writeEvent({
10169
10232
  event: "started",
@@ -10204,7 +10267,7 @@ var commandRegistry = createCommandRegistry([
10204
10267
  resetApiRequestCountBeforeRun: true,
10205
10268
  telemetry: "result-and-error",
10206
10269
  handler: async (context) => {
10207
- const { runProvision } = await import("./provision-7fhbgk2d.js");
10270
+ const { runProvision } = await import("./provision-d1dfj6as.js");
10208
10271
  return runProvision(context, {
10209
10272
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
10210
10273
  });
@@ -10668,6 +10731,7 @@ var commandRegistry = createCommandRegistry([
10668
10731
  examples: [
10669
10732
  "moda context <conversation_id> --window=3",
10670
10733
  "moda context <conversation_id> --msg-index=40 --window=0",
10734
+ "moda context <conversation_id> --metadata-only # summary + total_messages, no messages",
10671
10735
  "moda context <conversation_id> --all",
10672
10736
  "moda context <conversation_id> --all --from=500 --max-messages=500"
10673
10737
  ],
@@ -10701,7 +10765,7 @@ var commandRegistry = createCommandRegistry([
10701
10765
  }),
10702
10766
  legacyCommand({
10703
10767
  name: "tool-failure-detail",
10704
- description: "Get per-tool failure detail (each example embeds its anchor ±2 messages, long text cut to 300 chars)",
10768
+ description: "Get per-tool failure detail (each example embeds its anchor ±2 messages)",
10705
10769
  examples: [
10706
10770
  "moda tool-failure-detail search",
10707
10771
  "moda tool-failure-detail search --compact # no embedded conversation (same as --include-window=false)",
@@ -11078,7 +11142,15 @@ var commandRegistry = createCommandRegistry([
11078
11142
  }
11079
11143
  ]);
11080
11144
  function classifyCliError(error) {
11081
- if (error instanceof z2.ZodError || error instanceof CliInputError || error instanceof CliInputRequiredError || error instanceof CliAuthError || error instanceof CliAccessError) {
11145
+ if (error instanceof CliAuthError)
11146
+ return "auth_error";
11147
+ if (error instanceof CliAccessError)
11148
+ return "access_error";
11149
+ if (error instanceof ApiError && error.statusCode === 401)
11150
+ return "auth_error";
11151
+ if (error instanceof ApiError && error.statusCode === 403)
11152
+ return "access_error";
11153
+ if (error instanceof z2.ZodError || error instanceof CliInputError || error instanceof CliInputRequiredError) {
11082
11154
  return "validation_error";
11083
11155
  }
11084
11156
  if (error instanceof ApiError || isApiLikeError(error)) {
@@ -11089,6 +11161,21 @@ function classifyCliError(error) {
11089
11161
  }
11090
11162
  return "unknown_error";
11091
11163
  }
11164
+ function cliValidationReason(error) {
11165
+ if (error instanceof z2.ZodError) {
11166
+ const issue = error.issues[0];
11167
+ if (!issue)
11168
+ return "zod";
11169
+ const path = issue.path.join(".") || "_";
11170
+ return issue.code === "invalid_enum_value" ? `bad_enum:${path}` : `zod:${path}:${issue.code}`;
11171
+ }
11172
+ if (error instanceof CliInputRequiredError)
11173
+ return "input_required";
11174
+ if (error instanceof CliInputError) {
11175
+ return error.reason ?? (/^<[^>]+> is required/.test(error.message) ? "missing_positional" : "input_error");
11176
+ }
11177
+ return;
11178
+ }
11092
11179
  function printCliError(error) {
11093
11180
  if (error instanceof CliInputError) {
11094
11181
  safeWriteStderr(`Error: ${error.message}
@@ -11272,6 +11359,7 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
11272
11359
  flags: commandFlags,
11273
11360
  status: "error",
11274
11361
  errorType: classifyCliError(error),
11362
+ validationReason: cliValidationReason(error),
11275
11363
  exitCode: exitCodeForError(error),
11276
11364
  durationMs: Date.now() - startedAt,
11277
11365
  apiRequestCount: getApiRequestCount(),
@@ -11332,6 +11420,7 @@ async function dispatchParsedCommandInner(parsed, startedAt) {
11332
11420
  flags: commandFlags,
11333
11421
  status: "error",
11334
11422
  errorType: classifyCliError(error),
11423
+ validationReason: cliValidationReason(error),
11335
11424
  exitCode: exitCodeForError(error),
11336
11425
  durationMs: Date.now() - startedAt,
11337
11426
  apiRequestCount: getApiRequestCount(),
@@ -11399,6 +11488,7 @@ export {
11399
11488
  runCommand,
11400
11489
  resolveTailSignal,
11401
11490
  printCliError,
11491
+ parseContextRequest,
11402
11492
  parseCommandFlags,
11403
11493
  parseArgs,
11404
11494
  initFlagsForContext,
@@ -11407,6 +11497,7 @@ export {
11407
11497
  detectCallerAgent,
11408
11498
  describeLinearLink,
11409
11499
  commandRegistry,
11500
+ cliValidationReason,
11410
11501
  classifyCliError,
11411
11502
  capProblemsBlocks,
11412
11503
  buildResumeCommand,
@@ -11414,5 +11505,6 @@ export {
11414
11505
  TAIL_LIMITS,
11415
11506
  LEGACY_WINDOW_MAX_MESSAGES,
11416
11507
  FULL_TRANSCRIPT_DEFAULT_MAX_MESSAGES,
11417
- CliInputError
11508
+ CliInputError,
11509
+ CONTEXT_MAX_WINDOW
11418
11510
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildSourceSnapshot
3
- } from "./cli-m3bg9yyh.js";
4
- import"./cli-bqhs6sk7.js";
3
+ } from "./cli-he2wb4hd.js";
4
+ import"./cli-ac3p2byq.js";
5
5
  import"./cli-0v6na3yp.js";
6
6
 
7
7
  // src/harness-github-actions.ts
@@ -42,8 +42,8 @@ import {
42
42
  writeHarnessAnalyzeResult,
43
43
  writeHarnessArtifacts,
44
44
  writeHumanProgress
45
- } from "./cli-m3bg9yyh.js";
46
- import"./cli-bqhs6sk7.js";
45
+ } from "./cli-he2wb4hd.js";
46
+ import"./cli-ac3p2byq.js";
47
47
  import"./cli-0v6na3yp.js";
48
48
  export {
49
49
  writeHumanProgress,
@@ -3,7 +3,7 @@ import {
3
3
  initPrompts,
4
4
  runPromptSync,
5
5
  runSkillSync
6
- } from "./cli-6xndg2h4.js";
6
+ } from "./cli-ha40mgy1.js";
7
7
  import {
8
8
  codingAgentDisplayName,
9
9
  describeCodingAgentEvent,
@@ -22,7 +22,7 @@ import {
22
22
  runHarnessCommand,
23
23
  startRemoteAnalyze,
24
24
  stripAnsi
25
- } from "./cli-m3bg9yyh.js";
25
+ } from "./cli-he2wb4hd.js";
26
26
  import {
27
27
  bundledSkillsDir,
28
28
  installIntegrationSkill,
@@ -34,14 +34,14 @@ import {
34
34
  } from "./cli-ssfq84k5.js";
35
35
  import {
36
36
  selectTenantAndCreateKey
37
- } from "./cli-taf854xx.js";
37
+ } from "./cli-sq6g0tk5.js";
38
38
  import {
39
39
  ConnectCodeError,
40
40
  connectWithCode,
41
41
  loadAuthSession,
42
42
  loadToken,
43
43
  login
44
- } from "./cli-c64d2jrw.js";
44
+ } from "./cli-kxwm7y62.js";
45
45
  import {
46
46
  CliAuthError,
47
47
  CliInputError,
@@ -49,7 +49,7 @@ import {
49
49
  createCommandContext,
50
50
  resolveIngestUrl,
51
51
  resolveModaBaseUrl
52
- } from "./cli-bqhs6sk7.js";
52
+ } from "./cli-ac3p2byq.js";
53
53
  import"./cli-0v6na3yp.js";
54
54
 
55
55
  // src/init/index.ts
@@ -5,8 +5,8 @@ import {
5
5
  inspectCodeSelectors,
6
6
  inspectPythonSelectors,
7
7
  safePromptEvidencePath
8
- } from "./cli-m3bg9yyh.js";
9
- import"./cli-bqhs6sk7.js";
8
+ } from "./cli-he2wb4hd.js";
9
+ import"./cli-ac3p2byq.js";
10
10
  import {
11
11
  __commonJS,
12
12
  __toESM
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  selectTenantAndCreateKey
3
- } from "./cli-taf854xx.js";
3
+ } from "./cli-sq6g0tk5.js";
4
4
  import {
5
5
  loadAuthSession
6
- } from "./cli-c64d2jrw.js";
6
+ } from "./cli-kxwm7y62.js";
7
7
  import {
8
8
  CliAuthError,
9
9
  resolveIngestUrl,
10
10
  resolveModaBaseUrl,
11
11
  stringOption
12
- } from "./cli-bqhs6sk7.js";
12
+ } from "./cli-ac3p2byq.js";
13
13
  import"./cli-0v6na3yp.js";
14
14
 
15
15
  // src/provision.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.49.0",
3
+ "version": "1.52.0",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "moda.skill_index.v1",
3
- "bundled_at": "2026-10-10T08:40:09.772Z",
4
- "cli_version": "1.49.0",
3
+ "bundled_at": "2026-10-12T06:24:12.441Z",
4
+ "cli_version": "1.52.0",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-cloudflare-think",