@moda-ai/cli 1.54.0 → 1.55.1

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
@@ -410,6 +410,7 @@ var SearchSchema = z.object({
410
410
  q: z.string().min(1).max(500),
411
411
  mode: z.enum(SEARCH_MODES).default("hybrid").optional(),
412
412
  user_id: z.string().optional(),
413
+ conversation_id: z.string().min(1).max(256).optional(),
413
414
  time_range: z.enum(TIME_RANGES).default("all").optional(),
414
415
  limit: z.number().min(1).max(100).default(20).optional(),
415
416
  include_tool_io: boolFlag(),
@@ -8049,6 +8050,7 @@ Traces flags:
8049
8050
  Search flags:
8050
8051
  --mode=keyword|semantic|hybrid Search mode (default hybrid)
8051
8052
  --user-id=ID Filter to a single user
8053
+ --conversation-id=ID Search inside one trace only
8052
8054
  --time-range=RANGE Time range filter (all|1h|3d|7d|24h|30d|90d)
8053
8055
  --limit=N Maximum results to return (1-100, default 20)
8054
8056
 
@@ -8808,6 +8810,23 @@ function shellArg(value) {
8808
8810
  return `"${value}"`;
8809
8811
  return `'${value.replace(/'/g, `'\\''`)}'`;
8810
8812
  }
8813
+ function searchCoverageWarnings(coverage, timeRange) {
8814
+ const record = asRecord(coverage);
8815
+ if (!record)
8816
+ return [];
8817
+ const warnings = [];
8818
+ const freshness = asRecord(record.index_freshness);
8819
+ const originalUsed = record.original_messages_search === "used" || record.keyword_source === "original_messages";
8820
+ if (freshness?.window_extends_beyond_index === true) {
8821
+ const indexedAt = asString(freshness.latest_indexed_message_at) ?? "none in the last 7 days";
8822
+ const originalAt = asString(freshness.latest_original_message_at) ?? "unknown";
8823
+ warnings.push(`The search index trails ingestion for --time-range=${timeRange}: newest indexed message ${indexedAt}, newest message ${originalAt}. ` + (originalUsed ? "Literal matches were read from the original messages, but semantic ranking cannot see messages after the index cutoff." : "Messages after the index cutoff are missing from these results; retry with --mode=keyword for exact text."));
8824
+ }
8825
+ if (record.results_only_from_original_messages === true) {
8826
+ warnings.push("Every result came from the original-message fallback: the search index returned no literal match (it may lag ingestion or not tokenize this text). Scores are literal match counts.");
8827
+ }
8828
+ return warnings;
8829
+ }
8811
8830
  async function searchFallbackToTraces(params) {
8812
8831
  const limit = Math.min(params.limit ?? 20, 100);
8813
8832
  const offset = params.offset ?? 0;
@@ -9354,7 +9373,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
9354
9373
  }
9355
9374
  case "search": {
9356
9375
  if (!positional || positional.trim() === "") {
9357
- throw new CliInputError("<query> is required", 'Usage: moda search "<query>" [--mode=keyword|semantic|hybrid] [--user-id=ID] [--time-range=RANGE] [--include-tool-io] [--limit=N]');
9376
+ throw new CliInputError("<query> is required", 'Usage: moda search "<query>" [--mode=keyword|semantic|hybrid] [--user-id=ID] [--conversation-id=ID] [--time-range=RANGE] [--include-tool-io] [--limit=N]');
9358
9377
  }
9359
9378
  const args = withStringIdFlags(flagsToArgs(flags, "q", positional), flags);
9360
9379
  const params = parseCommandFlags(SearchSchema, args, command, { positionalKey: "q" });
@@ -9364,6 +9383,8 @@ async function runCommand(command, positional, flags, positionals = positional ?
9364
9383
  query2.set("mode", params.mode);
9365
9384
  if (params.user_id)
9366
9385
  query2.set("user_id", params.user_id);
9386
+ if (params.conversation_id)
9387
+ query2.set("conversation_id", params.conversation_id);
9367
9388
  if (params.time_range)
9368
9389
  query2.set("time_range", params.time_range);
9369
9390
  if (params.limit)
@@ -9381,11 +9402,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
9381
9402
  } else if (params.mode && actualMode && actualMode !== params.mode && params.mode !== "hybrid") {
9382
9403
  warnings.push(`Search ran in ${actualMode} mode instead of the requested ${params.mode} mode.`);
9383
9404
  }
9405
+ warnings.push(...searchCoverageWarnings(searchRecord.coverage, params.time_range ?? "all"));
9384
9406
  const results = Array.isArray(searchRecord.results) ? searchRecord.results : [];
9385
9407
  if (results.length > 0 && (params.offset ?? 0) > 0) {
9386
9408
  warnings.push(`--offset=${params.offset} was not applied: message-level search is not pageable (it returns the top --limit hits). ` + "--offset only pages the trace-level fallback. Narrow with --time-range/--user-id or raise --limit instead.");
9387
9409
  }
9388
- if (results.length === 0) {
9410
+ if (results.length === 0 && !params.conversation_id) {
9389
9411
  const fallback = await searchFallbackToTraces({ ...params, offset: params.offset });
9390
9412
  if (fallback?.kind === "notice") {
9391
9413
  warnings.push(fallback.warning);
@@ -11511,6 +11533,7 @@ if (isMain) {
11511
11533
  export {
11512
11534
  telemetryCommandName,
11513
11535
  stripLoneSurrogates,
11536
+ searchCoverageWarnings,
11514
11537
  runCommand,
11515
11538
  resolveTailSignal,
11516
11539
  printCliError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.54.0",
3
+ "version": "1.55.1",
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-12T07:41:12.130Z",
4
- "cli_version": "1.54.0",
3
+ "bundled_at": "2026-10-13T00:08:16.766Z",
4
+ "cli_version": "1.55.1",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-cloudflare-think",
@@ -506,6 +506,7 @@ moda search "customer asked for a refund" --mode=semantic
506
506
  moda search "SEARCH_NO_RESULTS" --mode=keyword
507
507
  moda search "checkout error" --time-range=7d --limit=10
508
508
  moda search "stripe.charges.create failed" --user-id=<id>
509
+ moda search "Иван" --conversation-id=<id> --mode=keyword # inside one trace
509
510
  ```
510
511
 
511
512
  Searches trace messages at message grain. Pass `--include-tool-io` to
@@ -514,9 +515,17 @@ output previews), so it finds where an agent *did* something, not just where
514
515
  it talked about it — tool-IO search is opt-in, not the default.
515
516
 
516
517
  Flags: `--mode=keyword|semantic|hybrid` (default `hybrid`), `--user-id`,
518
+ `--conversation-id` (search inside one trace; no trace fallback),
517
519
  `--time-range` (`all|1h|3d|7d|24h|30d|90d`, default `all`), `--limit` (1–100,
518
520
  default 20). The query is a positional arg (max 500 chars).
519
521
 
522
+ **Index freshness.** Keyword matches (and hybrid's keyword half) fall back to
523
+ the original messages when the search index returns too few literal matches,
524
+ so recent, Cyrillic and partial-word text is still found. `coverage` reports
525
+ which source served the keyword leg (`keyword_source`) and how far the index
526
+ trails ingestion (`index_freshness`); a warning says so when your
527
+ `--time-range` reaches past the newest indexed message.
528
+
520
529
  **Fallback.** If message-level search finds nothing but whole traces match
521
530
  (same as `moda traces --search`), those traces come back in `results` with
522
531
  `match_grain: "trace"`, plus `fallback.source: "traces_search"` and a warning.