codiedev 0.9.72 → 0.9.76
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 +20 -0
- package/dist/codieLauncher.d.ts +7 -1
- package/dist/codieLauncher.js +24 -4
- package/dist/commands/evals.d.ts +3 -0
- package/dist/commands/evals.js +193 -0
- package/dist/commands/traces.d.ts +68 -0
- package/dist/commands/traces.js +292 -0
- package/dist/piRuntime.d.ts +1 -0
- package/dist/piRuntime.js +33 -20
- package/dist/utils.js +2 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -30,6 +30,8 @@ const share_1 = require("./commands/share");
|
|
|
30
30
|
const send_1 = require("./commands/send");
|
|
31
31
|
const capture_1 = require("./commands/capture");
|
|
32
32
|
const pack_1 = require("./commands/pack");
|
|
33
|
+
const traces_1 = require("./commands/traces");
|
|
34
|
+
const evals_1 = require("./commands/evals");
|
|
33
35
|
// --- starter packs phase 2 ---
|
|
34
36
|
const hookGate_1 = require("./packs/hookGate");
|
|
35
37
|
const sync_1 = require("./packs/sync");
|
|
@@ -91,6 +93,16 @@ Starter packs:
|
|
|
91
93
|
detected tool
|
|
92
94
|
codiedev pack status Installed packs, up to date or not
|
|
93
95
|
|
|
96
|
+
Traces (agents on the gateway):
|
|
97
|
+
codiedev traces agents Agents you maintain, with saved test cases
|
|
98
|
+
codiedev traces cases <agent> [--dataset name] [--model id]
|
|
99
|
+
Pull an agent's test cases (or one dataset)
|
|
100
|
+
into this repo for your coding agent to
|
|
101
|
+
run, optionally on another model
|
|
102
|
+
codiedev traces report <results.json> Send the results back to Traces
|
|
103
|
+
codiedev evals report <run.json> File one variant's eval scores under an experiment
|
|
104
|
+
codiedev evals last <subject> Latest experiment on a subject + its champion
|
|
105
|
+
|
|
94
106
|
Discovery:
|
|
95
107
|
codiedev search "<query>" Semantic search across the team's
|
|
96
108
|
captured sessions and artifacts
|
|
@@ -152,6 +164,8 @@ status sync never weakens it.
|
|
|
152
164
|
`.trim();
|
|
153
165
|
const COMMAND_HELP = {
|
|
154
166
|
pack: pack_1.PACK_HELP,
|
|
167
|
+
traces: traces_1.TRACES_HELP,
|
|
168
|
+
evals: evals_1.EVALS_HELP,
|
|
155
169
|
connect: `
|
|
156
170
|
codiedev connect — link your agent CLI to Codiedev
|
|
157
171
|
|
|
@@ -515,6 +529,12 @@ async function main() {
|
|
|
515
529
|
case "ls":
|
|
516
530
|
await (0, library_1.runLibrary)(rest);
|
|
517
531
|
return;
|
|
532
|
+
case "traces":
|
|
533
|
+
await (0, traces_1.runTraces)(rest);
|
|
534
|
+
return;
|
|
535
|
+
case "evals":
|
|
536
|
+
await (0, evals_1.runEvals)(rest);
|
|
537
|
+
return;
|
|
518
538
|
case "share":
|
|
519
539
|
await (0, share_1.runShare)(rest);
|
|
520
540
|
return;
|
package/dist/codieLauncher.d.ts
CHANGED
|
@@ -69,10 +69,16 @@ export declare function resolveCapturePlugin({ homeDir, existsSync, }: {
|
|
|
69
69
|
homeDir: string;
|
|
70
70
|
existsSync: (candidate: string) => boolean;
|
|
71
71
|
}): string;
|
|
72
|
-
|
|
72
|
+
/**
|
|
73
|
+
* The account's approved models. Every `codie` session starts here, so one
|
|
74
|
+
* dropped connection or slow gateway reply would end the session before it
|
|
75
|
+
* began; anything but a definite rejection is retried once.
|
|
76
|
+
*/
|
|
77
|
+
export declare function fetchApprovedCatalog({ baseUrl, key, fetchImpl, retryDelayMs, }: {
|
|
73
78
|
baseUrl?: string;
|
|
74
79
|
key: string;
|
|
75
80
|
fetchImpl?: typeof fetch;
|
|
81
|
+
retryDelayMs?: number;
|
|
76
82
|
}): Promise<CatalogModel[]>;
|
|
77
83
|
export declare function parseCodieArgs(argv: string[]): {
|
|
78
84
|
args: string[];
|
package/dist/codieLauncher.js
CHANGED
|
@@ -349,9 +349,29 @@ function resolveCapturePlugin({ homeDir, existsSync, }) {
|
|
|
349
349
|
}
|
|
350
350
|
return (0, url_1.pathToFileURL)(capturePath).href;
|
|
351
351
|
}
|
|
352
|
-
|
|
352
|
+
// A definite answer from the gateway (harness off, token rejected): retrying
|
|
353
|
+
// can't change it.
|
|
354
|
+
class CatalogRejected extends Error {
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* The account's approved models. Every `codie` session starts here, so one
|
|
358
|
+
* dropped connection or slow gateway reply would end the session before it
|
|
359
|
+
* began; anything but a definite rejection is retried once.
|
|
360
|
+
*/
|
|
361
|
+
async function fetchApprovedCatalog({ baseUrl, key, fetchImpl = fetch, retryDelayMs = 1_000, }) {
|
|
362
|
+
try {
|
|
363
|
+
return await fetchCatalogOnce(baseUrl, key, fetchImpl);
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
if (error instanceof CatalogRejected)
|
|
367
|
+
throw error;
|
|
368
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
369
|
+
return fetchCatalogOnce(baseUrl, key, fetchImpl);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function fetchCatalogOnce(baseUrl, key, fetchImpl) {
|
|
353
373
|
const controller = new AbortController();
|
|
354
|
-
const timeout = setTimeout(() => controller.abort(),
|
|
374
|
+
const timeout = setTimeout(() => controller.abort(), 10_000);
|
|
355
375
|
try {
|
|
356
376
|
const response = await fetchImpl(`${cleanBaseUrl(baseUrl)}/models`, {
|
|
357
377
|
headers: { Authorization: `Bearer ${key}` },
|
|
@@ -361,11 +381,11 @@ async function fetchApprovedCatalog({ baseUrl, key, fetchImpl = fetch, }) {
|
|
|
361
381
|
// whose gateway is still switched off. Those developers must get an
|
|
362
382
|
// answer, not an HTTP status.
|
|
363
383
|
if (response.status === 403) {
|
|
364
|
-
throw new
|
|
384
|
+
throw new CatalogRejected("The Codie harness is not enabled for your company yet. Ask your Codiedev admin to turn it on — " +
|
|
365
385
|
"your session capture keeps working in the meantime.");
|
|
366
386
|
}
|
|
367
387
|
if (response.status === 401) {
|
|
368
|
-
throw new
|
|
388
|
+
throw new CatalogRejected("Your Codiedev token was rejected. Run `codiedev connect` to relink this machine.");
|
|
369
389
|
}
|
|
370
390
|
if (!response.ok)
|
|
371
391
|
throw new Error(`model catalog returned ${response.status}`);
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `codiedev evals report <run.json> [--harness name]`
|
|
3
|
+
// `codiedev evals last <subject> [--json]`
|
|
4
|
+
//
|
|
5
|
+
// Evals are experiments (a question about a subject: an agent, or a workload
|
|
6
|
+
// like session-summaries) and the runs that answer them, one per variant. The
|
|
7
|
+
// eval runs wherever it runs (a script, a coding agent); `report` files the
|
|
8
|
+
// scores under the experiment in Codiedev → Traces → Evals, shared with the
|
|
9
|
+
// team. `last` is where to start the next one: the latest experiment on the
|
|
10
|
+
// subject, its runs, and the champion the subject runs today.
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.EVALS_HELP = void 0;
|
|
46
|
+
exports.runEvals = runEvals;
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const shared_1 = require("./shared");
|
|
50
|
+
exports.EVALS_HELP = `
|
|
51
|
+
codiedev evals — experiments and their runs, shared with the team
|
|
52
|
+
|
|
53
|
+
codiedev evals report <run.json> [--harness name]
|
|
54
|
+
File one variant's scores under an
|
|
55
|
+
experiment (created on first use)
|
|
56
|
+
codiedev evals last <subject> [--json] The latest experiment on a subject,
|
|
57
|
+
its runs, and the current champion
|
|
58
|
+
|
|
59
|
+
run.json:
|
|
60
|
+
{
|
|
61
|
+
"subject": "session-summaries", // agent name or workload
|
|
62
|
+
"question": "Is Terra medium as good as GLM high?",
|
|
63
|
+
"experimentId": "…", // optional, instead of subject + question
|
|
64
|
+
"dataset": "30 Signal & Code sessions", // optional
|
|
65
|
+
"run": {
|
|
66
|
+
"label": "terra-med-v2", "model": "openai/gpt-6.1-terra",
|
|
67
|
+
"provider": "openai", "reasoningEffort": "medium", "prompt": "summary_extract_v2",
|
|
68
|
+
"gitSha": "…", "isControl": false, "judgeModel": "anthropic/claude-sonnet-5.5",
|
|
69
|
+
"casesTotal": 30, "casesScored": 30, "errors": 0,
|
|
70
|
+
"score": 3.85, "metrics": { "fidelity": 3.6 },
|
|
71
|
+
"costPerCaseUsd": 0.026, "latencyMsAvg": 21000,
|
|
72
|
+
"wins": 20, "losses": 8, "ties": 2, "note": "…",
|
|
73
|
+
"cases": [{ "key": "case-1", "score": 4, "note": "…" }]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Scores and notes only: never put prompts, transcripts or outputs in it.
|
|
78
|
+
`.trim();
|
|
79
|
+
function flag(args, i, name) {
|
|
80
|
+
const a = args[i];
|
|
81
|
+
if (a === name && i + 1 < args.length)
|
|
82
|
+
return { value: args[i + 1], skip: 1 };
|
|
83
|
+
if (a.startsWith(`${name}=`))
|
|
84
|
+
return { value: a.slice(name.length + 1), skip: 0 };
|
|
85
|
+
return { skip: -1 };
|
|
86
|
+
}
|
|
87
|
+
function variant(run) {
|
|
88
|
+
return [run.model, run.reasoningEffort, run.prompt].filter(Boolean).join(" · ");
|
|
89
|
+
}
|
|
90
|
+
function fmtScore(n) {
|
|
91
|
+
return n === null ? "–" : n.toFixed(2);
|
|
92
|
+
}
|
|
93
|
+
function fmtCost(n) {
|
|
94
|
+
return n === null ? "–" : `$${n.toFixed(4)}`;
|
|
95
|
+
}
|
|
96
|
+
async function runReport(args, config) {
|
|
97
|
+
let file;
|
|
98
|
+
let harness;
|
|
99
|
+
for (let i = 0; i < args.length; i++) {
|
|
100
|
+
const f = flag(args, i, "--harness");
|
|
101
|
+
if (f.skip >= 0) {
|
|
102
|
+
harness = f.value;
|
|
103
|
+
i += f.skip;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (!args[i].startsWith("-") && !file)
|
|
107
|
+
file = args[i];
|
|
108
|
+
}
|
|
109
|
+
if (!file) {
|
|
110
|
+
console.error("Usage: codiedev evals report <run.json> [--harness name]");
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
let body;
|
|
114
|
+
try {
|
|
115
|
+
body = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), file), "utf8"));
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
console.error(`Couldn't read ${file}: ${err.message}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
if (!body.subject || !body.question || !body.run?.label || !body.run?.model || typeof body.run?.casesTotal !== "number") {
|
|
122
|
+
console.error(`${file} needs subject, question, and run.label, run.model, run.casesTotal. See: codiedev evals help`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
const start = Date.now();
|
|
126
|
+
try {
|
|
127
|
+
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/evals/runs", {
|
|
128
|
+
config,
|
|
129
|
+
body: { ...body, run: { ...body.run, harness: harness ?? body.run.harness ?? undefined } },
|
|
130
|
+
});
|
|
131
|
+
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_evals_report", ok: true, latencyMs: Date.now() - start });
|
|
132
|
+
console.log(`✓ Reported ${body.run.label} on ${body.subject}${res.created ? " (new experiment)" : ""}: "${body.question}"`);
|
|
133
|
+
console.log(` experiment ${res.experimentId}. See it in Codiedev → Traces → Evals.`);
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
const e = err;
|
|
137
|
+
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_evals_report", ok: false, latencyMs: Date.now() - start, error: e.message });
|
|
138
|
+
console.error(`Couldn't report: ${e.message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function runLast(args, config) {
|
|
143
|
+
const json = args.includes("--json");
|
|
144
|
+
const subject = args.find((a) => !a.startsWith("-"));
|
|
145
|
+
if (!subject) {
|
|
146
|
+
console.error("Usage: codiedev evals last <subject> [--json]");
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
let res;
|
|
150
|
+
try {
|
|
151
|
+
res = await (0, shared_1.apiRequest)("GET", `/api/cli/evals/last?subject=${encodeURIComponent(subject)}`, { config });
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
const e = err;
|
|
155
|
+
console.error(e.status === 404 ? `No evals on ${subject} yet.` : `Couldn't load evals: ${e.message}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
if (json) {
|
|
159
|
+
console.log(JSON.stringify(res, null, 2));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const { experiment, runs, champion } = res;
|
|
163
|
+
if (champion) {
|
|
164
|
+
console.log(`Champion: ${champion.run.label} (${variant(champion.run)}), score ${fmtScore(champion.run.score)}, ${fmtCost(champion.run.costPerCaseUsd)}/case`);
|
|
165
|
+
if (champion.decisionNote)
|
|
166
|
+
console.log(` Why: ${champion.decisionNote}`);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
console.log("Champion: none picked yet");
|
|
170
|
+
}
|
|
171
|
+
console.log("");
|
|
172
|
+
console.log(`Latest experiment (${res.experiments} on ${experiment.subject}): ${experiment.question}`);
|
|
173
|
+
if (experiment.dataset)
|
|
174
|
+
console.log(` Dataset: ${experiment.dataset}`);
|
|
175
|
+
console.log(` id ${experiment.id}, by ${experiment.createdBy}, updated ${new Date(experiment.updatedAt).toISOString().slice(0, 16).replace("T", " ")}`);
|
|
176
|
+
console.log("");
|
|
177
|
+
for (const run of [...runs].sort((a, b) => a.ranAt - b.ranAt)) {
|
|
178
|
+
const h2h = run.wins !== null ? ` · ${run.wins}W/${run.losses ?? 0}L/${run.ties ?? 0}T` : "";
|
|
179
|
+
console.log(` ${run.isControl ? "[control] " : ""}${run.label.padEnd(24)} ${fmtScore(run.score).padStart(5)} ${fmtCost(run.costPerCaseUsd).padStart(8)}/case${h2h} ${variant(run)}`);
|
|
180
|
+
}
|
|
181
|
+
console.log("");
|
|
182
|
+
console.log(`Next run on this experiment: put "experimentId": "${experiment.id}" in run.json, then codiedev evals report run.json`);
|
|
183
|
+
}
|
|
184
|
+
async function runEvals(args, configOverride) {
|
|
185
|
+
const [sub, ...rest] = args;
|
|
186
|
+
if (sub === "report")
|
|
187
|
+
return runReport(rest, configOverride ?? (0, shared_1.requireConfig)());
|
|
188
|
+
if (sub === "last")
|
|
189
|
+
return runLast(rest, configOverride ?? (0, shared_1.requireConfig)());
|
|
190
|
+
console.log(exports.EVALS_HELP);
|
|
191
|
+
if (sub && sub !== "help")
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { CodiedevConfig } from "../utils";
|
|
2
|
+
export declare const TRACES_HELP: string;
|
|
3
|
+
interface CaseStep {
|
|
4
|
+
model: string;
|
|
5
|
+
finishReason?: string;
|
|
6
|
+
statusCode: number;
|
|
7
|
+
request?: string;
|
|
8
|
+
reply?: string;
|
|
9
|
+
toolCalls?: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
arguments: string;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
interface CaseRow {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
suite: string | null;
|
|
18
|
+
runId: string;
|
|
19
|
+
kind: "fix" | "keep";
|
|
20
|
+
flags: string[];
|
|
21
|
+
note: string | null;
|
|
22
|
+
expected: string | null;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
steps: CaseStep[];
|
|
25
|
+
}
|
|
26
|
+
interface CasesResponse {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
/** The suite pulled, when --suite was given. */
|
|
29
|
+
suite: string | null;
|
|
30
|
+
/** How the suite is judged, when it has settings. */
|
|
31
|
+
judge: {
|
|
32
|
+
model: string | null;
|
|
33
|
+
rubric: string | null;
|
|
34
|
+
} | null;
|
|
35
|
+
agent: {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
description: string | null;
|
|
39
|
+
promptDays: number | null;
|
|
40
|
+
};
|
|
41
|
+
cases: CaseRow[];
|
|
42
|
+
}
|
|
43
|
+
/** What `report` sends, and what `cases` writes for the coding agent to fill in. */
|
|
44
|
+
export interface ResultsFile {
|
|
45
|
+
agent: string;
|
|
46
|
+
suite: string | null;
|
|
47
|
+
model: string;
|
|
48
|
+
judgeModel: string | null;
|
|
49
|
+
harness: string | null;
|
|
50
|
+
results: Array<{
|
|
51
|
+
caseId: string;
|
|
52
|
+
name: string;
|
|
53
|
+
passes: number | null;
|
|
54
|
+
tries: number;
|
|
55
|
+
note: string | null;
|
|
56
|
+
output: string | null;
|
|
57
|
+
}>;
|
|
58
|
+
}
|
|
59
|
+
export declare function slugify(name: string): string;
|
|
60
|
+
/** The brief a coding agent follows to run the cases and report back. */
|
|
61
|
+
export declare function casesBrief(res: CasesResponse, casesFile: string, opts?: {
|
|
62
|
+
model?: string;
|
|
63
|
+
resultsFile?: string;
|
|
64
|
+
}): string;
|
|
65
|
+
/** The results.json a run starts from: every case, passes to be filled in. */
|
|
66
|
+
export declare function resultsTemplate(res: CasesResponse, model: string | undefined): ResultsFile;
|
|
67
|
+
export declare function runTraces(args: string[], configOverride?: CodiedevConfig): Promise<void>;
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `codiedev traces agents`
|
|
3
|
+
// `codiedev traces cases <agent> [--dataset name] [--model id] [--out dir] [--json]`
|
|
4
|
+
// `codiedev traces report <results.json> [--harness name]`
|
|
5
|
+
//
|
|
6
|
+
// Test cases are runs an engineer saved from Codiedev → Traces. They run in
|
|
7
|
+
// the engineer's coding agent, not in the portal: `cases` writes them into the
|
|
8
|
+
// repo with a brief the agent follows to replay each one against the agent's
|
|
9
|
+
// own code, optionally on another model (a regression check before a model
|
|
10
|
+
// swap), and a results.json for `report` to send back so Traces shows pass
|
|
11
|
+
// rates by model.
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.TRACES_HELP = void 0;
|
|
47
|
+
exports.slugify = slugify;
|
|
48
|
+
exports.casesBrief = casesBrief;
|
|
49
|
+
exports.resultsTemplate = resultsTemplate;
|
|
50
|
+
exports.runTraces = runTraces;
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const shared_1 = require("./shared");
|
|
54
|
+
exports.TRACES_HELP = `
|
|
55
|
+
codiedev traces — test cases saved from Codiedev → Traces
|
|
56
|
+
|
|
57
|
+
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]
|
|
59
|
+
Write an agent's cases (one dataset, or just
|
|
60
|
+
the cases named) into this repo, default
|
|
61
|
+
.codiedev/traces/<agent>[/<dataset>]/, then
|
|
62
|
+
ask your coding agent to run them.
|
|
63
|
+
--model runs them on another model, e.g.
|
|
64
|
+
the replacement for a retiring one
|
|
65
|
+
codiedev traces report <results.json> [--harness name]
|
|
66
|
+
Send the filled-in results back, so
|
|
67
|
+
Traces shows the pass rate for that model
|
|
68
|
+
|
|
69
|
+
<agent> is the agent's name (its API key name) or id. Only an agent's
|
|
70
|
+
maintainers can pull its cases or report on them.
|
|
71
|
+
`.trim();
|
|
72
|
+
const FLAG_LABEL = {
|
|
73
|
+
budget_block: "refused for budget",
|
|
74
|
+
errors: "model call failed",
|
|
75
|
+
tool_loop: "repeated the same tool call",
|
|
76
|
+
truncated: "reply cut off at max_tokens",
|
|
77
|
+
context_overflow: "prompt over 70% of the context window",
|
|
78
|
+
};
|
|
79
|
+
const TRIES = 3;
|
|
80
|
+
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";
|
|
82
|
+
function slugify(name) {
|
|
83
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
84
|
+
}
|
|
85
|
+
/** The brief a coding agent follows to run the cases and report back. */
|
|
86
|
+
function casesBrief(res, casesFile, opts = {}) {
|
|
87
|
+
const fix = res.cases.filter((c) => c.kind === "fix");
|
|
88
|
+
const keep = res.cases.filter((c) => c.kind === "keep");
|
|
89
|
+
const withBodies = res.cases.filter((c) => c.steps.some((s) => s.request)).length;
|
|
90
|
+
const target = opts.model;
|
|
91
|
+
const results = opts.resultsFile ? path.basename(opts.resultsFile) : "results.json";
|
|
92
|
+
const lines = [
|
|
93
|
+
`# Test cases for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${target ? ` on ${target}` : ""}`,
|
|
94
|
+
"",
|
|
95
|
+
res.agent.description ? `${res.agent.description}\n` : "",
|
|
96
|
+
`Saved from Codiedev → Traces. ${plural(res.cases.length, "case")} in \`${path.basename(casesFile)}\` (one JSON object per line):`,
|
|
97
|
+
`- **${fix.length} to fix**: production runs that failed. The fix should make them pass.`,
|
|
98
|
+
`- **${keep.length} to keep passing**: good runs. They should still pass after any change.`,
|
|
99
|
+
"",
|
|
100
|
+
];
|
|
101
|
+
if (target) {
|
|
102
|
+
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
|
+
}
|
|
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.");
|
|
105
|
+
if (res.judge?.model || res.judge?.rubric) {
|
|
106
|
+
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
|
+
}
|
|
108
|
+
lines.push(`4. Run each case ${TRIES} times: agents are stochastic, so report a pass rate per case, not a single result.`, `5. Fill in \`${results}\`: for each case set \`passes\` (out of \`tries\`), \`output\` (the agent's final reply on one try, so it can be compared side by side with other models) and a one-line \`note\` on any failure. Set \`harness\` to the coding agent you are (claude-code, codex, cursor, copilot, …).`, `6. Send it back: \`codiedev traces report ${opts.resultsFile ? path.relative(process.cwd(), opts.resultsFile) || results : results}\`. Then show a table: case, kind, passes out of ${TRIES}, and the failure note.`, "", "Send real model calls through the gateway with the agent's usual key and `X-Codiedev-Run-Id: case-<id>-<try>`, so the replays show up in Traces too.", "");
|
|
109
|
+
if (withBodies < res.cases.length) {
|
|
110
|
+
lines.push(`Note: ${plural(res.cases.length - withBodies, "case")} ${res.cases.length - withBodies === 1 ? "has" : "have"} no recorded prompt (the agent wasn't keeping prompts then). For those, rebuild the input from the case's \`expected\` or \`note\`, or skip them and say so.`, "");
|
|
111
|
+
}
|
|
112
|
+
lines.push("## Cases", "");
|
|
113
|
+
for (const c of res.cases) {
|
|
114
|
+
const flags = c.flags.length ? c.flags.map((f) => FLAG_LABEL[f] ?? f).join(", ") : "no flags";
|
|
115
|
+
lines.push(`- \`${c.id}\` · ${c.name} · ${c.kind} · ${flags} · ${plural(c.steps.length, "model call")}${c.expected ? ` · expected: "${c.expected.replace(/\s+/g, " ").slice(0, 200)}"` : ""}${c.note ? ` · note: "${c.note}"` : ""}`);
|
|
116
|
+
}
|
|
117
|
+
return lines.filter((line, i, all) => !(line === "" && all[i - 1] === "")).join("\n") + "\n";
|
|
118
|
+
}
|
|
119
|
+
/** The results.json a run starts from: every case, passes to be filled in. */
|
|
120
|
+
function resultsTemplate(res, model) {
|
|
121
|
+
return {
|
|
122
|
+
agent: res.agent.name,
|
|
123
|
+
suite: res.suite,
|
|
124
|
+
model: model ?? recordedModel(res),
|
|
125
|
+
judgeModel: res.judge?.model ?? null,
|
|
126
|
+
harness: null,
|
|
127
|
+
results: res.cases.map((c) => ({ caseId: c.id, name: c.name, passes: null, tries: TRIES, note: null, output: null })),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function flag(args, i, name) {
|
|
131
|
+
const a = args[i];
|
|
132
|
+
if (a === name && i + 1 < args.length)
|
|
133
|
+
return { value: args[i + 1], skip: 1 };
|
|
134
|
+
if (a.startsWith(`${name}=`))
|
|
135
|
+
return { value: a.slice(name.length + 1), skip: 0 };
|
|
136
|
+
return { skip: -1 };
|
|
137
|
+
}
|
|
138
|
+
function parseCasesArgs(args) {
|
|
139
|
+
const parsed = { json: false };
|
|
140
|
+
for (let i = 0; i < args.length; i++) {
|
|
141
|
+
const a = args[i];
|
|
142
|
+
let matched = false;
|
|
143
|
+
for (const [name, key] of [["--out", "out"], ["-o", "out"], ["--dataset", "suite"], ["--suite", "suite"], ["--model", "model"], ["--case", "caseIds"]]) {
|
|
144
|
+
const f = flag(args, i, name);
|
|
145
|
+
if (f.skip >= 0) {
|
|
146
|
+
parsed[key] = f.value;
|
|
147
|
+
i += f.skip;
|
|
148
|
+
matched = true;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (matched)
|
|
153
|
+
continue;
|
|
154
|
+
if (a === "--json")
|
|
155
|
+
parsed.json = true;
|
|
156
|
+
else if (!a.startsWith("-") && !parsed.agent)
|
|
157
|
+
parsed.agent = a;
|
|
158
|
+
}
|
|
159
|
+
return parsed;
|
|
160
|
+
}
|
|
161
|
+
async function runAgents(config) {
|
|
162
|
+
const start = Date.now();
|
|
163
|
+
let res;
|
|
164
|
+
try {
|
|
165
|
+
res = await (0, shared_1.apiRequest)("GET", "/api/cli/traces/agents", { config });
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
const e = err;
|
|
169
|
+
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_traces_agents", ok: false, latencyMs: Date.now() - start, error: e.message });
|
|
170
|
+
console.error(`Couldn't list agents: ${e.message}`);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_traces_agents", ok: true, latencyMs: Date.now() - start });
|
|
174
|
+
if (!res.agents.length) {
|
|
175
|
+
console.log("You don't maintain any agents yet. Connect one in Codiedev → Traces → How it works.");
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
for (const agent of res.agents) {
|
|
179
|
+
const capture = agent.promptDays ? `prompts kept ${agent.promptDays} days` : "metadata only";
|
|
180
|
+
console.log(`${agent.name.padEnd(28)} ${plural(agent.cases, "case").padStart(9)} ${capture}`);
|
|
181
|
+
}
|
|
182
|
+
console.log("\nPull one: codiedev traces cases <agent>");
|
|
183
|
+
}
|
|
184
|
+
async function runCases(args, config) {
|
|
185
|
+
const { agent, suite, model, caseIds, out, json } = parseCasesArgs(args);
|
|
186
|
+
if (!agent) {
|
|
187
|
+
console.error("Usage: codiedev traces cases <agent> [--dataset name] [--model id] [--out dir] [--json]");
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
const start = Date.now();
|
|
191
|
+
let res;
|
|
192
|
+
try {
|
|
193
|
+
res = await (0, shared_1.apiRequest)("GET", "/api/cli/traces/cases", { config, query: { agent, ...(suite ? { suite } : {}), ...(caseIds ? { case: caseIds } : {}) } });
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
const e = err;
|
|
197
|
+
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_traces_cases", ok: false, latencyMs: Date.now() - start, error: e.message });
|
|
198
|
+
if (e.status === 404)
|
|
199
|
+
console.error(`No agent called "${agent}". See your agents with: codiedev traces agents`);
|
|
200
|
+
else if (e.status === 403)
|
|
201
|
+
console.error(`Only ${agent}'s maintainers can pull its test cases.`);
|
|
202
|
+
else
|
|
203
|
+
console.error(`Couldn't pull cases: ${e.message}`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_traces_cases", ok: true, latencyMs: Date.now() - start });
|
|
207
|
+
if (json) {
|
|
208
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (!res.cases.length) {
|
|
212
|
+
console.log(suite
|
|
213
|
+
? `${res.agent.name} has no test cases in "${suite}". See your datasets in Codiedev → Traces → Test cases.`
|
|
214
|
+
: `${res.agent.name} has no saved test cases. Open a run in Codiedev → Traces and click "Save as test case".`);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
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)] : [])));
|
|
218
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
219
|
+
const casesFile = path.join(dir, "cases.jsonl");
|
|
220
|
+
fs.writeFileSync(casesFile, res.cases.map((c) => JSON.stringify(c)).join("\n") + "\n", "utf8");
|
|
221
|
+
const resultsFile = path.join(dir, "results.json");
|
|
222
|
+
fs.writeFileSync(resultsFile, JSON.stringify(resultsTemplate(res, model), null, 2) + "\n", "utf8");
|
|
223
|
+
const briefFile = path.join(dir, "RUN_CASES.md");
|
|
224
|
+
fs.writeFileSync(briefFile, casesBrief(res, casesFile, { model, resultsFile }), "utf8");
|
|
225
|
+
const fix = res.cases.filter((c) => c.kind === "fix").length;
|
|
226
|
+
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)`);
|
|
228
|
+
console.log(` ${rel(casesFile)}`);
|
|
229
|
+
console.log(` ${rel(briefFile)}`);
|
|
230
|
+
console.log(` ${rel(resultsFile)} (filled in by your coding agent, then: codiedev traces report ${rel(resultsFile)})`);
|
|
231
|
+
console.log(`\nNext: ask your coding agent to "run the test cases in ${rel(briefFile)}"`);
|
|
232
|
+
}
|
|
233
|
+
async function runReport(args, config) {
|
|
234
|
+
let file;
|
|
235
|
+
let harness;
|
|
236
|
+
for (let i = 0; i < args.length; i++) {
|
|
237
|
+
const f = flag(args, i, "--harness");
|
|
238
|
+
if (f.skip >= 0) {
|
|
239
|
+
harness = f.value;
|
|
240
|
+
i += f.skip;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (!args[i].startsWith("-") && !file)
|
|
244
|
+
file = args[i];
|
|
245
|
+
}
|
|
246
|
+
if (!file) {
|
|
247
|
+
console.error("Usage: codiedev traces report <results.json> [--harness name]");
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
let body;
|
|
251
|
+
try {
|
|
252
|
+
body = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), file), "utf8"));
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
console.error(`Couldn't read ${file}: ${err.message}`);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
const missing = body.results.filter((r) => typeof r.passes !== "number");
|
|
259
|
+
if (missing.length) {
|
|
260
|
+
console.error(`${plural(missing.length, "case")} in ${file} ${missing.length === 1 ? "has" : "have"} no "passes" yet: ${missing.slice(0, 5).map((r) => r.name).join(", ")}. Run them first.`);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
const start = Date.now();
|
|
264
|
+
try {
|
|
265
|
+
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/traces/results", {
|
|
266
|
+
config,
|
|
267
|
+
body: { ...body, harness: harness ?? body.harness ?? undefined },
|
|
268
|
+
});
|
|
269
|
+
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_traces_report", ok: true, latencyMs: Date.now() - start });
|
|
270
|
+
const passed = body.results.filter((r) => r.passes === r.tries).length;
|
|
271
|
+
console.log(`✓ Reported ${body.agent}${body.suite ? ` · ${body.suite}` : ""} on ${body.model}: ${Math.round(res.passRate * 100)}% of tries passed, ${passed}/${body.results.length} cases passed every try.`);
|
|
272
|
+
console.log(" See it next to other models in Codiedev → Traces → Test cases.");
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
const e = err;
|
|
276
|
+
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_traces_report", ok: false, latencyMs: Date.now() - start, error: e.message });
|
|
277
|
+
console.error(e.status === 403 ? `Only ${body.agent}'s maintainers can report its results.` : `Couldn't report: ${e.message}`);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async function runTraces(args, configOverride) {
|
|
282
|
+
const [sub, ...rest] = args;
|
|
283
|
+
if (sub === "agents")
|
|
284
|
+
return runAgents(configOverride ?? (0, shared_1.requireConfig)());
|
|
285
|
+
if (sub === "cases")
|
|
286
|
+
return runCases(rest, configOverride ?? (0, shared_1.requireConfig)());
|
|
287
|
+
if (sub === "report")
|
|
288
|
+
return runReport(rest, configOverride ?? (0, shared_1.requireConfig)());
|
|
289
|
+
console.log(exports.TRACES_HELP);
|
|
290
|
+
if (sub && sub !== "help")
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
package/dist/piRuntime.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface PiRuntimeDeps {
|
|
|
16
16
|
rename?: (from: string, to: string) => void;
|
|
17
17
|
remove?: (target: string) => void;
|
|
18
18
|
log?: (message: string) => void;
|
|
19
|
+
sleep?: (ms: number) => void;
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
22
|
* The pi CLI entry to run, installing the pinned version first if it is
|
package/dist/piRuntime.js
CHANGED
|
@@ -70,6 +70,10 @@ function installedPiEntry(installDir, readFile = (f) => fs.readFileSync(f, "utf8
|
|
|
70
70
|
return null;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
+
// One retry rides out a dropped connection or registry blip; a second
|
|
74
|
+
// failure is a real problem (offline, blocked registry) and is reported.
|
|
75
|
+
const INSTALL_ATTEMPTS = 2;
|
|
76
|
+
const INSTALL_RETRY_DELAY_MS = 5_000;
|
|
73
77
|
/**
|
|
74
78
|
* The pi CLI entry to run, installing the pinned version first if it is
|
|
75
79
|
* missing or a different version. Staged then renamed, so an interrupted
|
|
@@ -85,10 +89,10 @@ function ensurePiRuntime(deps) {
|
|
|
85
89
|
const remove = deps.remove ?? ((target) => fs.rmSync(target, { recursive: true, force: true }));
|
|
86
90
|
const rename = deps.rename ?? fs.renameSync;
|
|
87
91
|
const log = deps.log ?? ((message) => console.error(message));
|
|
88
|
-
const
|
|
92
|
+
const makeStagingDir = deps.makeStagingDir ?? (() => {
|
|
89
93
|
fs.mkdirSync(deps.stateDir, { recursive: true });
|
|
90
94
|
return fs.mkdtempSync(path.join(deps.stateDir, ".runtime-pi-"));
|
|
91
|
-
})
|
|
95
|
+
});
|
|
92
96
|
const run = deps.run ?? ((args) => {
|
|
93
97
|
const result = (0, child_process_1.spawnSync)(process.platform === "win32" ? "npm.cmd" : "npm", args, {
|
|
94
98
|
encoding: "utf8", stdio: ["ignore", "ignore", "pipe"], shell: process.platform === "win32",
|
|
@@ -97,26 +101,35 @@ function ensurePiRuntime(deps) {
|
|
|
97
101
|
throw result.error;
|
|
98
102
|
return { status: result.status, stderr: result.stderr ?? "" };
|
|
99
103
|
});
|
|
104
|
+
const sleep = deps.sleep ?? ((ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms));
|
|
100
105
|
log(`Installing pi (${exports.PI_RUNTIME_SPEC}). This runs once, and only for \`codie --pi\`.`);
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
for (let attempt = 1;; attempt++) {
|
|
107
|
+
const staging = makeStagingDir();
|
|
108
|
+
try {
|
|
109
|
+
const result = run((0, codieRuntimeInstall_1.buildRuntimeInstallArgs)(staging, exports.PI_RUNTIME_SPEC));
|
|
110
|
+
if (result.status !== 0)
|
|
111
|
+
throw new Error(result.stderr?.trim() || `npm exited with ${result.status}`);
|
|
112
|
+
// Trust the artifact, not the exit code.
|
|
113
|
+
const staged = installedPiEntry(staging, readFile);
|
|
114
|
+
if (!staged || !exists(staged)) {
|
|
115
|
+
throw new Error(`the install did not produce pi. Check that ${exports.PI_RUNTIME_SPEC} is available from your npm registry.`);
|
|
116
|
+
}
|
|
117
|
+
remove(installDir);
|
|
118
|
+
rename(staging, installDir);
|
|
119
|
+
break;
|
|
109
120
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
catch (error) {
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
if (attempt >= INSTALL_ATTEMPTS)
|
|
124
|
+
throw new Error(`Couldn't install pi: ${message}`);
|
|
125
|
+
log(`pi install failed (${message.split("\n")[0]}); retrying.`);
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
// No-op after a successful promote; otherwise it keeps a ~200MB staging
|
|
129
|
+
// dir from being orphaned.
|
|
130
|
+
remove(staging);
|
|
131
|
+
}
|
|
132
|
+
sleep(INSTALL_RETRY_DELAY_MS);
|
|
120
133
|
}
|
|
121
134
|
const entry = installedPiEntry(installDir, readFile);
|
|
122
135
|
if (!entry)
|
package/dist/utils.js
CHANGED
|
@@ -1681,6 +1681,8 @@ 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>\`) |
|
|
1685
|
+
| "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) |
|
|
1684
1686
|
|
|
1685
1687
|
**Just push the file — we'll figure out where it goes.** Every artifact
|
|
1686
1688
|
is classified into one of two kinds at upload time:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.76",
|
|
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",
|