@moda-ai/cli 1.41.2 → 1.42.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 +341 -137
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +39 -0
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
HARNESS_REPORT_PATH,
|
|
16
16
|
SIGNALS_MANAGEMENT_SUBCOMMANDS,
|
|
17
17
|
assertFlags,
|
|
18
|
+
assertPositionals,
|
|
18
19
|
callControlAPI,
|
|
19
20
|
callDataAPI,
|
|
20
21
|
getApiRequestCount,
|
|
@@ -2500,6 +2501,183 @@ function isDefined(value) {
|
|
|
2500
2501
|
return value !== undefined;
|
|
2501
2502
|
}
|
|
2502
2503
|
|
|
2504
|
+
// src/replays.ts
|
|
2505
|
+
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2506
|
+
var PLAYOUT_ID_RE = /^[0-9a-f]{64}$/i;
|
|
2507
|
+
var ARMS = ["prod", "proposed"];
|
|
2508
|
+
var REPLAYS_SUBCOMMANDS = ["sets", "runs", "run", "playouts", "playout"];
|
|
2509
|
+
var REPLAYS_SUBCOMMAND_HELP = [
|
|
2510
|
+
{
|
|
2511
|
+
name: "sets",
|
|
2512
|
+
description: "List replay sets (ids, names, case counts)",
|
|
2513
|
+
usage: "moda replays sets",
|
|
2514
|
+
examples: ["moda replays sets --json"]
|
|
2515
|
+
},
|
|
2516
|
+
{
|
|
2517
|
+
name: "runs",
|
|
2518
|
+
description: "Run history for a set, newest first, every status",
|
|
2519
|
+
usage: "moda replays runs <set_id> [--limit=N]",
|
|
2520
|
+
flags: [{ name: "--limit=N", description: "Runs to return (default 20, max 100)." }],
|
|
2521
|
+
examples: ["moda replays runs <set_id> --limit=5 --json"]
|
|
2522
|
+
},
|
|
2523
|
+
{
|
|
2524
|
+
name: "run",
|
|
2525
|
+
description: "One run: summary, per-case aggregate, every case x arm x seed verdict, playout count",
|
|
2526
|
+
usage: "moda replays run <set_id> <run_id|latest>",
|
|
2527
|
+
examples: ["moda replays run <set_id> <run_id> --json", "moda replays run <set_id> latest --json"]
|
|
2528
|
+
},
|
|
2529
|
+
{
|
|
2530
|
+
name: "playouts",
|
|
2531
|
+
description: "Playouts of a run (one per case x arm x seed) with verdicts; --transcript inlines what was said and every tool call",
|
|
2532
|
+
usage: "moda replays playouts <set_id> <run_id> [--case=<case_id>] [--arm=prod|proposed] [--seed=N] [--transcript] [--max-chars=N] [--limit=N] [--cursor=<next_cursor>]",
|
|
2533
|
+
flags: [
|
|
2534
|
+
{ name: "--case=<case_id>", description: "Only this case." },
|
|
2535
|
+
{ name: "--arm=prod|proposed", description: "Only this arm (prod = baseline, proposed = candidate)." },
|
|
2536
|
+
{ name: "--seed=N", description: "Only this seed." },
|
|
2537
|
+
{ name: "--transcript", description: "Inline each transcript (messages, tool calls with served output and provenance, criteria). Page cap 10." },
|
|
2538
|
+
{ name: "--max-chars=N", description: "Per-field cap for message text and tool JSON (default 4000, 100-32000)." },
|
|
2539
|
+
{ name: "--limit=N", description: "Page size (default 50, max 200; 10 with --transcript)." },
|
|
2540
|
+
{ name: "--cursor=<next_cursor>", description: "Next page: the previous response's nextCursor." }
|
|
2541
|
+
],
|
|
2542
|
+
examples: [
|
|
2543
|
+
"moda replays playouts <set_id> <run_id> --json",
|
|
2544
|
+
"moda replays playouts <set_id> <run_id> --case=<case_id> --arm=proposed --seed=1 --transcript --json"
|
|
2545
|
+
]
|
|
2546
|
+
},
|
|
2547
|
+
{
|
|
2548
|
+
name: "playout",
|
|
2549
|
+
description: "One playout with its full transcript",
|
|
2550
|
+
usage: "moda replays playout <set_id> <run_id> <playout_id> [--max-chars=N]",
|
|
2551
|
+
flags: [{ name: "--max-chars=N", description: "Per-field cap (default 4000, 100-32000)." }],
|
|
2552
|
+
examples: ["moda replays playout <set_id> <run_id> <playout_id> --json"]
|
|
2553
|
+
}
|
|
2554
|
+
];
|
|
2555
|
+
function profileOptions(context) {
|
|
2556
|
+
return { profile: context.profile, env: context.env };
|
|
2557
|
+
}
|
|
2558
|
+
function requireTenant(context) {
|
|
2559
|
+
const key = resolveApiKey(profileOptions(context));
|
|
2560
|
+
if (!key) {
|
|
2561
|
+
throw new CliInputError("No Moda API key found for moda replays.", "Set MODA_API_KEY, run `moda auth login --api-key`, or select a profile with an API key.");
|
|
2562
|
+
}
|
|
2563
|
+
const keyTenant = embeddedTenantIdFromApiKey(key);
|
|
2564
|
+
const flagTenant = context.flags["tenant-id"];
|
|
2565
|
+
if (flagTenant && keyTenant && flagTenant !== keyTenant) {
|
|
2566
|
+
throw new CliInputError(`--tenant-id=${flagTenant} does not match this API key's tenant (${keyTenant}), so nothing was sent.`, "Drop --tenant-id (the key names its tenant) or use a key for that tenant.");
|
|
2567
|
+
}
|
|
2568
|
+
const tenantId = flagTenant || resolveApiTenantId(profileOptions(context));
|
|
2569
|
+
if (!tenantId) {
|
|
2570
|
+
throw new CliInputError("Could not determine the tenant.", "Use a signed API key (it names its tenant), set MODA_TENANT_ID, or pass --tenant-id=<id>.");
|
|
2571
|
+
}
|
|
2572
|
+
return tenantId;
|
|
2573
|
+
}
|
|
2574
|
+
function intFlag(name, value, min, max) {
|
|
2575
|
+
if (value === undefined)
|
|
2576
|
+
return;
|
|
2577
|
+
const parsed = Number(value);
|
|
2578
|
+
if (!Number.isInteger(parsed) || parsed < min || parsed > max) {
|
|
2579
|
+
throw new CliInputError(`--${name} must be an integer from ${min} to ${max} (got '${value}').`);
|
|
2580
|
+
}
|
|
2581
|
+
return String(parsed);
|
|
2582
|
+
}
|
|
2583
|
+
function query(params) {
|
|
2584
|
+
const search = new URLSearchParams(Object.entries(params).filter((entry) => entry[1] !== undefined)).toString();
|
|
2585
|
+
return search ? `?${search}` : "";
|
|
2586
|
+
}
|
|
2587
|
+
function needArg(value, name, usage) {
|
|
2588
|
+
if (!value)
|
|
2589
|
+
throw new CliInputError(`<${name}> is required.`, `Usage: ${usage}`);
|
|
2590
|
+
return value;
|
|
2591
|
+
}
|
|
2592
|
+
function needRunId(value, usage, allowLatest = false) {
|
|
2593
|
+
const runId = needArg(value, "run_id", usage);
|
|
2594
|
+
if (allowLatest && runId === "latest")
|
|
2595
|
+
return runId;
|
|
2596
|
+
if (!UUID_RE2.test(runId)) {
|
|
2597
|
+
throw new CliInputError(`<run_id> must be a run UUID (got '${runId}').`, "List runs with `moda replays runs <set_id>`.");
|
|
2598
|
+
}
|
|
2599
|
+
return runId;
|
|
2600
|
+
}
|
|
2601
|
+
async function runReplaysCommand(context) {
|
|
2602
|
+
const [subcommand, ...args] = context.positionals;
|
|
2603
|
+
const flags = context.flags;
|
|
2604
|
+
const help = REPLAYS_SUBCOMMAND_HELP.find((entry) => entry.name === subcommand);
|
|
2605
|
+
if (!subcommand || !help) {
|
|
2606
|
+
throw new CliInputError(subcommand ? `Unknown replays command '${subcommand}'.` : "A replays subcommand is required.", `Use one of: ${REPLAYS_SUBCOMMANDS.join(", ")}. See \`moda replays --help\`.`);
|
|
2607
|
+
}
|
|
2608
|
+
const usage = help.usage;
|
|
2609
|
+
const allowed = {
|
|
2610
|
+
sets: [],
|
|
2611
|
+
runs: ["limit"],
|
|
2612
|
+
run: [],
|
|
2613
|
+
playouts: ["case", "arm", "seed", "transcript", "max-chars", "limit", "cursor"],
|
|
2614
|
+
playout: ["max-chars"]
|
|
2615
|
+
};
|
|
2616
|
+
const name = subcommand;
|
|
2617
|
+
assertFlags(`replays ${name}`, flags, [...allowed[name], "tenant-id"]);
|
|
2618
|
+
const maxArgs = { sets: 0, runs: 1, run: 2, playouts: 2, playout: 3 }[name];
|
|
2619
|
+
assertPositionals(`replays ${name}`, args, maxArgs, usage);
|
|
2620
|
+
const tenantId = requireTenant(context);
|
|
2621
|
+
const base = `/tenants/${encodeURIComponent(tenantId)}/replay-sets`;
|
|
2622
|
+
let endpoint;
|
|
2623
|
+
switch (name) {
|
|
2624
|
+
case "sets":
|
|
2625
|
+
endpoint = base;
|
|
2626
|
+
break;
|
|
2627
|
+
case "runs": {
|
|
2628
|
+
const setId = needArg(args[0], "set_id", usage);
|
|
2629
|
+
endpoint = `${base}/${encodeURIComponent(setId)}/runs${query({ limit: intFlag("limit", flags.limit, 1, 100) })}`;
|
|
2630
|
+
break;
|
|
2631
|
+
}
|
|
2632
|
+
case "run": {
|
|
2633
|
+
const setId = needArg(args[0], "set_id", usage);
|
|
2634
|
+
const runId = needRunId(args[1], usage, true);
|
|
2635
|
+
endpoint = runId === "latest" ? `${base}/${encodeURIComponent(setId)}/runs/latest` : `${base}/${encodeURIComponent(setId)}/runs/${encodeURIComponent(runId)}`;
|
|
2636
|
+
break;
|
|
2637
|
+
}
|
|
2638
|
+
case "playouts": {
|
|
2639
|
+
const setId = needArg(args[0], "set_id", usage);
|
|
2640
|
+
const runId = needRunId(args[1], usage);
|
|
2641
|
+
const arm = flags.arm;
|
|
2642
|
+
if (arm !== undefined && !ARMS.includes(arm)) {
|
|
2643
|
+
throw new CliInputError(`--arm must be one of ${ARMS.join(", ")} (got '${arm}').`);
|
|
2644
|
+
}
|
|
2645
|
+
if (flags.case !== undefined && (flags.case.length === 0 || flags.case.length > 128)) {
|
|
2646
|
+
throw new CliInputError("--case must be a case id of 1 to 128 characters.");
|
|
2647
|
+
}
|
|
2648
|
+
if (flags.cursor !== undefined && !/^\d{1,9}$/.test(flags.cursor)) {
|
|
2649
|
+
throw new CliInputError(`--cursor must be the nextCursor of a previous page (got '${flags.cursor}').`);
|
|
2650
|
+
}
|
|
2651
|
+
const transcript = flags.transcript === "true";
|
|
2652
|
+
endpoint = `${base}/${encodeURIComponent(setId)}/runs/${encodeURIComponent(runId)}/playouts${query({
|
|
2653
|
+
caseId: flags.case,
|
|
2654
|
+
arm,
|
|
2655
|
+
seed: intFlag("seed", flags.seed, 0, 1000),
|
|
2656
|
+
include: transcript ? "transcript" : undefined,
|
|
2657
|
+
maxChars: intFlag("max-chars", flags["max-chars"], 100, 32000),
|
|
2658
|
+
limit: intFlag("limit", flags.limit, 1, transcript ? 10 : 200),
|
|
2659
|
+
cursor: flags.cursor
|
|
2660
|
+
})}`;
|
|
2661
|
+
break;
|
|
2662
|
+
}
|
|
2663
|
+
case "playout": {
|
|
2664
|
+
const setId = needArg(args[0], "set_id", usage);
|
|
2665
|
+
const runId = needRunId(args[1], usage);
|
|
2666
|
+
const playoutId = needArg(args[2], "playout_id", usage);
|
|
2667
|
+
if (!PLAYOUT_ID_RE.test(playoutId)) {
|
|
2668
|
+
throw new CliInputError(`<playout_id> must be a 64-character hex id (got '${playoutId}').`, "List playouts with `moda replays playouts <set_id> <run_id>`.");
|
|
2669
|
+
}
|
|
2670
|
+
endpoint = `${base}/${encodeURIComponent(setId)}/runs/${encodeURIComponent(runId)}/playouts/${encodeURIComponent(playoutId)}${query({
|
|
2671
|
+
maxChars: intFlag("max-chars", flags["max-chars"], 100, 32000)
|
|
2672
|
+
})}`;
|
|
2673
|
+
break;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
const data = await callControlAPI(endpoint, { method: "GET" }, profileOptions(context), { timeoutMs: 60000 });
|
|
2677
|
+
context.output.writeData(data ?? {});
|
|
2678
|
+
return { exitCode: 0 };
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2503
2681
|
// src/telemetry.ts
|
|
2504
2682
|
import { randomUUID } from "node:crypto";
|
|
2505
2683
|
var TELEMETRY_TIMEOUT_MS = 500;
|
|
@@ -2514,8 +2692,8 @@ var SEARCH_SURFACE_BY_COMMAND = {
|
|
|
2514
2692
|
context: "context"
|
|
2515
2693
|
};
|
|
2516
2694
|
var SEARCH_MODES2 = ["keyword", "semantic", "hybrid"];
|
|
2517
|
-
function termCount(
|
|
2518
|
-
return
|
|
2695
|
+
function termCount(query2) {
|
|
2696
|
+
return query2.trim().split(/\s+/).filter(Boolean).length;
|
|
2519
2697
|
}
|
|
2520
2698
|
function clampInt(value, min, max) {
|
|
2521
2699
|
if (typeof value !== "number" || !Number.isFinite(value))
|
|
@@ -5180,8 +5358,8 @@ function parsePayload(payload) {
|
|
|
5180
5358
|
}
|
|
5181
5359
|
var DEFAULT_STREAM_TIMEOUT_MS = 250000;
|
|
5182
5360
|
async function streamAsk(options) {
|
|
5183
|
-
const
|
|
5184
|
-
const url = `${resolveModaBaseUrl(
|
|
5361
|
+
const profileOptions2 = options.profile ? { profile: options.profile } : {};
|
|
5362
|
+
const url = `${resolveModaBaseUrl(profileOptions2)}/api/cli/ask/stream`;
|
|
5185
5363
|
const controller = new AbortController;
|
|
5186
5364
|
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS);
|
|
5187
5365
|
const startedAt = Date.now();
|
|
@@ -5197,7 +5375,7 @@ async function streamAsk(options) {
|
|
|
5197
5375
|
response = await fetchImpl(url, {
|
|
5198
5376
|
method: "POST",
|
|
5199
5377
|
headers: {
|
|
5200
|
-
"x-api-key": resolveApiKey(
|
|
5378
|
+
"x-api-key": resolveApiKey(profileOptions2) ?? "",
|
|
5201
5379
|
"Content-Type": "application/json",
|
|
5202
5380
|
Accept: "text/event-stream"
|
|
5203
5381
|
},
|
|
@@ -7005,6 +7183,15 @@ Commands:
|
|
|
7005
7183
|
prompts ab Run a judged prompt A/B replay experiment
|
|
7006
7184
|
prompts propose Revise a prompt from a run's failures (--gate to auto-A/B, --promote-on-win)
|
|
7007
7185
|
|
|
7186
|
+
Replays (read-only results):
|
|
7187
|
+
replays sets List replay sets
|
|
7188
|
+
replays runs <set_id> Run history for a set (every status)
|
|
7189
|
+
replays run <set_id> <run_id> One run: per-case and per-seed verdicts, playout count
|
|
7190
|
+
replays playouts <set_id> <run_id>
|
|
7191
|
+
Playouts per case/arm/seed (--transcript inlines messages and tool calls)
|
|
7192
|
+
replays playout <set_id> <run_id> <playout_id>
|
|
7193
|
+
One playout transcript
|
|
7194
|
+
|
|
7008
7195
|
Skills:
|
|
7009
7196
|
skills gen Kick off tenant-wide skill generation
|
|
7010
7197
|
skills status [run-id] Show a generation run's status, outcome, and recent events (default: latest run)
|
|
@@ -7383,10 +7570,10 @@ async function attachWindows(rows, windowSize) {
|
|
|
7383
7570
|
return { ...row, window: conversation };
|
|
7384
7571
|
}
|
|
7385
7572
|
try {
|
|
7386
|
-
const
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
const data = await callDataAPI(`/conversations/${conv_id}/context?${
|
|
7573
|
+
const query2 = new URLSearchParams;
|
|
7574
|
+
query2.set("msg_index", msg_index.toString());
|
|
7575
|
+
query2.set("window", windowSize.toString());
|
|
7576
|
+
const data = await callDataAPI(`/conversations/${conv_id}/context?${query2.toString()}`);
|
|
7390
7577
|
return { ...row, window: data };
|
|
7391
7578
|
} catch (err) {
|
|
7392
7579
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7787,15 +7974,15 @@ function shellArg(value) {
|
|
|
7787
7974
|
async function searchFallbackToTraces(params) {
|
|
7788
7975
|
const limit = Math.min(params.limit ?? 20, 100);
|
|
7789
7976
|
const offset = params.offset ?? 0;
|
|
7790
|
-
const
|
|
7791
|
-
|
|
7977
|
+
const query2 = new URLSearchParams;
|
|
7978
|
+
query2.set("search", params.q);
|
|
7792
7979
|
if (params.user_id)
|
|
7793
|
-
|
|
7980
|
+
query2.set("user_id", params.user_id);
|
|
7794
7981
|
if (params.time_range)
|
|
7795
|
-
|
|
7796
|
-
|
|
7982
|
+
query2.set("time_range", params.time_range);
|
|
7983
|
+
query2.set("limit", String(limit));
|
|
7797
7984
|
if (offset > 0)
|
|
7798
|
-
|
|
7985
|
+
query2.set("offset", String(offset));
|
|
7799
7986
|
const tracesCommand = [
|
|
7800
7987
|
`moda traces --search=${shellArg(params.q)}`,
|
|
7801
7988
|
params.user_id ? `--user-id=${shellArg(params.user_id)}` : "",
|
|
@@ -7804,7 +7991,7 @@ async function searchFallbackToTraces(params) {
|
|
|
7804
7991
|
].filter(Boolean).join(" ");
|
|
7805
7992
|
let traces;
|
|
7806
7993
|
try {
|
|
7807
|
-
traces = asRecord(await callDataAPI(`/conversations?${
|
|
7994
|
+
traces = asRecord(await callDataAPI(`/conversations?${query2.toString()}`)) ?? {};
|
|
7808
7995
|
} catch (error) {
|
|
7809
7996
|
const reason = error instanceof Error ? error.message : String(error);
|
|
7810
7997
|
return {
|
|
@@ -7953,16 +8140,16 @@ function assertHandParsedFlags(command, flags, positionals) {
|
|
|
7953
8140
|
var PROFILE_MANAGING_COMMANDS = new Set(["profiles", "auth", "init", "provision", "status", "config", "keys", "manifest", "agent", "doctor"]);
|
|
7954
8141
|
async function runProblemsRemainder(flags, context) {
|
|
7955
8142
|
const params = parseCommandFlags(ProblemsRemainderSchema, flagsToArgs(flags), "problems remainder");
|
|
7956
|
-
const
|
|
8143
|
+
const query2 = new URLSearchParams;
|
|
7957
8144
|
if (params.days_back)
|
|
7958
|
-
|
|
8145
|
+
query2.set("days_back", params.days_back.toString());
|
|
7959
8146
|
if (params.limit)
|
|
7960
|
-
|
|
8147
|
+
query2.set("limit", params.limit.toString());
|
|
7961
8148
|
if (params.family)
|
|
7962
|
-
|
|
8149
|
+
query2.set("family", params.family);
|
|
7963
8150
|
if (params.cursor)
|
|
7964
|
-
|
|
7965
|
-
const queryString =
|
|
8151
|
+
query2.set("cursor", params.cursor);
|
|
8152
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
7966
8153
|
context.output.writeData(await callDataAPI(`/problems/remainder${queryString}`));
|
|
7967
8154
|
}
|
|
7968
8155
|
function processingDisabledWarning(data, family) {
|
|
@@ -8158,28 +8345,28 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8158
8345
|
const args = flagsToArgs(flags);
|
|
8159
8346
|
const params = parseCommandFlags(ClustersSchema, args, command);
|
|
8160
8347
|
if (params.search !== undefined || params.node_id !== undefined) {
|
|
8161
|
-
const
|
|
8348
|
+
const query3 = new URLSearchParams;
|
|
8162
8349
|
if (params.search !== undefined)
|
|
8163
|
-
|
|
8350
|
+
query3.set("q", params.search);
|
|
8164
8351
|
if (params.node_id !== undefined)
|
|
8165
|
-
|
|
8352
|
+
query3.set("node_id", params.node_id);
|
|
8166
8353
|
if (params.mode)
|
|
8167
|
-
|
|
8354
|
+
query3.set("mode", params.mode);
|
|
8168
8355
|
if (params.limit)
|
|
8169
|
-
|
|
8170
|
-
const data2 = await callDataAPI(`/task-clusters/search?${
|
|
8356
|
+
query3.set("limit", params.limit.toString());
|
|
8357
|
+
const data2 = await callDataAPI(`/task-clusters/search?${query3.toString()}`);
|
|
8171
8358
|
const record = asRecord(data2) ?? {};
|
|
8172
8359
|
const matches = Array.isArray(record.matches) ? record.matches : [];
|
|
8173
8360
|
const warnings = params.node_id !== undefined && matches.length === 0 ? notFoundWarning("cluster", params.node_id, true) : [];
|
|
8174
8361
|
context.output.writeData(data2, warnings.length > 0 ? { warnings } : undefined);
|
|
8175
8362
|
break;
|
|
8176
8363
|
}
|
|
8177
|
-
const
|
|
8364
|
+
const query2 = new URLSearchParams;
|
|
8178
8365
|
if (params.parent_id)
|
|
8179
|
-
|
|
8366
|
+
query2.set("parent_id", params.parent_id);
|
|
8180
8367
|
if (params.time_range)
|
|
8181
|
-
|
|
8182
|
-
const queryString =
|
|
8368
|
+
query2.set("time_range", params.time_range);
|
|
8369
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8183
8370
|
const data = await callDataAPI(`/clusters${queryString}`);
|
|
8184
8371
|
context.output.writeData(data);
|
|
8185
8372
|
break;
|
|
@@ -8191,12 +8378,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8191
8378
|
}
|
|
8192
8379
|
const args = flagsToArgs(flags, "node_id", positional);
|
|
8193
8380
|
const params = parseCommandFlags(ClusterConversationsSchema, args, command, { positionalKey: "node_id" });
|
|
8194
|
-
const
|
|
8381
|
+
const query2 = new URLSearchParams;
|
|
8195
8382
|
if (params.limit)
|
|
8196
|
-
|
|
8383
|
+
query2.set("limit", params.limit.toString());
|
|
8197
8384
|
if (params.offset !== undefined)
|
|
8198
|
-
|
|
8199
|
-
const queryString =
|
|
8385
|
+
query2.set("offset", params.offset.toString());
|
|
8386
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8200
8387
|
const data = await callDataAPI(`/clusters/${params.node_id}/conversations${queryString}`);
|
|
8201
8388
|
const clusterRecord = asRecord(data) ?? {};
|
|
8202
8389
|
const warnings = notFoundWarning("cluster", params.node_id, clusterRecord.cluster === null || clusterRecord.cluster === undefined);
|
|
@@ -8207,28 +8394,28 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8207
8394
|
case "conversations": {
|
|
8208
8395
|
const args = withStringIdFlags(flagsToArgs(flags), flags);
|
|
8209
8396
|
const params = parseCommandFlags(ConversationsSchema, args, command);
|
|
8210
|
-
const
|
|
8397
|
+
const query2 = new URLSearchParams;
|
|
8211
8398
|
if (params.search)
|
|
8212
|
-
|
|
8399
|
+
query2.set("search", params.search);
|
|
8213
8400
|
if (params.cluster_id)
|
|
8214
|
-
|
|
8401
|
+
query2.set("cluster_id", params.cluster_id);
|
|
8215
8402
|
if (params.user_id)
|
|
8216
|
-
|
|
8403
|
+
query2.set("user_id", params.user_id);
|
|
8217
8404
|
if (params.time_range)
|
|
8218
|
-
|
|
8405
|
+
query2.set("time_range", params.time_range);
|
|
8219
8406
|
if (params.environment)
|
|
8220
|
-
|
|
8407
|
+
query2.set("environment", params.environment);
|
|
8221
8408
|
if (params.world_state)
|
|
8222
|
-
|
|
8409
|
+
query2.set("world_state", params.world_state);
|
|
8223
8410
|
if (params.outcome && params.outcome !== "any")
|
|
8224
|
-
|
|
8411
|
+
query2.set("outcome", params.outcome);
|
|
8225
8412
|
if (params.sort)
|
|
8226
|
-
|
|
8413
|
+
query2.set("sort_by", params.sort);
|
|
8227
8414
|
if (params.limit)
|
|
8228
|
-
|
|
8415
|
+
query2.set("limit", params.limit.toString());
|
|
8229
8416
|
if (params.offset !== undefined)
|
|
8230
|
-
|
|
8231
|
-
const queryString =
|
|
8417
|
+
query2.set("offset", params.offset.toString());
|
|
8418
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8232
8419
|
const raw = await callDataAPI(`/conversations${queryString}`);
|
|
8233
8420
|
recordConversationsTelemetry(context, raw);
|
|
8234
8421
|
const searchWarnings = [];
|
|
@@ -8265,19 +8452,19 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8265
8452
|
}
|
|
8266
8453
|
const args = withStringIdFlags(flagsToArgs(flags, "q", positional), flags);
|
|
8267
8454
|
const params = parseCommandFlags(SearchSchema, args, command, { positionalKey: "q" });
|
|
8268
|
-
const
|
|
8269
|
-
|
|
8455
|
+
const query2 = new URLSearchParams;
|
|
8456
|
+
query2.set("q", params.q);
|
|
8270
8457
|
if (params.mode)
|
|
8271
|
-
|
|
8458
|
+
query2.set("mode", params.mode);
|
|
8272
8459
|
if (params.user_id)
|
|
8273
|
-
|
|
8460
|
+
query2.set("user_id", params.user_id);
|
|
8274
8461
|
if (params.time_range)
|
|
8275
|
-
|
|
8462
|
+
query2.set("time_range", params.time_range);
|
|
8276
8463
|
if (params.limit)
|
|
8277
|
-
|
|
8464
|
+
query2.set("limit", params.limit.toString());
|
|
8278
8465
|
if (params.include_tool_io)
|
|
8279
|
-
|
|
8280
|
-
const data = await callDataAPI(`/search?${
|
|
8466
|
+
query2.set("include_tool_io", "true");
|
|
8467
|
+
const data = await callDataAPI(`/search?${query2.toString()}`);
|
|
8281
8468
|
recordSearchTelemetry(context, data);
|
|
8282
8469
|
const searchRecord = asRecord(data) ?? {};
|
|
8283
8470
|
const degradeReason = asString(searchRecord.degrade_reason);
|
|
@@ -8325,13 +8512,13 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8325
8512
|
throw new CliInputError("Choose either --snapshot or --replay, not both.", "Usage: moda world-state <conversation_id> [--snapshot --msg-index=N [--block-index=M] [--segment-index=K]] | [--replay --message-count=N [--format=frames|fold-inputs]]");
|
|
8326
8513
|
}
|
|
8327
8514
|
if (params.snapshot === true) {
|
|
8328
|
-
const
|
|
8329
|
-
|
|
8515
|
+
const query3 = new URLSearchParams;
|
|
8516
|
+
query3.set("msg_index", (params.msg_index ?? 0).toString());
|
|
8330
8517
|
if (params.block_index !== undefined)
|
|
8331
|
-
|
|
8518
|
+
query3.set("block_index", params.block_index.toString());
|
|
8332
8519
|
if (params.segment_index !== undefined)
|
|
8333
|
-
|
|
8334
|
-
const data2 = await callDataAPI(`/conversations/${params.conversation_id}/world-state/snapshot?${
|
|
8520
|
+
query3.set("segment_index", params.segment_index.toString());
|
|
8521
|
+
const data2 = await callDataAPI(`/conversations/${params.conversation_id}/world-state/snapshot?${query3.toString()}`);
|
|
8335
8522
|
const snapshotRecord = asRecord(data2) ?? {};
|
|
8336
8523
|
const warnings2 = notFoundWarning("world state", params.conversation_id, snapshotRecord.found === false);
|
|
8337
8524
|
context.output.writeData(data2, warnings2.length > 0 ? { warnings: warnings2 } : undefined);
|
|
@@ -8342,22 +8529,22 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8342
8529
|
if (format === "frames" && params.message_count === undefined) {
|
|
8343
8530
|
throw new CliInputError("--message-count=N is required with --replay in frames mode (the API returns no frames without it). Use --format=fold-inputs for the raw keyframes/events without a count.");
|
|
8344
8531
|
}
|
|
8345
|
-
const
|
|
8346
|
-
|
|
8532
|
+
const query3 = new URLSearchParams;
|
|
8533
|
+
query3.set("format", format === "fold-inputs" ? "fold_inputs" : "frames");
|
|
8347
8534
|
if (params.message_count !== undefined)
|
|
8348
|
-
|
|
8349
|
-
const data2 = await callDataAPI(`/conversations/${params.conversation_id}/world-state/replay?${
|
|
8535
|
+
query3.set("message_count", params.message_count.toString());
|
|
8536
|
+
const data2 = await callDataAPI(`/conversations/${params.conversation_id}/world-state/replay?${query3.toString()}`);
|
|
8350
8537
|
const replayRecord = asRecord(data2) ?? {};
|
|
8351
8538
|
const warnings2 = notFoundWarning("world state", params.conversation_id, replayRecord.hasData === false);
|
|
8352
8539
|
context.output.writeData(data2, warnings2.length > 0 ? { warnings: warnings2 } : undefined);
|
|
8353
8540
|
break;
|
|
8354
8541
|
}
|
|
8355
|
-
const
|
|
8542
|
+
const query2 = new URLSearchParams;
|
|
8356
8543
|
if (params.summary_only === true)
|
|
8357
|
-
|
|
8544
|
+
query2.set("summary_only", "1");
|
|
8358
8545
|
if (params.event_limit)
|
|
8359
|
-
|
|
8360
|
-
const queryString =
|
|
8546
|
+
query2.set("event_limit", params.event_limit.toString());
|
|
8547
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8361
8548
|
const data = await callDataAPI(`/conversations/${params.conversation_id}/world-state${queryString}`);
|
|
8362
8549
|
const wsRecord = asRecord(data) ?? {};
|
|
8363
8550
|
const warnings = notFoundWarning("world state", params.conversation_id, wsRecord.hasData === false);
|
|
@@ -8389,12 +8576,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8389
8576
|
if (params.from !== undefined || params.max_messages !== undefined) {
|
|
8390
8577
|
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]");
|
|
8391
8578
|
}
|
|
8392
|
-
const
|
|
8579
|
+
const query2 = new URLSearchParams;
|
|
8393
8580
|
if (params.msg_index !== undefined)
|
|
8394
|
-
|
|
8581
|
+
query2.set("msg_index", params.msg_index.toString());
|
|
8395
8582
|
if (params.window)
|
|
8396
|
-
|
|
8397
|
-
const queryString =
|
|
8583
|
+
query2.set("window", params.window.toString());
|
|
8584
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8398
8585
|
const data = await callDataAPI(`/conversations/${params.conversation_id}/context${queryString}`);
|
|
8399
8586
|
const ctxRecord = asRecord(data) ?? {};
|
|
8400
8587
|
const warnings = notFoundWarning("trace", params.conversation_id, asNumber(ctxRecord.total_messages) === 0);
|
|
@@ -8408,14 +8595,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8408
8595
|
}
|
|
8409
8596
|
const args = flagsToArgs(flags, "id", positional);
|
|
8410
8597
|
const params = parseCommandFlags(AuditSchema, args, command, { positionalKey: "id" });
|
|
8411
|
-
const
|
|
8598
|
+
const query2 = new URLSearchParams;
|
|
8412
8599
|
if (params.kind && params.kind !== "auto")
|
|
8413
|
-
|
|
8600
|
+
query2.set("kind", params.kind);
|
|
8414
8601
|
if (params.include_raw === true)
|
|
8415
|
-
|
|
8602
|
+
query2.set("include_raw", "1");
|
|
8416
8603
|
if (params.limit)
|
|
8417
|
-
|
|
8418
|
-
const queryString =
|
|
8604
|
+
query2.set("limit", params.limit.toString());
|
|
8605
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8419
8606
|
const data = await callDataAPI(`/audit/${encodeURIComponent(params.id)}${queryString}`);
|
|
8420
8607
|
const capped = auditCapStatus(data, params.limit ?? AUDIT_DEFAULT_LIMIT);
|
|
8421
8608
|
const auditRecord = asRecord(data);
|
|
@@ -8448,19 +8635,19 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8448
8635
|
case "frustrations": {
|
|
8449
8636
|
const args = withStringIdFlags(flagsToArgs(flags), flags);
|
|
8450
8637
|
const params = parseCommandFlags(FrustrationsSchema, args, command);
|
|
8451
|
-
const
|
|
8638
|
+
const query2 = new URLSearchParams;
|
|
8452
8639
|
if (params.days_back)
|
|
8453
|
-
|
|
8640
|
+
query2.set("days_back", params.days_back.toString());
|
|
8454
8641
|
if (params.limit)
|
|
8455
|
-
|
|
8642
|
+
query2.set("limit", params.limit.toString());
|
|
8456
8643
|
if (params.offset !== undefined)
|
|
8457
|
-
|
|
8644
|
+
query2.set("offset", params.offset.toString());
|
|
8458
8645
|
if (params.user_id)
|
|
8459
|
-
|
|
8646
|
+
query2.set("user_id", params.user_id);
|
|
8460
8647
|
if (params.conversation_id)
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
const data = frustrationsFromEmotions(await callDataAPI(`/emotions?${
|
|
8648
|
+
query2.set("conversation_id", params.conversation_id);
|
|
8649
|
+
query2.set("family", "frustration");
|
|
8650
|
+
const data = frustrationsFromEmotions(await callDataAPI(`/emotions?${query2.toString()}`));
|
|
8464
8651
|
await printWithOptionalWindows(withFrustrationAnchors(data), "frustrations", params.include_window === true, params.window ?? 1, context.output, processingDisabledWarning(data, "frustration"));
|
|
8465
8652
|
break;
|
|
8466
8653
|
}
|
|
@@ -8489,15 +8676,15 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8489
8676
|
if (params.compact === true && params.include_window === true) {
|
|
8490
8677
|
throw new CliInputError("--compact drops the conversation window; drop --include-window, or drop --compact.");
|
|
8491
8678
|
}
|
|
8492
|
-
const
|
|
8679
|
+
const query2 = new URLSearchParams;
|
|
8493
8680
|
if (params.subtype)
|
|
8494
|
-
|
|
8681
|
+
query2.set("subtype", params.subtype);
|
|
8495
8682
|
if (params.days_back)
|
|
8496
|
-
|
|
8497
|
-
|
|
8683
|
+
query2.set("days_back", params.days_back.toString());
|
|
8684
|
+
query2.set("limit", (params.limit ?? TOOL_FAILURE_DETAIL_DEFAULT_LIMIT).toString());
|
|
8498
8685
|
if (params.offset !== undefined)
|
|
8499
|
-
|
|
8500
|
-
const queryString =
|
|
8686
|
+
query2.set("offset", params.offset.toString());
|
|
8687
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8501
8688
|
const data = await callDataAPI(`/tool-failures/${params.tool_name}${queryString}`);
|
|
8502
8689
|
const tfRecord = asRecord(data) ?? {};
|
|
8503
8690
|
const subtypeCount = Array.isArray(tfRecord.subtypes) ? tfRecord.subtypes.length : 0;
|
|
@@ -8531,7 +8718,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8531
8718
|
}
|
|
8532
8719
|
case "problem-impact": {
|
|
8533
8720
|
const params = ProblemImpactSchema.parse(Object.fromEntries(Object.entries(flags).map(([key, value]) => [key.replace(/-/g, "_"), value])));
|
|
8534
|
-
const
|
|
8721
|
+
const query2 = new URLSearchParams({ days: String(params.days_back), mode: params.mode });
|
|
8535
8722
|
for (const [key, value] of Object.entries({
|
|
8536
8723
|
nodeId: params.node_id,
|
|
8537
8724
|
problemId: params.problem_id,
|
|
@@ -8541,8 +8728,8 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8541
8728
|
conversationIds: params.conversation_ids ? JSON.stringify(params.conversation_ids) : undefined
|
|
8542
8729
|
}))
|
|
8543
8730
|
if (value !== undefined)
|
|
8544
|
-
|
|
8545
|
-
const data = asRecord(await callDataAPI(`/problem-impact?${
|
|
8731
|
+
query2.set(key, value);
|
|
8732
|
+
const data = asRecord(await callDataAPI(`/problem-impact?${query2}`)) ?? {};
|
|
8546
8733
|
const link = new URLSearchParams({ impactDays: String(params.days_back) });
|
|
8547
8734
|
const window = asRecord(data.window);
|
|
8548
8735
|
const resolvedRun = asString(data.runId);
|
|
@@ -8569,12 +8756,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8569
8756
|
}
|
|
8570
8757
|
const args = flagsToArgs(flags);
|
|
8571
8758
|
const params = parseCommandFlags(ProblemsSchema, args, command);
|
|
8572
|
-
const
|
|
8759
|
+
const query2 = new URLSearchParams;
|
|
8573
8760
|
if (params.days_back)
|
|
8574
|
-
|
|
8761
|
+
query2.set("days_back", params.days_back.toString());
|
|
8575
8762
|
if (params.limit)
|
|
8576
|
-
|
|
8577
|
-
const queryString =
|
|
8763
|
+
query2.set("limit", params.limit.toString());
|
|
8764
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8578
8765
|
const data = await callDataAPI(`/problems${queryString}`);
|
|
8579
8766
|
const { payload, truncated } = capProblemsBlocks(asRecord(data) ?? {}, params.limit ?? 25);
|
|
8580
8767
|
const withLink = {
|
|
@@ -8616,18 +8803,18 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8616
8803
|
const viewFlag = view === "conversations" ? "--traces" : `--${view}`;
|
|
8617
8804
|
throw new CliInputError(`${viewFlag} requires a canonical problem UUID; got "${params.id}". Run \`moda problems\` or \`moda problem <id>\` first to resolve the id.`);
|
|
8618
8805
|
}
|
|
8619
|
-
const
|
|
8806
|
+
const query2 = new URLSearchParams;
|
|
8620
8807
|
if (params.limit)
|
|
8621
|
-
|
|
8808
|
+
query2.set("limit", params.limit.toString());
|
|
8622
8809
|
if (params.cursor)
|
|
8623
|
-
|
|
8810
|
+
query2.set("cursor", params.cursor);
|
|
8624
8811
|
if (view === "conversations") {
|
|
8625
8812
|
if (params.family)
|
|
8626
|
-
|
|
8813
|
+
query2.set("family", params.family);
|
|
8627
8814
|
if (params.door)
|
|
8628
|
-
|
|
8815
|
+
query2.set("door", params.door);
|
|
8629
8816
|
}
|
|
8630
|
-
const queryString =
|
|
8817
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8631
8818
|
const data = await callDataAPI(`/problems/${params.id}/${view}${queryString}`);
|
|
8632
8819
|
context.output.writeData(data);
|
|
8633
8820
|
break;
|
|
@@ -8763,14 +8950,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8763
8950
|
context.output.writeData(data2);
|
|
8764
8951
|
break;
|
|
8765
8952
|
}
|
|
8766
|
-
const
|
|
8953
|
+
const query2 = new URLSearchParams;
|
|
8767
8954
|
if (params.days_back)
|
|
8768
|
-
|
|
8955
|
+
query2.set("days_back", params.days_back.toString());
|
|
8769
8956
|
if (params.limit)
|
|
8770
|
-
|
|
8957
|
+
query2.set("limit", params.limit.toString());
|
|
8771
8958
|
if (params.offset !== undefined)
|
|
8772
|
-
|
|
8773
|
-
const queryString =
|
|
8959
|
+
query2.set("offset", params.offset.toString());
|
|
8960
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8774
8961
|
const data = await callDataAPI(`/custom-signals/${id}/detections${queryString}`);
|
|
8775
8962
|
context.output.writeData(data);
|
|
8776
8963
|
break;
|
|
@@ -8922,20 +9109,20 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8922
9109
|
case "false-positives": {
|
|
8923
9110
|
if (!positional) {
|
|
8924
9111
|
const kinds = { quote: "frustration_quote", tool: "tool_call", laziness: "laziness", hallucination: "hallucination", "custom-signal": "custom_signal" };
|
|
8925
|
-
const
|
|
9112
|
+
const query2 = new URLSearchParams;
|
|
8926
9113
|
if (flags.kind !== undefined) {
|
|
8927
9114
|
if (!kinds[flags.kind])
|
|
8928
9115
|
throw new CliInputError(`--kind must be one of ${Object.keys(kinds).join("|")}`);
|
|
8929
|
-
|
|
9116
|
+
query2.set("kind", kinds[flags.kind]);
|
|
8930
9117
|
}
|
|
8931
9118
|
if (flags.limit !== undefined) {
|
|
8932
9119
|
if (!/^\d+$/.test(flags.limit) || Number(flags.limit) < 1 || Number(flags.limit) > 200)
|
|
8933
9120
|
throw new CliInputError("--limit must be 1-200");
|
|
8934
|
-
|
|
9121
|
+
query2.set("limit", flags.limit);
|
|
8935
9122
|
}
|
|
8936
9123
|
if (flags.all !== undefined && flags.all !== "false")
|
|
8937
|
-
|
|
8938
|
-
const queryString =
|
|
9124
|
+
query2.set("include_cleared", "true");
|
|
9125
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8939
9126
|
const data2 = await callDataAPI(`/false-positives${queryString}`);
|
|
8940
9127
|
context.output.writeData(data2);
|
|
8941
9128
|
break;
|
|
@@ -8947,20 +9134,20 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8947
9134
|
case "emotions": {
|
|
8948
9135
|
const args = withStringIdFlags(flagsToArgs(flags), flags);
|
|
8949
9136
|
const params = parseCommandFlags(EmotionsSchema, args, command);
|
|
8950
|
-
const
|
|
9137
|
+
const query2 = new URLSearchParams;
|
|
8951
9138
|
if (params.days_back)
|
|
8952
|
-
|
|
9139
|
+
query2.set("days_back", params.days_back.toString());
|
|
8953
9140
|
if (params.limit)
|
|
8954
|
-
|
|
9141
|
+
query2.set("limit", params.limit.toString());
|
|
8955
9142
|
if (params.offset !== undefined)
|
|
8956
|
-
|
|
9143
|
+
query2.set("offset", params.offset.toString());
|
|
8957
9144
|
if (params.family)
|
|
8958
|
-
|
|
9145
|
+
query2.set("family", params.family);
|
|
8959
9146
|
if (params.user_id)
|
|
8960
|
-
|
|
9147
|
+
query2.set("user_id", params.user_id);
|
|
8961
9148
|
if (params.conversation_id)
|
|
8962
|
-
|
|
8963
|
-
const queryString =
|
|
9149
|
+
query2.set("conversation_id", params.conversation_id);
|
|
9150
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8964
9151
|
const data = await callDataAPI(`/emotions${queryString}`);
|
|
8965
9152
|
const processingWarnings = processingDisabledWarning(data, "emotion");
|
|
8966
9153
|
context.output.writeData(data, processingWarnings.length > 0 ? { warnings: processingWarnings } : undefined);
|
|
@@ -8972,18 +9159,18 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8972
9159
|
args.conversation_id = flags["conversation-id"];
|
|
8973
9160
|
}
|
|
8974
9161
|
const params = parseCommandFlags(LazinessSchema, args, command);
|
|
8975
|
-
const
|
|
9162
|
+
const query2 = new URLSearchParams;
|
|
8976
9163
|
if (params.days_back)
|
|
8977
|
-
|
|
9164
|
+
query2.set("days_back", params.days_back.toString());
|
|
8978
9165
|
if (params.limit)
|
|
8979
|
-
|
|
9166
|
+
query2.set("limit", params.limit.toString());
|
|
8980
9167
|
if (params.offset !== undefined)
|
|
8981
|
-
|
|
9168
|
+
query2.set("offset", params.offset.toString());
|
|
8982
9169
|
if (params.conversation_id)
|
|
8983
|
-
|
|
9170
|
+
query2.set("conversation_id", params.conversation_id);
|
|
8984
9171
|
if (params.family)
|
|
8985
|
-
|
|
8986
|
-
const queryString =
|
|
9172
|
+
query2.set("family", params.family);
|
|
9173
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8987
9174
|
const data = await callDataAPI(`/laziness${queryString}`);
|
|
8988
9175
|
context.output.writeData(data);
|
|
8989
9176
|
break;
|
|
@@ -8994,18 +9181,18 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8994
9181
|
args.conversation_id = flags["conversation-id"];
|
|
8995
9182
|
}
|
|
8996
9183
|
const params = parseCommandFlags(HallucinationsSchema, args, command);
|
|
8997
|
-
const
|
|
9184
|
+
const query2 = new URLSearchParams;
|
|
8998
9185
|
if (params.days_back)
|
|
8999
|
-
|
|
9186
|
+
query2.set("days_back", params.days_back.toString());
|
|
9000
9187
|
if (params.limit)
|
|
9001
|
-
|
|
9188
|
+
query2.set("limit", params.limit.toString());
|
|
9002
9189
|
if (params.offset !== undefined)
|
|
9003
|
-
|
|
9190
|
+
query2.set("offset", params.offset.toString());
|
|
9004
9191
|
if (params.conversation_id)
|
|
9005
|
-
|
|
9192
|
+
query2.set("conversation_id", params.conversation_id);
|
|
9006
9193
|
if (params.kind)
|
|
9007
|
-
|
|
9008
|
-
const queryString =
|
|
9194
|
+
query2.set("kind", params.kind);
|
|
9195
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
9009
9196
|
const data = await callDataAPI(`/hallucinations${queryString}`);
|
|
9010
9197
|
context.output.writeData(data);
|
|
9011
9198
|
break;
|
|
@@ -9481,6 +9668,23 @@ var commandRegistry = createCommandRegistry([
|
|
|
9481
9668
|
telemetry: "result-and-error",
|
|
9482
9669
|
handler: runUserCommand
|
|
9483
9670
|
},
|
|
9671
|
+
{
|
|
9672
|
+
name: "replays",
|
|
9673
|
+
description: "Read replay results: sets, run history, per-seed verdicts, and playout transcripts",
|
|
9674
|
+
examples: [
|
|
9675
|
+
"moda replays runs <set_id>",
|
|
9676
|
+
"moda replays run <set_id> <run_id> --json",
|
|
9677
|
+
"moda replays playouts <set_id> <run_id> --case=<case_id> --arm=proposed --transcript --json"
|
|
9678
|
+
],
|
|
9679
|
+
subcommands: REPLAYS_SUBCOMMAND_HELP,
|
|
9680
|
+
auth: "api-key",
|
|
9681
|
+
outputModes: ["human", "json", "agent"],
|
|
9682
|
+
mutability: "read",
|
|
9683
|
+
validateConfigBeforeRun: false,
|
|
9684
|
+
resetApiRequestCountBeforeRun: false,
|
|
9685
|
+
telemetry: "result-and-error",
|
|
9686
|
+
handler: runReplaysCommand
|
|
9687
|
+
},
|
|
9484
9688
|
{
|
|
9485
9689
|
name: "problem-reopen",
|
|
9486
9690
|
description: "Reopen a problem that was marked fixed (resolved problems return only via re-detection)",
|
package/package.json
CHANGED
package/skills/moda-cli/SKILL.md
CHANGED
|
@@ -785,6 +785,43 @@ Do not copy prompt text from the dashboard into source code. The repo prompt
|
|
|
785
785
|
files are the source of truth; the dashboard is for visibility, usage, labels,
|
|
786
786
|
and future evals.
|
|
787
787
|
|
|
788
|
+
### 7a. Check a replay verdict (`moda replays`)
|
|
789
|
+
|
|
790
|
+
Use after `prompts ab`, `prompts propose --gate`, or a dashboard replay when
|
|
791
|
+
you need to see *why* an arm passed or failed. Read-only; no replay spend.
|
|
792
|
+
|
|
793
|
+
```bash
|
|
794
|
+
moda replays sets --json # set ids
|
|
795
|
+
moda replays runs <set_id> --limit=5 --json # newest runs, every status
|
|
796
|
+
moda replays run <set_id> <run_id> --json # per-case + per-seed verdicts
|
|
797
|
+
moda replays playouts <set_id> <run_id> --json # one row per case x arm x seed
|
|
798
|
+
moda replays playouts <set_id> <run_id> --case=<case_id> --arm=proposed --seed=1 --transcript --json
|
|
799
|
+
moda replays playout <set_id> <run_id> <playout_id> --json
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
`run` returns `results[]` (one per case × arm × seed: `pass`, `abstained`,
|
|
803
|
+
`coverageReason`, `score`, `rationale`, `criteria[]`) next to the per-case
|
|
804
|
+
majority in `cases[]`. `arm=prod` is the baseline and `arm=proposed` the
|
|
805
|
+
candidate. An `abstained: true` row is not a verdict: the pair hit a replay
|
|
806
|
+
coverage hole and is excluded from pass counts.
|
|
807
|
+
|
|
808
|
+
A playout's `transcript.events[]` holds the simulated user and agent messages
|
|
809
|
+
and every tool call with its `input`, the `output` the replay served, and
|
|
810
|
+
`provenance.source`: `replay_runtime` (compiled mock over the case's recorded
|
|
811
|
+
source state), `agent_tool_simulator` (LLM simulator grounded in recorded
|
|
812
|
+
receipts; `simulator.model`), `counterfactual_reconstruction`, or
|
|
813
|
+
`generic_handler`. `provenance.errorSubtype` such as `missing_source_state`
|
|
814
|
+
marks an unservable call. `judge` is the judgement stored with the playout and
|
|
815
|
+
`verdict` the stored eval row (null when `seed` is null, which happens on
|
|
816
|
+
older runs whose graphs cannot be matched to a seed).
|
|
817
|
+
|
|
818
|
+
Limits: `--transcript` pages at most 10 playouts (`--limit` default 50, max
|
|
819
|
+
200 without it); text and tool JSON are clipped at `--max-chars` (default
|
|
820
|
+
4000, max 32000) and `transcript.truncated.fields` counts the clipped fields.
|
|
821
|
+
Page with `--cursor=<nextCursor>`. Playouts exist only for runs whose
|
|
822
|
+
playouts were captured; `playoutCount: 0` on `run` means there is no
|
|
823
|
+
transcript to read.
|
|
824
|
+
|
|
788
825
|
### 7b. Push prompts, skills, and tools without a repo scan (`moda registry`)
|
|
789
826
|
|
|
790
827
|
Use when the user wants their prompts, skills, and tools visible in Moda but
|
|
@@ -1026,6 +1063,7 @@ Writes listed here accept `--dry-run` (prints the exact request, sends nothing)
|
|
|
1026
1063
|
| Custom signals: edit, lifecycle | `moda signals update <id> …`, `moda signals activate\|pause\|delete <id>` (delete: drafts only; archiving is dashboard-only) |
|
|
1027
1064
|
| Custom signals: review & tune | `moda signals examples <id> add\|update\|remove --conversation-id=…`, `refine <id> --request="…"`, `revise <id> --conversation-ids=…`, `proposals <id>`, `accept\|reject <id> <proposal_id>` |
|
|
1028
1065
|
| Custom signals: find matches | `moda signals search <id>`, `search-status <id> <search_id>`, `search-stop <id> <search_id>`, `preview --file=detector.json` |
|
|
1066
|
+
| Replays: run detail, playout transcripts | `moda replays runs <set_id>`, `run <set_id> <run_id>`, `playouts <set_id> <run_id> [--case --arm --seed --transcript]`, `playout <set_id> <run_id> <playout_id>` |
|
|
1029
1067
|
| Prompts registry | `moda prompts list [--label --search]`, `show <key>`, `proposals <key>`, `usage <key>`, `replay-runs <key>`, `decide <key> <proposal_id> --action=merge\|reject\|reopen` |
|
|
1030
1068
|
| Skills library | `moda skills list`, `show <key>`, `policy <key> --live\|--local` |
|
|
1031
1069
|
| Harness pages | `moda harness list`, `show <id>`, `versions <id>`, `diff <id> --from=… --to=…` |
|
|
@@ -1234,6 +1272,7 @@ npx: `npx -p @moda-ai/cli moda <command>`.
|
|
|
1234
1272
|
| `moda problem-reopen <pid>` | Reopen a Problem marked fixed |
|
|
1235
1273
|
| `moda signals <create\|update\|activate\|pause\|delete\|examples\|refine\|revise\|proposals\|accept\|reject\|search\|preview>` | Manage custom signals (writes take `--dry-run`; LLM-backed ones are rate-limited per key) |
|
|
1236
1274
|
| `moda prompts list\|show\|proposals\|usage\|replay-runs\|decide` | Read the prompt registry; merge/reject proposals |
|
|
1275
|
+
| `moda replays sets\|runs\|run\|playouts\|playout` | Replay results: run history, per-seed verdicts, playout transcripts with tool provenance (read-only) |
|
|
1237
1276
|
| `moda skills list\|show\|policy` / `moda harness list\|show\|versions\|diff` | Skill library and synced harnesses |
|
|
1238
1277
|
| `moda fixes` | Ranked queue of Fixes ("a fix with proof"); `--status=S`, `--limit=N`, `--cursor=TOKEN` |
|
|
1239
1278
|
| `moda fix <fix_id>` | One Fix: status + gate result; `--packet` prints `moda.fix_packet.v1` and writes tool/skill candidates under `.moda/fixes/<ref>/`; `--wait` drives a running pipeline to rest |
|