codiedev 0.9.77 → 0.9.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -95,11 +95,14 @@ Starter packs:
95
95
 
96
96
  Traces (agents on the gateway):
97
97
  codiedev traces agents Agents you maintain, with saved test cases
98
- codiedev traces cases <agent> [--dataset name] [--model id]
98
+ codiedev traces cases <agent> [--dataset name [--version n]] [--model id]
99
99
  Pull an agent's test cases (or one dataset)
100
100
  into this repo for your coding agent to
101
101
  run, optionally on another model
102
102
  codiedev traces report <results.json> Send the results back to Traces
103
+ codiedev traces overview | runs | run | score | scores | score-kinds | queue | judge | import | add-example | versions | save-version
104
+ Agents, runs, failures, scores, review queues,
105
+ automatic scoring and datasets (codiedev traces help)
103
106
  codiedev evals report <run.json> File one variant's eval scores under an experiment
104
107
  codiedev evals last <subject> Latest experiment on a subject + its champion
105
108
 
@@ -27,6 +27,9 @@ interface CasesResponse {
27
27
  ok: boolean;
28
28
  /** The suite pulled, when --suite was given. */
29
29
  suite: string | null;
30
+ /** The saved dataset version pulled, when --version was given; cases deleted since are counted in `missing`. */
31
+ version?: number | null;
32
+ missing?: number;
30
33
  /** How the suite is judged, when it has settings. */
31
34
  judge: {
32
35
  model: string | null;
@@ -44,6 +47,7 @@ interface CasesResponse {
44
47
  export interface ResultsFile {
45
48
  agent: string;
46
49
  suite: string | null;
50
+ version?: number | null;
47
51
  model: string;
48
52
  judgeModel: string | null;
49
53
  harness: string | null;
@@ -2,6 +2,7 @@
2
2
  // `codiedev traces agents`
3
3
  // `codiedev traces cases <agent> [--dataset name] [--model id] [--out dir] [--json]`
4
4
  // `codiedev traces report <results.json> [--harness name]`
5
+ // (scores, review queues, automatic scoring and datasets: ./tracesLabel.ts)
5
6
  //
6
7
  // Test cases are runs an engineer saved from Codiedev → Traces. They run in
7
8
  // the engineer's coding agent, not in the portal: `cases` writes them into the
@@ -51,14 +52,15 @@ exports.runTraces = runTraces;
51
52
  const fs = __importStar(require("fs"));
52
53
  const path = __importStar(require("path"));
53
54
  const shared_1 = require("./shared");
55
+ const tracesLabel_1 = require("./tracesLabel");
54
56
  exports.TRACES_HELP = `
55
- codiedev traces — test cases saved from Codiedev → Traces
57
+ codiedev traces — test cases, scores, review queues and automatic scoring from Codiedev → Traces
56
58
 
57
59
  codiedev traces agents Agents you maintain, with saved case counts
58
- codiedev traces cases <agent> [--dataset name] [--case id[,id]] [--model id] [--out dir] [--json]
60
+ codiedev traces cases <agent> [--dataset name [--version n]] [--case id[,id]] [--model id] [--out dir] [--json]
59
61
  Write an agent's cases (one dataset, or just
60
62
  the cases named) into this repo, default
61
- .codiedev/traces/<agent>[/<dataset>]/, then
63
+ .codiedev/traces/<agent>[/<dataset>[/v<n>]]/, then
62
64
  ask your coding agent to run them.
63
65
  --model runs them on another model, e.g.
64
66
  the replacement for a retiring one
@@ -66,6 +68,36 @@ codiedev traces — test cases saved from Codiedev → Traces
66
68
  Send the filled-in results back, so
67
69
  Traces shows the pass rate for that model
68
70
 
71
+ codiedev traces overview [--days n] [--json]
72
+ Every agent you can see: runs, failures,
73
+ spend, and what's going wrong most
74
+ codiedev traces runs <agent> [--failed] [--flag name] [--model id] [--verdict pass|fail] [--days n] [--limit n] [--json]
75
+ An agent's runs, newest first
76
+ codiedev traces run <run> [--agent name] [--json]
77
+ One run: steps, what went wrong and the fix,
78
+ scores, prompts and replies (maintainers)
79
+ codiedev traces score-kinds [add <name> --type number|categories|boolean [--min n --max n] [--categories a,b]]
80
+ List or add the kinds of score your team uses
81
+ codiedev traces queue create <name> [--scores a,b] [--description text]
82
+ codiedev traces score <run> <score> <value> [--agent name] [--comment text] [--step n]
83
+ Score a run (a portal run id agent~run, or
84
+ the agent's own run id with --agent)
85
+ codiedev traces scores <agent> [--days n] [--json]
86
+ Each score's average or mix, and the score kinds
87
+ codiedev traces queue list Review queues and how many are left
88
+ codiedev traces queue add <queue> --agent <name> [--failed] [--days n] [--run id ...]
89
+ File runs into a queue for people to review
90
+ codiedev traces judge <agent> [--on | --off] [--scores a,b] [--instructions text]
91
+ [--model id] [--sample percent] [--daily n] [--budget usd | --key cdvm_…]
92
+ Show or set automatic scoring (a judge model
93
+ scores a sample of runs, billed to your key)
94
+ codiedev traces import <agent> <file.csv|file.jsonl> --dataset <name>
95
+ Add examples from a file (input, expected, name)
96
+ codiedev traces add-example <agent> --dataset <name> --input <text> [--expected <text>] [--name <text>]
97
+ codiedev traces versions <dataset> A dataset's saved versions and what changed
98
+ codiedev traces save-version <dataset> [--note text]
99
+ Save the dataset as its next version
100
+
69
101
  <agent> is the agent's name (its API key name) or id. Only an agent's
70
102
  maintainers can pull its cases or report on them.
71
103
  `.trim();
@@ -78,7 +110,7 @@ const FLAG_LABEL = {
78
110
  };
79
111
  const TRIES = 3;
80
112
  const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
81
- const recordedModel = (res) => res.cases.flatMap((c) => c.steps.map((s) => s.model))[0] ?? "the recorded model";
113
+ const recordedModel = (res) => res.cases.flatMap((c) => c.steps.map((s) => s.model)).filter(Boolean)[0] ?? "the agent's current model";
82
114
  function slugify(name) {
83
115
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
84
116
  }
@@ -90,7 +122,7 @@ function casesBrief(res, casesFile, opts = {}) {
90
122
  const target = opts.model;
91
123
  const results = opts.resultsFile ? path.basename(opts.resultsFile) : "results.json";
92
124
  const lines = [
93
- `# Test cases for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${target ? ` on ${target}` : ""}`,
125
+ `# Test cases for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${res.version ? ` v${res.version}` : ""}${target ? ` on ${target}` : ""}`,
94
126
  "",
95
127
  res.agent.description ? `${res.agent.description}\n` : "",
96
128
  `Saved from Codiedev → Traces. ${plural(res.cases.length, "case")} in \`${path.basename(casesFile)}\` (one JSON object per line):`,
@@ -101,7 +133,7 @@ function casesBrief(res, casesFile, opts = {}) {
101
133
  if (target) {
102
134
  lines.push("## This is a model check", "", `Run every case on **${target}** instead of ${recordedModel(res)} (the model the cases were recorded on). Change only the model the agent asks the gateway for; keep its prompts and tools as they are, so the result says how ${target} does on the same work.`, "");
103
135
  }
104
- lines.push("## How to run them", "", "1. Find where this agent builds its model requests and calls the Codiedev gateway.", `2. For each case, replay \`steps[0].request\` (the first prompt the agent sent) through the agent's own code path, so tools and prompts are the current version, not the recorded one${target ? `, with the model set to ${target}` : ""}.`, "3. Judge each try against the case:", " - A case's `expected`, when present, is what a passing answer looks like (a reference reply or criteria). It wins over everything below.", " - `fix` cases: the run must not reproduce its `flags` (listed below), and must finish (no failed call, no `finish_reason: \"length\"`, no repeated identical tool call).", " - `keep` cases: the final reply must still do what the recorded final reply did. Compare meaning, not wording.", " - A case's `note`, when present, is the engineer's extra context.");
136
+ lines.push("## How to run them", "", "1. Find where this agent builds its model requests and calls the Codiedev gateway.", `2. For each case, replay \`steps[0].request\` (the first prompt the agent sent) through the agent's own code path, so tools and prompts are the current version, not the recorded one${target ? `, with the model set to ${target}` : ""}.`, "3. Judge each try against the case:", " - A case's `expected`, when present, is what a passing answer looks like (a reference reply or criteria). It wins over everything below.", " - `fix` cases: the run must not reproduce its `flags` (listed below), and must finish (no failed call, no `finish_reason: \"length\"`, no repeated identical tool call).", " - `keep` cases: the final reply must still do what the recorded final reply did. Compare meaning, not wording.", " - A case's `note`, when present, is the engineer's extra context.", " - Cases whose `runId` starts with `manual:` or `import:` were written by hand, not recorded: `steps[0].request` is the input and there's no recorded reply, so judge them on `expected`.");
105
137
  if (res.judge?.model || res.judge?.rubric) {
106
138
  lines.push(` - Where passing needs judgement, judge with **${res.judge.model ?? "a strong model"}** through the gateway${res.judge.rubric ? `, using this rubric: "${res.judge.rubric}"` : ""}. Use the same judge for every model so results compare.`);
107
139
  }
@@ -121,6 +153,7 @@ function resultsTemplate(res, model) {
121
153
  return {
122
154
  agent: res.agent.name,
123
155
  suite: res.suite,
156
+ ...(res.version ? { version: res.version } : {}),
124
157
  model: model ?? recordedModel(res),
125
158
  judgeModel: res.judge?.model ?? null,
126
159
  harness: null,
@@ -140,7 +173,7 @@ function parseCasesArgs(args) {
140
173
  for (let i = 0; i < args.length; i++) {
141
174
  const a = args[i];
142
175
  let matched = false;
143
- for (const [name, key] of [["--out", "out"], ["-o", "out"], ["--dataset", "suite"], ["--suite", "suite"], ["--model", "model"], ["--case", "caseIds"]]) {
176
+ for (const [name, key] of [["--out", "out"], ["-o", "out"], ["--dataset", "suite"], ["--suite", "suite"], ["--model", "model"], ["--case", "caseIds"], ["--version", "version"]]) {
144
177
  const f = flag(args, i, name);
145
178
  if (f.skip >= 0) {
146
179
  parsed[key] = f.value;
@@ -182,20 +215,26 @@ async function runAgents(config) {
182
215
  console.log("\nPull one: codiedev traces cases <agent>");
183
216
  }
184
217
  async function runCases(args, config) {
185
- const { agent, suite, model, caseIds, out, json } = parseCasesArgs(args);
218
+ const { agent, suite, model, caseIds, version, out, json } = parseCasesArgs(args);
186
219
  if (!agent) {
187
- console.error("Usage: codiedev traces cases <agent> [--dataset name] [--model id] [--out dir] [--json]");
220
+ console.error("Usage: codiedev traces cases <agent> [--dataset name [--version n]] [--model id] [--out dir] [--json]");
221
+ process.exit(1);
222
+ }
223
+ if (version !== undefined && (!suite || !/^[1-9]\d*$/.test(version))) {
224
+ console.error("--version takes a whole number and needs --dataset: codiedev traces cases <agent> --dataset <name> --version <n>");
188
225
  process.exit(1);
189
226
  }
190
227
  const start = Date.now();
191
228
  let res;
192
229
  try {
193
- res = await (0, shared_1.apiRequest)("GET", "/api/cli/traces/cases", { config, query: { agent, ...(suite ? { suite } : {}), ...(caseIds ? { case: caseIds } : {}) } });
230
+ res = await (0, shared_1.apiRequest)("GET", "/api/cli/traces/cases", { config, query: { agent, ...(suite ? { suite } : {}), ...(version ? { version } : {}), ...(caseIds ? { case: caseIds } : {}) } });
194
231
  }
195
232
  catch (err) {
196
233
  const e = err;
197
234
  (0, shared_1.recordAgentEvent)(config, { tool: "codiedev_traces_cases", ok: false, latencyMs: Date.now() - start, error: e.message });
198
- if (e.status === 404)
235
+ if (e.status === 404 && version && /No version/.test(e.message))
236
+ console.error(`No version ${version} of "${suite}". See its versions in Codiedev → Traces → Test cases.`);
237
+ else if (e.status === 404)
199
238
  console.error(`No agent called "${agent}". See your agents with: codiedev traces agents`);
200
239
  else if (e.status === 403)
201
240
  console.error(`Only ${agent}'s maintainers can pull its test cases.`);
@@ -214,7 +253,7 @@ async function runCases(args, config) {
214
253
  : `${res.agent.name} has no saved test cases. Open a run in Codiedev → Traces and click "Save as test case".`);
215
254
  return;
216
255
  }
217
- const dir = path.resolve(process.cwd(), out ?? path.join(".codiedev", "traces", slugify(res.agent.name), ...(res.suite ? [slugify(res.suite)] : []), ...(caseIds && res.cases.length === 1 ? [slugify(res.cases[0].name)] : []), ...(model ? [slugify(model)] : [])));
256
+ const dir = path.resolve(process.cwd(), out ?? path.join(".codiedev", "traces", slugify(res.agent.name), ...(res.suite ? [slugify(res.suite)] : []), ...(res.version ? [`v${res.version}`] : []), ...(caseIds && res.cases.length === 1 ? [slugify(res.cases[0].name)] : []), ...(model ? [slugify(model)] : [])));
218
257
  fs.mkdirSync(dir, { recursive: true });
219
258
  const casesFile = path.join(dir, "cases.jsonl");
220
259
  fs.writeFileSync(casesFile, res.cases.map((c) => JSON.stringify(c)).join("\n") + "\n", "utf8");
@@ -224,7 +263,9 @@ async function runCases(args, config) {
224
263
  fs.writeFileSync(briefFile, casesBrief(res, casesFile, { model, resultsFile }), "utf8");
225
264
  const fix = res.cases.filter((c) => c.kind === "fix").length;
226
265
  const rel = (file) => path.relative(process.cwd(), file) || file;
227
- console.log(`✓ ${plural(res.cases.length, "case")} for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${model ? ` on ${model}` : ""} (${fix} to fix, ${res.cases.length - fix} to keep passing)`);
266
+ console.log(`✓ ${plural(res.cases.length, "case")} for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${res.version ? ` v${res.version}` : ""}${model ? ` on ${model}` : ""} (${fix} to fix, ${res.cases.length - fix} to keep passing)`);
267
+ if (res.missing)
268
+ console.log(` ${plural(res.missing, "case")} in v${res.version} were deleted since and can't be pulled.`);
228
269
  console.log(` ${rel(casesFile)}`);
229
270
  console.log(` ${rel(briefFile)}`);
230
271
  console.log(` ${rel(resultsFile)} (filled in by your coding agent, then: codiedev traces report ${rel(resultsFile)})`);
@@ -286,6 +327,30 @@ async function runTraces(args, configOverride) {
286
327
  return runCases(rest, configOverride ?? (0, shared_1.requireConfig)());
287
328
  if (sub === "report")
288
329
  return runReport(rest, configOverride ?? (0, shared_1.requireConfig)());
330
+ if (sub === "overview")
331
+ return (0, tracesLabel_1.runOverview)(rest, configOverride ?? (0, shared_1.requireConfig)());
332
+ if (sub === "runs")
333
+ return (0, tracesLabel_1.runRuns)(rest, configOverride ?? (0, shared_1.requireConfig)());
334
+ if (sub === "run")
335
+ return (0, tracesLabel_1.runRun)(rest, configOverride ?? (0, shared_1.requireConfig)());
336
+ if (sub === "score-kinds")
337
+ return (0, tracesLabel_1.runScoreKinds)(rest, configOverride ?? (0, shared_1.requireConfig)());
338
+ if (sub === "score")
339
+ return (0, tracesLabel_1.runScore)(rest, configOverride ?? (0, shared_1.requireConfig)());
340
+ if (sub === "scores")
341
+ return (0, tracesLabel_1.runScores)(rest, configOverride ?? (0, shared_1.requireConfig)());
342
+ if (sub === "queue")
343
+ return (0, tracesLabel_1.runQueue)(rest, configOverride ?? (0, shared_1.requireConfig)());
344
+ if (sub === "judge")
345
+ return (0, tracesLabel_1.runJudge)(rest, configOverride ?? (0, shared_1.requireConfig)());
346
+ if (sub === "import")
347
+ return (0, tracesLabel_1.runImport)(rest, configOverride ?? (0, shared_1.requireConfig)());
348
+ if (sub === "add-example")
349
+ return (0, tracesLabel_1.runAddExample)(rest, configOverride ?? (0, shared_1.requireConfig)());
350
+ if (sub === "versions")
351
+ return (0, tracesLabel_1.runVersions)(rest, configOverride ?? (0, shared_1.requireConfig)());
352
+ if (sub === "save-version")
353
+ return (0, tracesLabel_1.runSaveVersion)(rest, configOverride ?? (0, shared_1.requireConfig)());
289
354
  console.log(exports.TRACES_HELP);
290
355
  if (sub && sub !== "help")
291
356
  process.exit(1);
@@ -0,0 +1,24 @@
1
+ import type { CodiedevConfig } from "../utils";
2
+ type Parsed = {
3
+ positional: string[];
4
+ flags: Record<string, string>;
5
+ bools: Set<string>;
6
+ runs: string[];
7
+ };
8
+ export declare function parseArgs(args: string[]): Parsed;
9
+ /** "4" → 4, "true" → true, anything else as text (a category). */
10
+ export declare function scoreValue(raw: string): number | boolean | string;
11
+ export declare function runScore(args: string[], config: CodiedevConfig): Promise<void>;
12
+ export declare function runScores(args: string[], config: CodiedevConfig): Promise<void>;
13
+ export declare function runQueue(args: string[], config: CodiedevConfig): Promise<void>;
14
+ export declare function runJudge(args: string[], config: CodiedevConfig): Promise<void>;
15
+ export declare function runImport(args: string[], config: CodiedevConfig): Promise<void>;
16
+ export declare function runAddExample(args: string[], config: CodiedevConfig): Promise<void>;
17
+ export declare function runVersions(args: string[], config: CodiedevConfig): Promise<void>;
18
+ export declare function runSaveVersion(args: string[], config: CodiedevConfig): Promise<void>;
19
+ export declare function runOverview(args: string[], config: CodiedevConfig): Promise<void>;
20
+ export declare function runRuns(args: string[], config: CodiedevConfig): Promise<void>;
21
+ export declare function runRun(args: string[], config: CodiedevConfig): Promise<void>;
22
+ export declare function runScoreKinds(args: string[], config: CodiedevConfig): Promise<void>;
23
+ export declare function runQueueCreate(args: string[], config: CodiedevConfig): Promise<void>;
24
+ export {};
@@ -0,0 +1,356 @@
1
+ "use strict";
2
+ // `codiedev traces overview | runs | run | score | scores | score-kinds |
3
+ // queue | judge | import | add-example | versions | save-version`: what the
4
+ // Traces portal shows and does (agents, runs, failures, scores, review
5
+ // queues, automatic scoring, datasets), so a coding agent can do it from
6
+ // plain words ("put this week's failed runs in the Week 44 queue"). Each
7
+ // calls a /api/cli/traces/* route that runs the portal's own code, with the
8
+ // same permissions.
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.parseArgs = parseArgs;
44
+ exports.scoreValue = scoreValue;
45
+ exports.runScore = runScore;
46
+ exports.runScores = runScores;
47
+ exports.runQueue = runQueue;
48
+ exports.runJudge = runJudge;
49
+ exports.runImport = runImport;
50
+ exports.runAddExample = runAddExample;
51
+ exports.runVersions = runVersions;
52
+ exports.runSaveVersion = runSaveVersion;
53
+ exports.runOverview = runOverview;
54
+ exports.runRuns = runRuns;
55
+ exports.runRun = runRun;
56
+ exports.runScoreKinds = runScoreKinds;
57
+ exports.runQueueCreate = runQueueCreate;
58
+ const fs = __importStar(require("fs"));
59
+ const path = __importStar(require("path"));
60
+ const shared_1 = require("./shared");
61
+ const BOOL_FLAGS = new Set(["--failed", "--on", "--off", "--json"]);
62
+ function parseArgs(args) {
63
+ const out = { positional: [], flags: {}, bools: new Set(), runs: [] };
64
+ for (let i = 0; i < args.length; i++) {
65
+ const a = args[i];
66
+ if (BOOL_FLAGS.has(a)) {
67
+ out.bools.add(a);
68
+ continue;
69
+ }
70
+ if (a.startsWith("--")) {
71
+ const eq = a.indexOf("=");
72
+ const name = eq > 0 ? a.slice(0, eq) : a;
73
+ const value = eq > 0 ? a.slice(eq + 1) : args[++i];
74
+ if (value === undefined)
75
+ throw new Error(`${name} needs a value`);
76
+ if (name === "--run")
77
+ out.runs.push(value);
78
+ else
79
+ out.flags[name] = value;
80
+ continue;
81
+ }
82
+ out.positional.push(a);
83
+ }
84
+ return out;
85
+ }
86
+ /** "4" → 4, "true" → true, anything else as text (a category). */
87
+ function scoreValue(raw) {
88
+ const t = raw.trim();
89
+ if (/^(true|false)$/i.test(t))
90
+ return t.toLowerCase() === "true";
91
+ if (t !== "" && Number.isFinite(Number(t)))
92
+ return Number(t);
93
+ return t;
94
+ }
95
+ async function call(config, tool, method, route, opts) {
96
+ const start = Date.now();
97
+ try {
98
+ const res = await (0, shared_1.apiRequest)(method, `/api/cli/traces/${route}`, { config, ...opts });
99
+ (0, shared_1.recordAgentEvent)(config, { tool, ok: true, latencyMs: Date.now() - start });
100
+ return res;
101
+ }
102
+ catch (err) {
103
+ const e = err;
104
+ (0, shared_1.recordAgentEvent)(config, { tool, ok: false, latencyMs: Date.now() - start, error: e.message });
105
+ console.error(e.message);
106
+ process.exit(1);
107
+ }
108
+ }
109
+ function usage(text) {
110
+ console.error(`Usage: ${text}`);
111
+ process.exit(1);
112
+ }
113
+ async function runScore(args, config) {
114
+ const p = parseArgs(args);
115
+ const [run, name, value] = p.positional;
116
+ if (!run || !name || value === undefined)
117
+ usage("codiedev traces score <run> <score> <value> [--agent name] [--comment text] [--step n]");
118
+ const res = await call(config, "codiedev_traces_score", "POST", "score", {
119
+ body: { run, name, value: scoreValue(value), agent: p.flags["--agent"], comment: p.flags["--comment"], ...(p.flags["--step"] ? { step: Number(p.flags["--step"]) } : {}) },
120
+ });
121
+ console.log(`✓ Scored ${res.runId}: ${name} = ${value}`);
122
+ }
123
+ async function runScores(args, config) {
124
+ const p = parseArgs(args);
125
+ const agent = p.positional[0];
126
+ if (!agent)
127
+ usage("codiedev traces scores <agent> [--days n] [--json]");
128
+ const res = await call(config, "codiedev_traces_scores", "GET", "scores", { query: { agent, days: p.flags["--days"] ?? "7" } });
129
+ if (p.bools.has("--json")) {
130
+ process.stdout.write(JSON.stringify(res, null, 2) + "\n");
131
+ return;
132
+ }
133
+ if (!res.summary.length)
134
+ console.log(`No scores on ${res.agent} in the last ${p.flags["--days"] ?? 7} days.`);
135
+ for (const s of res.summary) {
136
+ const head = s.dataType === "boolean" && s.average !== null ? `${Math.round(s.average * 100)}% true`
137
+ : s.dataType === "numeric" && s.average !== null ? `avg ${Math.round(s.average * 100) / 100}`
138
+ : s.categories.map((c) => `${c.label} ${c.count}`).join(", ");
139
+ const from = [s.bySource.annotation ? `${s.bySource.annotation} people` : "", s.bySource.api ? `${s.bySource.api} your app` : "", s.bySource.judge ? `${s.bySource.judge} judge` : ""].filter(Boolean).join(", ");
140
+ console.log(`${s.name.padEnd(24)} ${head.padEnd(28)} ${s.count} scores (${from})`);
141
+ }
142
+ if (res.kinds.length)
143
+ console.log(`\nScore kinds: ${res.kinds.map((k) => `${k.name} (${k.dataType === "numeric" ? `${k.min ?? ""}–${k.max ?? ""}` : k.dataType === "boolean" ? "true/false" : k.categories.join("/")})`).join(", ")}`);
144
+ }
145
+ async function runQueue(args, config) {
146
+ const [verb, ...rest] = args;
147
+ if (verb === "list" || verb === undefined) {
148
+ const res = await call(config, "codiedev_traces_queue_list", "GET", "queues", {});
149
+ const live = res.queues.filter((q) => !q.archived);
150
+ if (!live.length)
151
+ console.log("No review queues yet. Make one in Codiedev → Traces → Review.");
152
+ for (const q of live)
153
+ console.log(`${q.name.padEnd(28)} ${String(q.pending).padStart(4)} to review ${String(q.done).padStart(4)} done ${q.scoreNames.join(", ")}`);
154
+ return;
155
+ }
156
+ if (verb === "create")
157
+ return runQueueCreate(rest, config);
158
+ if (verb !== "add")
159
+ usage("codiedev traces queue list | queue create <name> [--scores a,b] | queue add <queue> --agent <name> [--failed] [--days n] [--run id ...]");
160
+ const p = parseArgs(rest);
161
+ const queue = p.positional[0];
162
+ if (!queue || (!p.flags["--agent"] && !p.runs.length))
163
+ usage("codiedev traces queue add <queue> --agent <name> [--failed] [--days n] [--run id ...]");
164
+ const res = await call(config, "codiedev_traces_queue_add", "POST", "queue", {
165
+ body: { queue, agent: p.flags["--agent"], failed: p.bools.has("--failed"), ...(p.flags["--days"] ? { days: Number(p.flags["--days"]) } : {}), ...(p.runs.length ? { runs: p.runs } : {}) },
166
+ });
167
+ console.log(`✓ Added ${res.added} ${res.added === 1 ? "run" : "runs"} to ${res.queue}${res.skipped ? ` (${res.skipped} already there or not yours)` : ""}. Review them in Codiedev → Traces → Review.`);
168
+ }
169
+ function printJudge(agent, judge) {
170
+ if (!judge || judge === "none") {
171
+ console.log(`Automatic scoring isn't set up for ${agent}.`);
172
+ return;
173
+ }
174
+ console.log(`${agent}: automatic scoring ${judge.enabled ? "on" : "off"}`);
175
+ console.log(` scores ${judge.scoreNames.join(", ")} · ${Math.round(judge.sampleRate * 100)}% of runs, up to ${judge.dailyMaxRuns}/day · ${judge.model}`);
176
+ if (judge.judgeKey)
177
+ console.log(` billed to ${judge.judgeKey.name}${judge.judgeKey.monthlyBudgetUsd !== null ? ` ($${judge.judgeKey.monthlyBudgetUsd}/mo)` : ""} · ${judge.judgedToday} judged in 24h${judge.errorsToday ? `, ${judge.errorsToday} failed` : ""}`);
178
+ if (judge.lastError)
179
+ console.log(` last problem: ${judge.lastError}`);
180
+ }
181
+ async function runJudge(args, config) {
182
+ const p = parseArgs(args);
183
+ const agent = p.positional[0];
184
+ const help = "codiedev traces judge <agent> [--on | --off] [--scores a,b] [--instructions text] [--model id] [--sample percent] [--daily n] [--budget usd | --key cdvm_…]";
185
+ if (!agent)
186
+ usage(help);
187
+ const changing = p.bools.has("--on") || p.bools.has("--off") || Object.keys(p.flags).length > 0;
188
+ if (!changing) {
189
+ const res = await call(config, "codiedev_traces_judge", "GET", "judge", { query: { agent } });
190
+ printJudge(res.agent, res.judge);
191
+ return;
192
+ }
193
+ const res = await call(config, "codiedev_traces_judge_set", "POST", "judge", {
194
+ body: {
195
+ agent,
196
+ ...(p.bools.has("--on") ? { enabled: true } : p.bools.has("--off") ? { enabled: false } : {}),
197
+ ...(p.flags["--scores"] ? { scores: p.flags["--scores"].split(",").map((s) => s.trim()).filter(Boolean) } : {}),
198
+ ...(p.flags["--instructions"] ? { instructions: p.flags["--instructions"] } : {}),
199
+ ...(p.flags["--model"] ? { model: p.flags["--model"] } : {}),
200
+ ...(p.flags["--sample"] ? { sampleRate: Number(p.flags["--sample"]) / 100 } : {}),
201
+ ...(p.flags["--daily"] ? { dailyMaxRuns: Number(p.flags["--daily"]) } : {}),
202
+ ...(p.flags["--budget"] ? { newKeyBudgetUsd: Number(p.flags["--budget"]) } : {}),
203
+ ...(p.flags["--key"] ? { pastedKey: p.flags["--key"] } : {}),
204
+ },
205
+ });
206
+ printJudge(res.agent, res.judge);
207
+ }
208
+ async function runImport(args, config) {
209
+ const p = parseArgs(args);
210
+ const [agent, file] = p.positional;
211
+ const dataset = p.flags["--dataset"];
212
+ if (!agent || !file || !dataset)
213
+ usage("codiedev traces import <agent> <file.csv|file.jsonl> --dataset <name>");
214
+ let text;
215
+ try {
216
+ text = fs.readFileSync(path.resolve(process.cwd(), file), "utf8");
217
+ }
218
+ catch (err) {
219
+ console.error(`Couldn't read ${file}: ${err.message}`);
220
+ process.exit(1);
221
+ }
222
+ const res = await call(config, "codiedev_traces_import", "POST", "examples", { body: { agent, dataset, text } });
223
+ console.log(`✓ Imported ${res.added} ${res.added === 1 ? "example" : "examples"} into ${dataset} for ${agent}.`);
224
+ for (const e of res.errors.slice(0, 10))
225
+ console.log(` skipped${e.line ? ` line ${e.line}` : ""}: ${e.error}`);
226
+ }
227
+ async function runAddExample(args, config) {
228
+ const p = parseArgs(args);
229
+ const agent = p.positional[0];
230
+ const dataset = p.flags["--dataset"];
231
+ const input = p.flags["--input"];
232
+ if (!agent || !dataset || !input)
233
+ usage("codiedev traces add-example <agent> --dataset <name> --input <text> [--expected <text>] [--name <text>]");
234
+ await call(config, "codiedev_traces_add_example", "POST", "examples", { body: { agent, dataset, input, expected: p.flags["--expected"], name: p.flags["--name"] } });
235
+ console.log(`✓ Added an example to ${dataset} for ${agent}.`);
236
+ }
237
+ async function runVersions(args, config) {
238
+ const dataset = parseArgs(args).positional[0];
239
+ if (!dataset)
240
+ usage("codiedev traces versions <dataset>");
241
+ const res = await call(config, "codiedev_traces_versions", "GET", "versions", { query: { dataset } });
242
+ if (!res.versions.length)
243
+ console.log(`${dataset} has no saved versions. Save one with: codiedev traces save-version "${dataset}"`);
244
+ for (const v of res.versions)
245
+ console.log(`v${v.version} ${String(v.cases).padStart(4)} cases ${new Date(v.createdAt).toISOString().slice(0, 10)} +${v.added} −${v.removed} ${v.changed} edited${v.note ? ` ${v.note}` : ""}`);
246
+ if (res.unsaved)
247
+ console.log(`Unsaved since v${res.versions[0].version}: +${res.unsaved.added} −${res.unsaved.removed} ${res.unsaved.changed} edited`);
248
+ }
249
+ async function runSaveVersion(args, config) {
250
+ const p = parseArgs(args);
251
+ const dataset = p.positional[0];
252
+ if (!dataset)
253
+ usage("codiedev traces save-version <dataset> [--note text]");
254
+ const res = await call(config, "codiedev_traces_save_version", "POST", "versions", { body: { dataset, note: p.flags["--note"] } });
255
+ console.log(`✓ Saved ${dataset} as v${res.version} (${res.cases} cases). Run it later with --dataset "${dataset}" --version ${res.version}.`);
256
+ }
257
+ const usd = (n) => (n === 0 ? "$0" : n < 0.01 ? "<$0.01" : `$${n.toFixed(2)}`);
258
+ const json = (p, value) => { if (p.bools.has("--json")) {
259
+ process.stdout.write(JSON.stringify(value, null, 2) + "\n");
260
+ return true;
261
+ } return false; };
262
+ async function runOverview(args, config) {
263
+ const p = parseArgs(args);
264
+ const res = await call(config, "codiedev_traces_overview", "GET", "overview", { query: { days: p.flags["--days"] ?? "7" } });
265
+ if (json(p, res))
266
+ return;
267
+ if (!res.agents.length) {
268
+ console.log("No agents you can see yet. Connect one in Codiedev → Traces.");
269
+ return;
270
+ }
271
+ console.log(`Agents, last ${res.range}:`);
272
+ for (const a of res.agents)
273
+ console.log(` ${a.name.padEnd(28)} ${String(a.runs).padStart(6)} runs ${String(a.failedRuns).padStart(5)} failed ${usd(a.spend7dUsd).padStart(9)} 7d spend ${a.keepsPromptsDays ? `prompts ${a.keepsPromptsDays}d` : "metadata only"}${a.spike ? " spend spike" : ""}${a.budgetBlock ? " budget block" : ""}`);
274
+ if (res.issues.length) {
275
+ console.log("\nWhat's going wrong:");
276
+ for (const i of res.issues)
277
+ console.log(` ${i.label.padEnd(18)} ${String(i.runs).padStart(5)} runs ${usd(i.wastedUsd).padStart(9)} wasted ${i.agents.join(", ")}${i.exampleRun ? ` e.g. ${i.exampleRun}` : ""}`);
278
+ }
279
+ }
280
+ async function runRuns(args, config) {
281
+ const p = parseArgs(args);
282
+ const agent = p.positional[0];
283
+ if (!agent)
284
+ usage("codiedev traces runs <agent> [--failed] [--flag errors|truncated|tool_loop|context_overflow|budget_block] [--model id] [--verdict pass|fail] [--days n] [--limit n] [--json]");
285
+ const res = await call(config, "codiedev_traces_runs", "GET", "runs", {
286
+ query: { agent, days: p.flags["--days"] ?? "7", ...(p.bools.has("--failed") ? { failed: "1" } : {}), ...(p.flags["--flag"] ? { flag: p.flags["--flag"] } : {}), ...(p.flags["--model"] ? { model: p.flags["--model"] } : {}), ...(p.flags["--verdict"] ? { verdict: p.flags["--verdict"] } : {}), limit: p.flags["--limit"] ?? "50" },
287
+ });
288
+ if (json(p, res))
289
+ return;
290
+ console.log(`${res.total} ${res.total === 1 ? "run" : "runs"} for ${res.agent}, last ${res.range}${res.total > res.runs.length ? ` (newest ${res.runs.length})` : ""}:`);
291
+ for (const r of res.runs)
292
+ console.log(` ${r.startedAt.slice(0, 16).replace("T", " ")} ${r.status === "failed" ? "FAIL" : " ok "} ${r.runId.padEnd(24)} ${String(r.steps).padStart(3)} calls ${usd(r.costUsd).padStart(8)} ${r.model ?? ""}${r.flags.length ? ` [${r.flags.join(", ")}]` : ""}${r.verdict ? ` marked ${r.verdict}` : ""}`);
293
+ if (res.runs.length)
294
+ console.log(`\nOpen one: codiedev traces run ${res.runs[0].runId} --agent "${res.agent}"`);
295
+ }
296
+ async function runRun(args, config) {
297
+ const p = parseArgs(args);
298
+ const run = p.positional[0];
299
+ if (!run)
300
+ usage("codiedev traces run <run> [--agent name] [--json]");
301
+ const res = await call(config, "codiedev_traces_run", "GET", "run", { query: { run, ...(p.flags["--agent"] ? { agent: p.flags["--agent"] } : {}) } });
302
+ if (json(p, res))
303
+ return;
304
+ const r = res.run;
305
+ console.log(`${r.title} · ${r.id}`);
306
+ console.log(`${new Date(r.startedAt).toISOString().slice(0, 16).replace("T", " ")} · ${r.status === "failed" ? "FAILED" : "ok"} · ${Math.round(r.durationMs / 100) / 10}s · ${usd(r.costUsd)}${r.flags.length ? ` · ${r.flags.join(", ")}` : ""}`);
307
+ if (r.diagnosis)
308
+ console.log(`\nWhat went wrong: ${r.diagnosis.title}\n ${r.diagnosis.why}\n\nFix (for your coding agent):\n${r.diagnosis.fix.split("\n").map((l) => ` ${l}`).join("\n")}`);
309
+ if (r.annotation)
310
+ console.log(`\nMarked ${r.annotation.verdict} by ${r.annotation.by}${r.annotation.reason ? `: ${r.annotation.reason}` : ""}`);
311
+ if (res.scores.length)
312
+ console.log(`\nScores: ${res.scores.map((s) => `${s.name} ${s.text}${s.step ? ` (step ${s.step})` : ""} by ${s.by ?? s.source}${s.comment ? ` "${s.comment}"` : ""}`).join("; ")}`);
313
+ console.log("\nSteps:");
314
+ for (const s of r.spans.filter((x) => x.kind !== "run")) {
315
+ console.log(` ${s.kind === "tool" ? "tool" : "call"} ${s.name} ${Math.round(s.durationMs)}ms${s.costUsd !== undefined ? ` ${usd(s.costUsd)}` : ""}${s.finishReason ? ` finish: ${s.finishReason}` : ""}${s.statusCode && s.statusCode >= 400 ? ` HTTP ${s.statusCode}` : ""}${s.flag ? ` [${s.flag}]` : ""}${s.note ? ` ${s.note}` : ""}`);
316
+ if (s.input)
317
+ console.log(` prompt: ${s.input.replace(/\s+/g, " ").slice(0, 300)}`);
318
+ if (s.output)
319
+ console.log(` reply: ${s.output.replace(/\s+/g, " ").slice(0, 300)}`);
320
+ }
321
+ if (!r.bodiesVisible)
322
+ console.log("\n(Prompts and replies are shown only to the agent's maintainers.)");
323
+ }
324
+ async function runScoreKinds(args, config) {
325
+ const [verb, ...rest] = args;
326
+ if (verb === "add") {
327
+ const p = parseArgs(rest);
328
+ const name = p.positional[0];
329
+ const type = p.flags["--type"];
330
+ if (!name || !type)
331
+ usage("codiedev traces score-kinds add <name> --type number|categories|boolean [--min n --max n] [--categories a,b,c] [--description text]");
332
+ const dataType = type === "number" || type === "numeric" ? "numeric" : type === "categories" || type === "categorical" ? "categorical" : type === "boolean" || type === "bool" ? "boolean" : type;
333
+ await call(config, "codiedev_traces_score_kind_add", "POST", "score-kinds", {
334
+ body: { name, type: dataType, ...(p.flags["--min"] ? { min: Number(p.flags["--min"]) } : {}), ...(p.flags["--max"] ? { max: Number(p.flags["--max"]) } : {}), ...(p.flags["--categories"] ? { categories: p.flags["--categories"].split(",").map((c) => c.trim()) } : {}), ...(p.flags["--description"] ? { description: p.flags["--description"] } : {}) },
335
+ });
336
+ console.log(`✓ Added score kind ${name}.`);
337
+ return;
338
+ }
339
+ const p = parseArgs(verb === undefined || verb === "list" ? rest : args);
340
+ const res = await call(config, "codiedev_traces_score_kinds", "GET", "score-kinds", {});
341
+ if (json(p, res))
342
+ return;
343
+ const live = res.kinds.filter((k) => !k.archived);
344
+ if (!live.length)
345
+ console.log("No score kinds yet. Add one: codiedev traces score-kinds add helpfulness --type number --min 1 --max 5");
346
+ for (const k of live)
347
+ console.log(` ${k.name.padEnd(24)} ${k.dataType === "numeric" ? `number ${k.min ?? ""}–${k.max ?? ""}` : k.dataType === "boolean" ? "true / false" : k.categories.join(" / ")}${k.description ? ` ${k.description}` : ""}`);
348
+ }
349
+ async function runQueueCreate(args, config) {
350
+ const p = parseArgs(args);
351
+ const name = p.positional[0];
352
+ if (!name)
353
+ usage("codiedev traces queue create <name> [--scores a,b] [--description text]");
354
+ await call(config, "codiedev_traces_queue_create", "POST", "queues", { body: { name, scores: (p.flags["--scores"] ?? "").split(",").map((s) => s.trim()).filter(Boolean), ...(p.flags["--description"] ? { description: p.flags["--description"] } : {}) } });
355
+ console.log(`✓ Created review queue ${name}. Add runs: codiedev traces queue add "${name}" --agent <name> --failed`);
356
+ }
package/dist/utils.js CHANGED
@@ -1681,7 +1681,13 @@ thought, use the \`codiedev\` CLI via Bash:**
1681
1681
  | "find something about X" / "has anyone solved Y?" | \`codiedev search "<query>" [--limit N]\` |
1682
1682
  | "why did we…?" / "what does the team know about X?" | \`codiedev ask "<question>"\` (synthesized answer + citations) |
1683
1683
  | "show my library" / "what artifacts exist?" | \`codiedev library [--scope mine\\|shared\\|all] [--kind doc\\|skill]\` |
1684
- | "run my agent's test cases" / "check the dataset on model Y" | \`codiedev traces cases <agent> [--dataset <name>] [--model <id>]\` (writes cases + RUN_CASES.md + results.json; follow it, then \`codiedev traces report <results.json>\`) |
1684
+ | "run my agent's test cases" / "check the dataset on model Y" / "run v2 of the dataset" | \`codiedev traces cases <agent> [--dataset <name> [--version <n>]] [--model <id>]\` (writes cases + RUN_CASES.md + results.json; follow it, then \`codiedev traces report <results.json>\`) |
1685
+ | "how are my agents doing?" / "what's failing this week?" / "show the booking agent's failed runs" / "why did run X fail?" | \`codiedev traces overview\`; \`codiedev traces runs <agent> [--failed] [--flag <issue>] [--days n]\`; \`codiedev traces run <run> --agent <name>\` (add \`--json\` to read the data yourself) |
1686
+ | "add a helpfulness 1–5 score" / "make a review queue for tone" | \`codiedev traces score-kinds add <name> --type number --min 1 --max 5\`; \`codiedev traces queue create <name> --scores <a,b>\` |
1687
+ | "score that run 4 for helpfulness" / "how is my agent scoring?" | \`codiedev traces score <run> <score> <value> [--agent <name>] [--comment <why>]\`; \`codiedev traces scores <agent>\` |
1688
+ | "put this week's failed runs in the review queue" | \`codiedev traces queue list\`; \`codiedev traces queue add <queue> --agent <name> [--failed] [--days n]\` |
1689
+ | "have a judge score 10% of my agent's runs" / "turn off automatic scoring" | \`codiedev traces judge <agent> --on --scores <a,b> --sample 10 [--budget 25]\`; \`--off\`; no flags shows it |
1690
+ | "import these test cases from a CSV" / "add an example to the dataset" / "save the dataset as a version" | \`codiedev traces import <agent> <file> --dataset <name>\`; \`codiedev traces add-example <agent> --dataset <name> --input "…" [--expected "…"]\`; \`codiedev traces save-version <dataset>\`; \`codiedev traces versions <dataset>\` |
1685
1691
  | "what was the last eval on X?" / "file these eval results" | \`codiedev evals last <subject>\` before starting a related eval; \`codiedev evals report <run.json>\` after each variant runs (scores only — \`codiedev evals help\` for the shape) |
1686
1692
 
1687
1693
  **Just push the file — we'll figure out where it goes.** Every artifact
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codiedev",
3
- "version": "0.9.77",
3
+ "version": "0.9.79",
4
4
  "description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration — and run the governed Codie harness",
5
5
  "bin": {
6
6
  "codiedev": "dist/cli.js",