@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 +25 -2
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +9 -0
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
package/skills/moda-cli/SKILL.md
CHANGED
|
@@ -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.
|