@moda-ai/cli 1.41.1 → 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 +344 -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,
|
|
@@ -2196,6 +2197,9 @@ async function pushRegistry(context) {
|
|
|
2196
2197
|
const detail = section.status === "error" ? ` — ${section.error}` : describeActions(section.response);
|
|
2197
2198
|
console.error(` ${section.section}: ${section.status} (${section.count})${detail}`);
|
|
2198
2199
|
}
|
|
2200
|
+
if (sections.some((section) => section.section === "tools" && section.status === "synced")) {
|
|
2201
|
+
console.error(" Replays now give your agent these tools with their real schemas and descriptions.");
|
|
2202
|
+
}
|
|
2199
2203
|
}
|
|
2200
2204
|
context.output.writeData({
|
|
2201
2205
|
schema: REGISTRY_SCHEMA,
|
|
@@ -2497,6 +2501,183 @@ function isDefined(value) {
|
|
|
2497
2501
|
return value !== undefined;
|
|
2498
2502
|
}
|
|
2499
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
|
+
|
|
2500
2681
|
// src/telemetry.ts
|
|
2501
2682
|
import { randomUUID } from "node:crypto";
|
|
2502
2683
|
var TELEMETRY_TIMEOUT_MS = 500;
|
|
@@ -2511,8 +2692,8 @@ var SEARCH_SURFACE_BY_COMMAND = {
|
|
|
2511
2692
|
context: "context"
|
|
2512
2693
|
};
|
|
2513
2694
|
var SEARCH_MODES2 = ["keyword", "semantic", "hybrid"];
|
|
2514
|
-
function termCount(
|
|
2515
|
-
return
|
|
2695
|
+
function termCount(query2) {
|
|
2696
|
+
return query2.trim().split(/\s+/).filter(Boolean).length;
|
|
2516
2697
|
}
|
|
2517
2698
|
function clampInt(value, min, max) {
|
|
2518
2699
|
if (typeof value !== "number" || !Number.isFinite(value))
|
|
@@ -5177,8 +5358,8 @@ function parsePayload(payload) {
|
|
|
5177
5358
|
}
|
|
5178
5359
|
var DEFAULT_STREAM_TIMEOUT_MS = 250000;
|
|
5179
5360
|
async function streamAsk(options) {
|
|
5180
|
-
const
|
|
5181
|
-
const url = `${resolveModaBaseUrl(
|
|
5361
|
+
const profileOptions2 = options.profile ? { profile: options.profile } : {};
|
|
5362
|
+
const url = `${resolveModaBaseUrl(profileOptions2)}/api/cli/ask/stream`;
|
|
5182
5363
|
const controller = new AbortController;
|
|
5183
5364
|
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS);
|
|
5184
5365
|
const startedAt = Date.now();
|
|
@@ -5194,7 +5375,7 @@ async function streamAsk(options) {
|
|
|
5194
5375
|
response = await fetchImpl(url, {
|
|
5195
5376
|
method: "POST",
|
|
5196
5377
|
headers: {
|
|
5197
|
-
"x-api-key": resolveApiKey(
|
|
5378
|
+
"x-api-key": resolveApiKey(profileOptions2) ?? "",
|
|
5198
5379
|
"Content-Type": "application/json",
|
|
5199
5380
|
Accept: "text/event-stream"
|
|
5200
5381
|
},
|
|
@@ -7002,6 +7183,15 @@ Commands:
|
|
|
7002
7183
|
prompts ab Run a judged prompt A/B replay experiment
|
|
7003
7184
|
prompts propose Revise a prompt from a run's failures (--gate to auto-A/B, --promote-on-win)
|
|
7004
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
|
+
|
|
7005
7195
|
Skills:
|
|
7006
7196
|
skills gen Kick off tenant-wide skill generation
|
|
7007
7197
|
skills status [run-id] Show a generation run's status, outcome, and recent events (default: latest run)
|
|
@@ -7380,10 +7570,10 @@ async function attachWindows(rows, windowSize) {
|
|
|
7380
7570
|
return { ...row, window: conversation };
|
|
7381
7571
|
}
|
|
7382
7572
|
try {
|
|
7383
|
-
const
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
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()}`);
|
|
7387
7577
|
return { ...row, window: data };
|
|
7388
7578
|
} catch (err) {
|
|
7389
7579
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7784,15 +7974,15 @@ function shellArg(value) {
|
|
|
7784
7974
|
async function searchFallbackToTraces(params) {
|
|
7785
7975
|
const limit = Math.min(params.limit ?? 20, 100);
|
|
7786
7976
|
const offset = params.offset ?? 0;
|
|
7787
|
-
const
|
|
7788
|
-
|
|
7977
|
+
const query2 = new URLSearchParams;
|
|
7978
|
+
query2.set("search", params.q);
|
|
7789
7979
|
if (params.user_id)
|
|
7790
|
-
|
|
7980
|
+
query2.set("user_id", params.user_id);
|
|
7791
7981
|
if (params.time_range)
|
|
7792
|
-
|
|
7793
|
-
|
|
7982
|
+
query2.set("time_range", params.time_range);
|
|
7983
|
+
query2.set("limit", String(limit));
|
|
7794
7984
|
if (offset > 0)
|
|
7795
|
-
|
|
7985
|
+
query2.set("offset", String(offset));
|
|
7796
7986
|
const tracesCommand = [
|
|
7797
7987
|
`moda traces --search=${shellArg(params.q)}`,
|
|
7798
7988
|
params.user_id ? `--user-id=${shellArg(params.user_id)}` : "",
|
|
@@ -7801,7 +7991,7 @@ async function searchFallbackToTraces(params) {
|
|
|
7801
7991
|
].filter(Boolean).join(" ");
|
|
7802
7992
|
let traces;
|
|
7803
7993
|
try {
|
|
7804
|
-
traces = asRecord(await callDataAPI(`/conversations?${
|
|
7994
|
+
traces = asRecord(await callDataAPI(`/conversations?${query2.toString()}`)) ?? {};
|
|
7805
7995
|
} catch (error) {
|
|
7806
7996
|
const reason = error instanceof Error ? error.message : String(error);
|
|
7807
7997
|
return {
|
|
@@ -7950,16 +8140,16 @@ function assertHandParsedFlags(command, flags, positionals) {
|
|
|
7950
8140
|
var PROFILE_MANAGING_COMMANDS = new Set(["profiles", "auth", "init", "provision", "status", "config", "keys", "manifest", "agent", "doctor"]);
|
|
7951
8141
|
async function runProblemsRemainder(flags, context) {
|
|
7952
8142
|
const params = parseCommandFlags(ProblemsRemainderSchema, flagsToArgs(flags), "problems remainder");
|
|
7953
|
-
const
|
|
8143
|
+
const query2 = new URLSearchParams;
|
|
7954
8144
|
if (params.days_back)
|
|
7955
|
-
|
|
8145
|
+
query2.set("days_back", params.days_back.toString());
|
|
7956
8146
|
if (params.limit)
|
|
7957
|
-
|
|
8147
|
+
query2.set("limit", params.limit.toString());
|
|
7958
8148
|
if (params.family)
|
|
7959
|
-
|
|
8149
|
+
query2.set("family", params.family);
|
|
7960
8150
|
if (params.cursor)
|
|
7961
|
-
|
|
7962
|
-
const queryString =
|
|
8151
|
+
query2.set("cursor", params.cursor);
|
|
8152
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
7963
8153
|
context.output.writeData(await callDataAPI(`/problems/remainder${queryString}`));
|
|
7964
8154
|
}
|
|
7965
8155
|
function processingDisabledWarning(data, family) {
|
|
@@ -8155,28 +8345,28 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8155
8345
|
const args = flagsToArgs(flags);
|
|
8156
8346
|
const params = parseCommandFlags(ClustersSchema, args, command);
|
|
8157
8347
|
if (params.search !== undefined || params.node_id !== undefined) {
|
|
8158
|
-
const
|
|
8348
|
+
const query3 = new URLSearchParams;
|
|
8159
8349
|
if (params.search !== undefined)
|
|
8160
|
-
|
|
8350
|
+
query3.set("q", params.search);
|
|
8161
8351
|
if (params.node_id !== undefined)
|
|
8162
|
-
|
|
8352
|
+
query3.set("node_id", params.node_id);
|
|
8163
8353
|
if (params.mode)
|
|
8164
|
-
|
|
8354
|
+
query3.set("mode", params.mode);
|
|
8165
8355
|
if (params.limit)
|
|
8166
|
-
|
|
8167
|
-
const data2 = await callDataAPI(`/task-clusters/search?${
|
|
8356
|
+
query3.set("limit", params.limit.toString());
|
|
8357
|
+
const data2 = await callDataAPI(`/task-clusters/search?${query3.toString()}`);
|
|
8168
8358
|
const record = asRecord(data2) ?? {};
|
|
8169
8359
|
const matches = Array.isArray(record.matches) ? record.matches : [];
|
|
8170
8360
|
const warnings = params.node_id !== undefined && matches.length === 0 ? notFoundWarning("cluster", params.node_id, true) : [];
|
|
8171
8361
|
context.output.writeData(data2, warnings.length > 0 ? { warnings } : undefined);
|
|
8172
8362
|
break;
|
|
8173
8363
|
}
|
|
8174
|
-
const
|
|
8364
|
+
const query2 = new URLSearchParams;
|
|
8175
8365
|
if (params.parent_id)
|
|
8176
|
-
|
|
8366
|
+
query2.set("parent_id", params.parent_id);
|
|
8177
8367
|
if (params.time_range)
|
|
8178
|
-
|
|
8179
|
-
const queryString =
|
|
8368
|
+
query2.set("time_range", params.time_range);
|
|
8369
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8180
8370
|
const data = await callDataAPI(`/clusters${queryString}`);
|
|
8181
8371
|
context.output.writeData(data);
|
|
8182
8372
|
break;
|
|
@@ -8188,12 +8378,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8188
8378
|
}
|
|
8189
8379
|
const args = flagsToArgs(flags, "node_id", positional);
|
|
8190
8380
|
const params = parseCommandFlags(ClusterConversationsSchema, args, command, { positionalKey: "node_id" });
|
|
8191
|
-
const
|
|
8381
|
+
const query2 = new URLSearchParams;
|
|
8192
8382
|
if (params.limit)
|
|
8193
|
-
|
|
8383
|
+
query2.set("limit", params.limit.toString());
|
|
8194
8384
|
if (params.offset !== undefined)
|
|
8195
|
-
|
|
8196
|
-
const queryString =
|
|
8385
|
+
query2.set("offset", params.offset.toString());
|
|
8386
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8197
8387
|
const data = await callDataAPI(`/clusters/${params.node_id}/conversations${queryString}`);
|
|
8198
8388
|
const clusterRecord = asRecord(data) ?? {};
|
|
8199
8389
|
const warnings = notFoundWarning("cluster", params.node_id, clusterRecord.cluster === null || clusterRecord.cluster === undefined);
|
|
@@ -8204,28 +8394,28 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8204
8394
|
case "conversations": {
|
|
8205
8395
|
const args = withStringIdFlags(flagsToArgs(flags), flags);
|
|
8206
8396
|
const params = parseCommandFlags(ConversationsSchema, args, command);
|
|
8207
|
-
const
|
|
8397
|
+
const query2 = new URLSearchParams;
|
|
8208
8398
|
if (params.search)
|
|
8209
|
-
|
|
8399
|
+
query2.set("search", params.search);
|
|
8210
8400
|
if (params.cluster_id)
|
|
8211
|
-
|
|
8401
|
+
query2.set("cluster_id", params.cluster_id);
|
|
8212
8402
|
if (params.user_id)
|
|
8213
|
-
|
|
8403
|
+
query2.set("user_id", params.user_id);
|
|
8214
8404
|
if (params.time_range)
|
|
8215
|
-
|
|
8405
|
+
query2.set("time_range", params.time_range);
|
|
8216
8406
|
if (params.environment)
|
|
8217
|
-
|
|
8407
|
+
query2.set("environment", params.environment);
|
|
8218
8408
|
if (params.world_state)
|
|
8219
|
-
|
|
8409
|
+
query2.set("world_state", params.world_state);
|
|
8220
8410
|
if (params.outcome && params.outcome !== "any")
|
|
8221
|
-
|
|
8411
|
+
query2.set("outcome", params.outcome);
|
|
8222
8412
|
if (params.sort)
|
|
8223
|
-
|
|
8413
|
+
query2.set("sort_by", params.sort);
|
|
8224
8414
|
if (params.limit)
|
|
8225
|
-
|
|
8415
|
+
query2.set("limit", params.limit.toString());
|
|
8226
8416
|
if (params.offset !== undefined)
|
|
8227
|
-
|
|
8228
|
-
const queryString =
|
|
8417
|
+
query2.set("offset", params.offset.toString());
|
|
8418
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8229
8419
|
const raw = await callDataAPI(`/conversations${queryString}`);
|
|
8230
8420
|
recordConversationsTelemetry(context, raw);
|
|
8231
8421
|
const searchWarnings = [];
|
|
@@ -8262,19 +8452,19 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8262
8452
|
}
|
|
8263
8453
|
const args = withStringIdFlags(flagsToArgs(flags, "q", positional), flags);
|
|
8264
8454
|
const params = parseCommandFlags(SearchSchema, args, command, { positionalKey: "q" });
|
|
8265
|
-
const
|
|
8266
|
-
|
|
8455
|
+
const query2 = new URLSearchParams;
|
|
8456
|
+
query2.set("q", params.q);
|
|
8267
8457
|
if (params.mode)
|
|
8268
|
-
|
|
8458
|
+
query2.set("mode", params.mode);
|
|
8269
8459
|
if (params.user_id)
|
|
8270
|
-
|
|
8460
|
+
query2.set("user_id", params.user_id);
|
|
8271
8461
|
if (params.time_range)
|
|
8272
|
-
|
|
8462
|
+
query2.set("time_range", params.time_range);
|
|
8273
8463
|
if (params.limit)
|
|
8274
|
-
|
|
8464
|
+
query2.set("limit", params.limit.toString());
|
|
8275
8465
|
if (params.include_tool_io)
|
|
8276
|
-
|
|
8277
|
-
const data = await callDataAPI(`/search?${
|
|
8466
|
+
query2.set("include_tool_io", "true");
|
|
8467
|
+
const data = await callDataAPI(`/search?${query2.toString()}`);
|
|
8278
8468
|
recordSearchTelemetry(context, data);
|
|
8279
8469
|
const searchRecord = asRecord(data) ?? {};
|
|
8280
8470
|
const degradeReason = asString(searchRecord.degrade_reason);
|
|
@@ -8322,13 +8512,13 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8322
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]]");
|
|
8323
8513
|
}
|
|
8324
8514
|
if (params.snapshot === true) {
|
|
8325
|
-
const
|
|
8326
|
-
|
|
8515
|
+
const query3 = new URLSearchParams;
|
|
8516
|
+
query3.set("msg_index", (params.msg_index ?? 0).toString());
|
|
8327
8517
|
if (params.block_index !== undefined)
|
|
8328
|
-
|
|
8518
|
+
query3.set("block_index", params.block_index.toString());
|
|
8329
8519
|
if (params.segment_index !== undefined)
|
|
8330
|
-
|
|
8331
|
-
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()}`);
|
|
8332
8522
|
const snapshotRecord = asRecord(data2) ?? {};
|
|
8333
8523
|
const warnings2 = notFoundWarning("world state", params.conversation_id, snapshotRecord.found === false);
|
|
8334
8524
|
context.output.writeData(data2, warnings2.length > 0 ? { warnings: warnings2 } : undefined);
|
|
@@ -8339,22 +8529,22 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8339
8529
|
if (format === "frames" && params.message_count === undefined) {
|
|
8340
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.");
|
|
8341
8531
|
}
|
|
8342
|
-
const
|
|
8343
|
-
|
|
8532
|
+
const query3 = new URLSearchParams;
|
|
8533
|
+
query3.set("format", format === "fold-inputs" ? "fold_inputs" : "frames");
|
|
8344
8534
|
if (params.message_count !== undefined)
|
|
8345
|
-
|
|
8346
|
-
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()}`);
|
|
8347
8537
|
const replayRecord = asRecord(data2) ?? {};
|
|
8348
8538
|
const warnings2 = notFoundWarning("world state", params.conversation_id, replayRecord.hasData === false);
|
|
8349
8539
|
context.output.writeData(data2, warnings2.length > 0 ? { warnings: warnings2 } : undefined);
|
|
8350
8540
|
break;
|
|
8351
8541
|
}
|
|
8352
|
-
const
|
|
8542
|
+
const query2 = new URLSearchParams;
|
|
8353
8543
|
if (params.summary_only === true)
|
|
8354
|
-
|
|
8544
|
+
query2.set("summary_only", "1");
|
|
8355
8545
|
if (params.event_limit)
|
|
8356
|
-
|
|
8357
|
-
const queryString =
|
|
8546
|
+
query2.set("event_limit", params.event_limit.toString());
|
|
8547
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8358
8548
|
const data = await callDataAPI(`/conversations/${params.conversation_id}/world-state${queryString}`);
|
|
8359
8549
|
const wsRecord = asRecord(data) ?? {};
|
|
8360
8550
|
const warnings = notFoundWarning("world state", params.conversation_id, wsRecord.hasData === false);
|
|
@@ -8386,12 +8576,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8386
8576
|
if (params.from !== undefined || params.max_messages !== undefined) {
|
|
8387
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]");
|
|
8388
8578
|
}
|
|
8389
|
-
const
|
|
8579
|
+
const query2 = new URLSearchParams;
|
|
8390
8580
|
if (params.msg_index !== undefined)
|
|
8391
|
-
|
|
8581
|
+
query2.set("msg_index", params.msg_index.toString());
|
|
8392
8582
|
if (params.window)
|
|
8393
|
-
|
|
8394
|
-
const queryString =
|
|
8583
|
+
query2.set("window", params.window.toString());
|
|
8584
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8395
8585
|
const data = await callDataAPI(`/conversations/${params.conversation_id}/context${queryString}`);
|
|
8396
8586
|
const ctxRecord = asRecord(data) ?? {};
|
|
8397
8587
|
const warnings = notFoundWarning("trace", params.conversation_id, asNumber(ctxRecord.total_messages) === 0);
|
|
@@ -8405,14 +8595,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8405
8595
|
}
|
|
8406
8596
|
const args = flagsToArgs(flags, "id", positional);
|
|
8407
8597
|
const params = parseCommandFlags(AuditSchema, args, command, { positionalKey: "id" });
|
|
8408
|
-
const
|
|
8598
|
+
const query2 = new URLSearchParams;
|
|
8409
8599
|
if (params.kind && params.kind !== "auto")
|
|
8410
|
-
|
|
8600
|
+
query2.set("kind", params.kind);
|
|
8411
8601
|
if (params.include_raw === true)
|
|
8412
|
-
|
|
8602
|
+
query2.set("include_raw", "1");
|
|
8413
8603
|
if (params.limit)
|
|
8414
|
-
|
|
8415
|
-
const queryString =
|
|
8604
|
+
query2.set("limit", params.limit.toString());
|
|
8605
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8416
8606
|
const data = await callDataAPI(`/audit/${encodeURIComponent(params.id)}${queryString}`);
|
|
8417
8607
|
const capped = auditCapStatus(data, params.limit ?? AUDIT_DEFAULT_LIMIT);
|
|
8418
8608
|
const auditRecord = asRecord(data);
|
|
@@ -8445,19 +8635,19 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8445
8635
|
case "frustrations": {
|
|
8446
8636
|
const args = withStringIdFlags(flagsToArgs(flags), flags);
|
|
8447
8637
|
const params = parseCommandFlags(FrustrationsSchema, args, command);
|
|
8448
|
-
const
|
|
8638
|
+
const query2 = new URLSearchParams;
|
|
8449
8639
|
if (params.days_back)
|
|
8450
|
-
|
|
8640
|
+
query2.set("days_back", params.days_back.toString());
|
|
8451
8641
|
if (params.limit)
|
|
8452
|
-
|
|
8642
|
+
query2.set("limit", params.limit.toString());
|
|
8453
8643
|
if (params.offset !== undefined)
|
|
8454
|
-
|
|
8644
|
+
query2.set("offset", params.offset.toString());
|
|
8455
8645
|
if (params.user_id)
|
|
8456
|
-
|
|
8646
|
+
query2.set("user_id", params.user_id);
|
|
8457
8647
|
if (params.conversation_id)
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
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()}`));
|
|
8461
8651
|
await printWithOptionalWindows(withFrustrationAnchors(data), "frustrations", params.include_window === true, params.window ?? 1, context.output, processingDisabledWarning(data, "frustration"));
|
|
8462
8652
|
break;
|
|
8463
8653
|
}
|
|
@@ -8486,15 +8676,15 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8486
8676
|
if (params.compact === true && params.include_window === true) {
|
|
8487
8677
|
throw new CliInputError("--compact drops the conversation window; drop --include-window, or drop --compact.");
|
|
8488
8678
|
}
|
|
8489
|
-
const
|
|
8679
|
+
const query2 = new URLSearchParams;
|
|
8490
8680
|
if (params.subtype)
|
|
8491
|
-
|
|
8681
|
+
query2.set("subtype", params.subtype);
|
|
8492
8682
|
if (params.days_back)
|
|
8493
|
-
|
|
8494
|
-
|
|
8683
|
+
query2.set("days_back", params.days_back.toString());
|
|
8684
|
+
query2.set("limit", (params.limit ?? TOOL_FAILURE_DETAIL_DEFAULT_LIMIT).toString());
|
|
8495
8685
|
if (params.offset !== undefined)
|
|
8496
|
-
|
|
8497
|
-
const queryString =
|
|
8686
|
+
query2.set("offset", params.offset.toString());
|
|
8687
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8498
8688
|
const data = await callDataAPI(`/tool-failures/${params.tool_name}${queryString}`);
|
|
8499
8689
|
const tfRecord = asRecord(data) ?? {};
|
|
8500
8690
|
const subtypeCount = Array.isArray(tfRecord.subtypes) ? tfRecord.subtypes.length : 0;
|
|
@@ -8528,7 +8718,7 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8528
8718
|
}
|
|
8529
8719
|
case "problem-impact": {
|
|
8530
8720
|
const params = ProblemImpactSchema.parse(Object.fromEntries(Object.entries(flags).map(([key, value]) => [key.replace(/-/g, "_"), value])));
|
|
8531
|
-
const
|
|
8721
|
+
const query2 = new URLSearchParams({ days: String(params.days_back), mode: params.mode });
|
|
8532
8722
|
for (const [key, value] of Object.entries({
|
|
8533
8723
|
nodeId: params.node_id,
|
|
8534
8724
|
problemId: params.problem_id,
|
|
@@ -8538,8 +8728,8 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8538
8728
|
conversationIds: params.conversation_ids ? JSON.stringify(params.conversation_ids) : undefined
|
|
8539
8729
|
}))
|
|
8540
8730
|
if (value !== undefined)
|
|
8541
|
-
|
|
8542
|
-
const data = asRecord(await callDataAPI(`/problem-impact?${
|
|
8731
|
+
query2.set(key, value);
|
|
8732
|
+
const data = asRecord(await callDataAPI(`/problem-impact?${query2}`)) ?? {};
|
|
8543
8733
|
const link = new URLSearchParams({ impactDays: String(params.days_back) });
|
|
8544
8734
|
const window = asRecord(data.window);
|
|
8545
8735
|
const resolvedRun = asString(data.runId);
|
|
@@ -8566,12 +8756,12 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8566
8756
|
}
|
|
8567
8757
|
const args = flagsToArgs(flags);
|
|
8568
8758
|
const params = parseCommandFlags(ProblemsSchema, args, command);
|
|
8569
|
-
const
|
|
8759
|
+
const query2 = new URLSearchParams;
|
|
8570
8760
|
if (params.days_back)
|
|
8571
|
-
|
|
8761
|
+
query2.set("days_back", params.days_back.toString());
|
|
8572
8762
|
if (params.limit)
|
|
8573
|
-
|
|
8574
|
-
const queryString =
|
|
8763
|
+
query2.set("limit", params.limit.toString());
|
|
8764
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8575
8765
|
const data = await callDataAPI(`/problems${queryString}`);
|
|
8576
8766
|
const { payload, truncated } = capProblemsBlocks(asRecord(data) ?? {}, params.limit ?? 25);
|
|
8577
8767
|
const withLink = {
|
|
@@ -8613,18 +8803,18 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8613
8803
|
const viewFlag = view === "conversations" ? "--traces" : `--${view}`;
|
|
8614
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.`);
|
|
8615
8805
|
}
|
|
8616
|
-
const
|
|
8806
|
+
const query2 = new URLSearchParams;
|
|
8617
8807
|
if (params.limit)
|
|
8618
|
-
|
|
8808
|
+
query2.set("limit", params.limit.toString());
|
|
8619
8809
|
if (params.cursor)
|
|
8620
|
-
|
|
8810
|
+
query2.set("cursor", params.cursor);
|
|
8621
8811
|
if (view === "conversations") {
|
|
8622
8812
|
if (params.family)
|
|
8623
|
-
|
|
8813
|
+
query2.set("family", params.family);
|
|
8624
8814
|
if (params.door)
|
|
8625
|
-
|
|
8815
|
+
query2.set("door", params.door);
|
|
8626
8816
|
}
|
|
8627
|
-
const queryString =
|
|
8817
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8628
8818
|
const data = await callDataAPI(`/problems/${params.id}/${view}${queryString}`);
|
|
8629
8819
|
context.output.writeData(data);
|
|
8630
8820
|
break;
|
|
@@ -8760,14 +8950,14 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8760
8950
|
context.output.writeData(data2);
|
|
8761
8951
|
break;
|
|
8762
8952
|
}
|
|
8763
|
-
const
|
|
8953
|
+
const query2 = new URLSearchParams;
|
|
8764
8954
|
if (params.days_back)
|
|
8765
|
-
|
|
8955
|
+
query2.set("days_back", params.days_back.toString());
|
|
8766
8956
|
if (params.limit)
|
|
8767
|
-
|
|
8957
|
+
query2.set("limit", params.limit.toString());
|
|
8768
8958
|
if (params.offset !== undefined)
|
|
8769
|
-
|
|
8770
|
-
const queryString =
|
|
8959
|
+
query2.set("offset", params.offset.toString());
|
|
8960
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8771
8961
|
const data = await callDataAPI(`/custom-signals/${id}/detections${queryString}`);
|
|
8772
8962
|
context.output.writeData(data);
|
|
8773
8963
|
break;
|
|
@@ -8919,20 +9109,20 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8919
9109
|
case "false-positives": {
|
|
8920
9110
|
if (!positional) {
|
|
8921
9111
|
const kinds = { quote: "frustration_quote", tool: "tool_call", laziness: "laziness", hallucination: "hallucination", "custom-signal": "custom_signal" };
|
|
8922
|
-
const
|
|
9112
|
+
const query2 = new URLSearchParams;
|
|
8923
9113
|
if (flags.kind !== undefined) {
|
|
8924
9114
|
if (!kinds[flags.kind])
|
|
8925
9115
|
throw new CliInputError(`--kind must be one of ${Object.keys(kinds).join("|")}`);
|
|
8926
|
-
|
|
9116
|
+
query2.set("kind", kinds[flags.kind]);
|
|
8927
9117
|
}
|
|
8928
9118
|
if (flags.limit !== undefined) {
|
|
8929
9119
|
if (!/^\d+$/.test(flags.limit) || Number(flags.limit) < 1 || Number(flags.limit) > 200)
|
|
8930
9120
|
throw new CliInputError("--limit must be 1-200");
|
|
8931
|
-
|
|
9121
|
+
query2.set("limit", flags.limit);
|
|
8932
9122
|
}
|
|
8933
9123
|
if (flags.all !== undefined && flags.all !== "false")
|
|
8934
|
-
|
|
8935
|
-
const queryString =
|
|
9124
|
+
query2.set("include_cleared", "true");
|
|
9125
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8936
9126
|
const data2 = await callDataAPI(`/false-positives${queryString}`);
|
|
8937
9127
|
context.output.writeData(data2);
|
|
8938
9128
|
break;
|
|
@@ -8944,20 +9134,20 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8944
9134
|
case "emotions": {
|
|
8945
9135
|
const args = withStringIdFlags(flagsToArgs(flags), flags);
|
|
8946
9136
|
const params = parseCommandFlags(EmotionsSchema, args, command);
|
|
8947
|
-
const
|
|
9137
|
+
const query2 = new URLSearchParams;
|
|
8948
9138
|
if (params.days_back)
|
|
8949
|
-
|
|
9139
|
+
query2.set("days_back", params.days_back.toString());
|
|
8950
9140
|
if (params.limit)
|
|
8951
|
-
|
|
9141
|
+
query2.set("limit", params.limit.toString());
|
|
8952
9142
|
if (params.offset !== undefined)
|
|
8953
|
-
|
|
9143
|
+
query2.set("offset", params.offset.toString());
|
|
8954
9144
|
if (params.family)
|
|
8955
|
-
|
|
9145
|
+
query2.set("family", params.family);
|
|
8956
9146
|
if (params.user_id)
|
|
8957
|
-
|
|
9147
|
+
query2.set("user_id", params.user_id);
|
|
8958
9148
|
if (params.conversation_id)
|
|
8959
|
-
|
|
8960
|
-
const queryString =
|
|
9149
|
+
query2.set("conversation_id", params.conversation_id);
|
|
9150
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8961
9151
|
const data = await callDataAPI(`/emotions${queryString}`);
|
|
8962
9152
|
const processingWarnings = processingDisabledWarning(data, "emotion");
|
|
8963
9153
|
context.output.writeData(data, processingWarnings.length > 0 ? { warnings: processingWarnings } : undefined);
|
|
@@ -8969,18 +9159,18 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8969
9159
|
args.conversation_id = flags["conversation-id"];
|
|
8970
9160
|
}
|
|
8971
9161
|
const params = parseCommandFlags(LazinessSchema, args, command);
|
|
8972
|
-
const
|
|
9162
|
+
const query2 = new URLSearchParams;
|
|
8973
9163
|
if (params.days_back)
|
|
8974
|
-
|
|
9164
|
+
query2.set("days_back", params.days_back.toString());
|
|
8975
9165
|
if (params.limit)
|
|
8976
|
-
|
|
9166
|
+
query2.set("limit", params.limit.toString());
|
|
8977
9167
|
if (params.offset !== undefined)
|
|
8978
|
-
|
|
9168
|
+
query2.set("offset", params.offset.toString());
|
|
8979
9169
|
if (params.conversation_id)
|
|
8980
|
-
|
|
9170
|
+
query2.set("conversation_id", params.conversation_id);
|
|
8981
9171
|
if (params.family)
|
|
8982
|
-
|
|
8983
|
-
const queryString =
|
|
9172
|
+
query2.set("family", params.family);
|
|
9173
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
8984
9174
|
const data = await callDataAPI(`/laziness${queryString}`);
|
|
8985
9175
|
context.output.writeData(data);
|
|
8986
9176
|
break;
|
|
@@ -8991,18 +9181,18 @@ async function runCommand(command, positional, flags, positionals = positional ?
|
|
|
8991
9181
|
args.conversation_id = flags["conversation-id"];
|
|
8992
9182
|
}
|
|
8993
9183
|
const params = parseCommandFlags(HallucinationsSchema, args, command);
|
|
8994
|
-
const
|
|
9184
|
+
const query2 = new URLSearchParams;
|
|
8995
9185
|
if (params.days_back)
|
|
8996
|
-
|
|
9186
|
+
query2.set("days_back", params.days_back.toString());
|
|
8997
9187
|
if (params.limit)
|
|
8998
|
-
|
|
9188
|
+
query2.set("limit", params.limit.toString());
|
|
8999
9189
|
if (params.offset !== undefined)
|
|
9000
|
-
|
|
9190
|
+
query2.set("offset", params.offset.toString());
|
|
9001
9191
|
if (params.conversation_id)
|
|
9002
|
-
|
|
9192
|
+
query2.set("conversation_id", params.conversation_id);
|
|
9003
9193
|
if (params.kind)
|
|
9004
|
-
|
|
9005
|
-
const queryString =
|
|
9194
|
+
query2.set("kind", params.kind);
|
|
9195
|
+
const queryString = query2.toString() ? `?${query2.toString()}` : "";
|
|
9006
9196
|
const data = await callDataAPI(`/hallucinations${queryString}`);
|
|
9007
9197
|
context.output.writeData(data);
|
|
9008
9198
|
break;
|
|
@@ -9478,6 +9668,23 @@ var commandRegistry = createCommandRegistry([
|
|
|
9478
9668
|
telemetry: "result-and-error",
|
|
9479
9669
|
handler: runUserCommand
|
|
9480
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
|
+
},
|
|
9481
9688
|
{
|
|
9482
9689
|
name: "problem-reopen",
|
|
9483
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 |
|