@nexrall/code-core 1.4.73 → 1.4.75

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.
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports._taskEventsInternal = void 0;
37
+ exports.taskScopeRoot = taskScopeRoot;
38
+ exports.taskScopeDir = taskScopeDir;
39
+ exports.taskEventsFile = taskEventsFile;
40
+ exports.emitTaskEvent = emitTaskEvent;
41
+ exports.takeNewTaskEvents = takeNewTaskEvents;
42
+ exports.formatTaskEventNotes = formatTaskEventNotes;
43
+ /**
44
+ * Task board events — the notification side of the shared task list
45
+ * (@see ./sharedTasks.ts for the board itself).
46
+ *
47
+ * WHY THIS EXISTS: the board is a directory of files, so a change made by one
48
+ * session becomes visible to every other session the moment it lands — and
49
+ * visibility with no signal is a poll tax. The concrete failure it removes:
50
+ * session B creates "write the release notes" blocked by "cut the release",
51
+ * session A runs `nex tasks done cut-the-release` (or its own tool call), and
52
+ * B — sitting at its prompt or mid-turn — has no way to learn its task just
53
+ * became claimable except by re-listing the board on a hunch. Claude Code's
54
+ * agent teams has no cross-session signal at all: teammates learn about each
55
+ * other's progress only by polling the task files.
56
+ *
57
+ * HOW: every mutation appends one JSON line to `events.jsonl` in the board
58
+ * scope directory (same directory as the task files; `listSharedTasks` skips
59
+ * it — it only reads `*.json`). Readers keep a per-(scope, session) BYTE
60
+ * cursor and pull what is new; there is no watcher, no socket and no queue,
61
+ * which is what lets the same mechanism work no matter who made the change —
62
+ * a tool call inside another interactive session, a headless `nex --bg` run,
63
+ * a VS Code window, or a person typing `nex tasks done` in a shell.
64
+ *
65
+ * DELIVERY SEMANTICS, on purpose:
66
+ * • A reader adopts the CURRENT end of the file the first time it looks, so
67
+ * a fresh session is never handed a replay of the board's history — the
68
+ * board STATE is what tools read; events are only about what changed
69
+ * while you were watching. (chat.ts primes the cursor at session start —
70
+ * see the takeNewTaskEvents call next to its CheckpointManager — rather
71
+ * than letting the first in-loop drain anchor, so an event that lands
72
+ * while the session's opening request is in flight is still delivered.)
73
+ * • A session never hears about its own actions: events carry the writing
74
+ * process's pid and readers drop their own pid. (Frame: events are
75
+ * notifications from OTHER processes. Within one process, e.g. two VS
76
+ * Code chat panels on the same workspace, the panel that made a change
77
+ * will not self-notify — its own tool result already said what happened.)
78
+ * • A torn line (a reader racing a writer mid-append) is never half-parsed:
79
+ * the cursor only advances past the last complete line.
80
+ *
81
+ * The module must never make a board mutation fail — every write here is
82
+ * best-effort and swallows its own errors. The board is the product; the
83
+ * events are a courtesy on top of it.
84
+ */
85
+ const crypto = __importStar(require("crypto"));
86
+ const fs = __importStar(require("fs"));
87
+ const os = __importStar(require("os"));
88
+ const path = __importStar(require("path"));
89
+ // ─── scope ────────────────────────────────────────────────────────────────────
90
+ // Moved here (from sharedTasks.ts) so this module can address the board
91
+ // directory without importing the mutators — one canonical hash, no cycle:
92
+ // sharedTasks imports THIS module, never the other way round.
93
+ function tasksRoot() {
94
+ return process.env.NEXRALL_SHARED_TASKS_DIR || path.join(os.homedir(), '.nexrall', 'peers', 'tasks');
95
+ }
96
+ /** The root directory all board scopes live under (env-overridable — tests redirect it). */
97
+ function taskScopeRoot() {
98
+ return tasksRoot();
99
+ }
100
+ /** The board scope directory for a project — one per resolved workDir, shared by every process. */
101
+ function taskScopeDir(workDir) {
102
+ const hash = crypto.createHash('sha256').update(path.resolve(workDir)).digest('hex').slice(0, 24);
103
+ return path.join(tasksRoot(), hash);
104
+ }
105
+ function taskEventsFile(workDir) {
106
+ return path.join(taskScopeDir(workDir), 'events.jsonl');
107
+ }
108
+ // ─── write ────────────────────────────────────────────────────────────────────
109
+ /**
110
+ * Append one event to the board's log. Called by every mutator in sharedTasks
111
+ * right after its atomic write lands. Best-effort by contract: a failure here
112
+ * must NEVER surface to the caller — the mutation itself already succeeded.
113
+ */
114
+ function emitTaskEvent(dir, event) {
115
+ try {
116
+ fs.mkdirSync(dir, { recursive: true });
117
+ const line = JSON.stringify({ ...event, ts: event.ts ?? Date.now(), pid: event.pid ?? process.pid }) + '\n';
118
+ // One appendFileSync per event: O_APPEND, so concurrent writers cannot
119
+ // overwrite each other, and a single small write lands whole (readers
120
+ // still guard against the torn-line case rather than trusting that).
121
+ fs.appendFileSync(eventsFileIn(dir), line);
122
+ }
123
+ catch {
124
+ /* courtesy, not contract */
125
+ }
126
+ }
127
+ function eventsFileIn(dir) {
128
+ return path.join(dir, 'events.jsonl');
129
+ }
130
+ // ─── read ─────────────────────────────────────────────────────────────────────
131
+ /** Per-(scope, session) byte cursors, in memory: a process restarts → a session restarts. */
132
+ const _cursors = new Map();
133
+ /**
134
+ * New events for this reader — everything appended since the last call, minus
135
+ * this process's own writes. The FIRST call for a (scope, session) adopts the
136
+ * current end of the log and returns nothing: events describe change relative
137
+ * to when you started looking, never the board's backstory.
138
+ */
139
+ function takeNewTaskEvents(workDir, opts = {}) {
140
+ const dir = taskScopeDir(workDir);
141
+ const key = `${dir}|${opts.sessionKey ?? ''}`;
142
+ const file = eventsFileIn(dir);
143
+ let size;
144
+ try {
145
+ size = fs.statSync(file).size;
146
+ }
147
+ catch {
148
+ // No log yet. Still ANCHOR (at zero): the file appearing later is exactly
149
+ // the "new events" case — without this, a brand-new board's first activity
150
+ // would be adopted-away by the next call's first-sight rule.
151
+ if (!_cursors.has(key))
152
+ _cursors.set(key, 0);
153
+ return [];
154
+ }
155
+ const from = _cursors.get(key);
156
+ if (from === undefined) {
157
+ _cursors.set(key, size);
158
+ return [];
159
+ }
160
+ if (size <= from) {
161
+ // Equal: nothing new. Shrunk: the log was replaced under us (someone
162
+ // cleared the scope directory) — re-anchor rather than replay, the only
163
+ // two options that cannot double-deliver.
164
+ if (size < from)
165
+ _cursors.set(key, size);
166
+ return [];
167
+ }
168
+ const length = size - from;
169
+ let buf;
170
+ try {
171
+ const fd = fs.openSync(file, 'r');
172
+ try {
173
+ buf = Buffer.alloc(length);
174
+ const read = fs.readSync(fd, buf, 0, length, from);
175
+ if (read < length)
176
+ buf = buf.subarray(0, read);
177
+ }
178
+ finally {
179
+ fs.closeSync(fd);
180
+ }
181
+ }
182
+ catch {
183
+ return [];
184
+ }
185
+ // Only advance past the last COMPLETE line — a writer may be mid-append.
186
+ const lastNewline = buf.lastIndexOf(0x0a);
187
+ if (lastNewline === -1)
188
+ return [];
189
+ const complete = buf.subarray(0, lastNewline + 1);
190
+ _cursors.set(key, from + complete.length);
191
+ const selfPid = opts.selfPid ?? process.pid;
192
+ const events = [];
193
+ for (const line of complete.toString('utf-8').split('\n')) {
194
+ if (!line.trim())
195
+ continue;
196
+ try {
197
+ const ev = JSON.parse(line);
198
+ if (ev && typeof ev.taskId === 'string' && typeof ev.type === 'string' && typeof ev.subject === 'string') {
199
+ events.push(ev);
200
+ }
201
+ }
202
+ catch {
203
+ /* a corrupt line is skipped, never fatal */
204
+ }
205
+ }
206
+ return events.filter((ev) => ev.pid !== selfPid);
207
+ }
208
+ /** Deps of `task` still NOT completed, given the live board. */
209
+ function unmetDeps(task, byId) {
210
+ return (task.blockedBy ?? []).filter((id) => byId.get(id)?.status !== 'completed');
211
+ }
212
+ function quoteRef(id, content) {
213
+ const subject = content.replace(/\s+/g, ' ').trim();
214
+ return `[${id}] "${subject.length > 80 ? subject.slice(0, 79) + '…' : subject}"`;
215
+ }
216
+ function quote(t) {
217
+ return quoteRef(t.id, t.content);
218
+ }
219
+ /** An event carries the subject directly — the task file may already be gone (removed). */
220
+ function quoteEvent(e) {
221
+ return quoteRef(e.taskId, e.subject);
222
+ }
223
+ /**
224
+ * Turn raw events into model-bound notes — but only for the events that
225
+ * actually changed what somebody can DO, because the rest are board gossip:
226
+ *
227
+ * completed → every open task waiting on it is now unblocked (or one dep
228
+ * closer), for whoever gets there first;
229
+ * reopened → the reverse: waiting tasks are stuck again;
230
+ * removed → waiting tasks are stuck FOREVER (a dep that can never
231
+ * complete) unless it comes back — the one case the board
232
+ * itself cannot express, since the task file is gone.
233
+ *
234
+ * created / claimed / released produce no note: they change no one's options
235
+ * (released → the task is pending again, which its `blockedBy` already gates).
236
+ * Hooks still see every event — the matcher decides — but the model only gets
237
+ * told about consequences.
238
+ */
239
+ function formatTaskEventNotes(events, tasks) {
240
+ const byId = new Map(tasks.map((t) => [t.id, t]));
241
+ const open = tasks.filter((t) => t.status !== 'completed');
242
+ const notes = [];
243
+ for (const event of events) {
244
+ if (event.type !== 'completed' && event.type !== 'reopened' && event.type !== 'removed')
245
+ continue;
246
+ const waiters = open.filter((t) => t.id !== event.taskId && (t.blockedBy ?? []).includes(event.taskId));
247
+ if (!waiters.length)
248
+ continue;
249
+ const actor = event.actor ? ` by ${event.actor}` : '';
250
+ if (event.type === 'completed') {
251
+ const nowFree = [];
252
+ const stillStuck = [];
253
+ for (const t of waiters) {
254
+ (unmetDeps(t, byId).length === 0 ? nowFree : stillStuck).push(quote(t));
255
+ }
256
+ const lines = [`Task ${quoteEvent(event)} was completed${actor}.`];
257
+ if (nowFree.length) {
258
+ lines.push(`Now unblocked: ${nowFree.join(', ')} — claim${nowFree.length === 1 ? ' it' : ' them'} when ready.`);
259
+ }
260
+ if (stillStuck.length) {
261
+ lines.push(`Still waiting on other tasks: ${stillStuck.join(', ')}.`);
262
+ }
263
+ notes.push({
264
+ event,
265
+ text: envelope(lines.join('\n')),
266
+ unblocked: waiters.filter((t) => unmetDeps(t, byId).length === 0).map((t) => t.id),
267
+ });
268
+ }
269
+ else if (event.type === 'reopened') {
270
+ notes.push({
271
+ event,
272
+ text: envelope(`Task ${quoteEvent(event)} was reopened${actor} — it must be completed again before: ${waiters.map(quote).join(', ')}.`),
273
+ unblocked: [],
274
+ });
275
+ }
276
+ else {
277
+ notes.push({
278
+ event,
279
+ text: envelope(`Task ${quoteEvent(event)} was removed${actor} while ${waiters.map(quote).join(', ')} still ${waiters.length === 1 ? 'waits' : 'wait'} on it. A removed task can never complete, so those stay blocked until it is recreated (or they are re-created without it).`),
280
+ unblocked: [],
281
+ });
282
+ }
283
+ }
284
+ return notes;
285
+ }
286
+ function envelope(body) {
287
+ return (`<task-event source="shared-tasks">\n` +
288
+ `${body}\n` +
289
+ `This is an automated notification, not a message from the user.\n` +
290
+ `</task-event>`);
291
+ }
292
+ /** Test-only handles — the cursor map is module state and must be observable/resettable. */
293
+ exports._taskEventsInternal = {
294
+ _cursors,
295
+ eventsFileIn,
296
+ };
297
+ //# sourceMappingURL=taskEvents.js.map
package/dist/index.d.ts CHANGED
@@ -36,11 +36,13 @@ export * from './plugins/index';
36
36
  export * from './plugins/installer';
37
37
  export * from './plugins/sources';
38
38
  export * from './plugins/data';
39
+ export * from './plugins/eval';
39
40
  export * from './agent/trust';
40
41
  export * from './agent/crossProcessLock';
41
42
  export * from './agent/worktree';
42
43
  export * from './agent/worktreeEnforcement';
43
44
  export * from './agent/sharedTasks';
45
+ export * from './agent/taskEvents';
44
46
  export * from './agent/peerRegistry';
45
47
  export * from './agent/peerTransport';
46
48
  export * from './agent/askOnce';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -52,11 +52,13 @@ __exportStar(require("./plugins/index"), exports);
52
52
  __exportStar(require("./plugins/installer"), exports);
53
53
  __exportStar(require("./plugins/sources"), exports);
54
54
  __exportStar(require("./plugins/data"), exports);
55
+ __exportStar(require("./plugins/eval"), exports);
55
56
  __exportStar(require("./agent/trust"), exports);
56
57
  __exportStar(require("./agent/crossProcessLock"), exports);
57
58
  __exportStar(require("./agent/worktree"), exports);
58
59
  __exportStar(require("./agent/worktreeEnforcement"), exports);
59
60
  __exportStar(require("./agent/sharedTasks"), exports);
61
+ __exportStar(require("./agent/taskEvents"), exports);
60
62
  __exportStar(require("./agent/peerRegistry"), exports);
61
63
  __exportStar(require("./agent/peerTransport"), exports);
62
64
  __exportStar(require("./agent/askOnce"), exports);
@@ -0,0 +1,199 @@
1
+ export declare const EVAL_SCHEMA_VERSION = 1;
2
+ /** CC's default: each case runs three times. */
3
+ export declare const EVAL_DEFAULT_RUNS = 3;
4
+ export declare const EVAL_MAX_RUNS = 50;
5
+ export declare const EVAL_DEFAULT_THRESHOLD = 1;
6
+ export declare const EVAL_DEFAULT_MAX_TURNS = 10;
7
+ export declare const EVAL_DEFAULT_TIMEOUT_S = 300;
8
+ /** CC caps both; larger values are refused, not clamped silently. */
9
+ export declare const EVAL_MAX_TURNS_CEILING = 200;
10
+ export declare const EVAL_MAX_TIMEOUT_S = 3600;
11
+ export type GraderType = 'regex' | 'tool_used' | 'tool_order' | 'file_exists' | 'llm' | 'baseline';
12
+ /**
13
+ * Which arm a grader scores in.
14
+ * · 'both' (default) — counted for the plugin arm AND the no-plugin baseline.
15
+ * · 'with-only' — counted only when the plugin was loaded (CC's rule for the
16
+ * two-arm runs: a grader that can only pass WITH the plugin must not drag
17
+ * the baseline down).
18
+ */
19
+ export type GraderArm = 'both' | 'with-only';
20
+ export interface EvalGrader {
21
+ /** File name inside graders/, for reporting. */
22
+ file: string;
23
+ type: GraderType;
24
+ weight: number;
25
+ arm: GraderArm;
26
+ pattern?: string;
27
+ flags?: string;
28
+ /** 'contains' (default) | 'not_contains' | 'count:<n>' */
29
+ match?: string;
30
+ /** 'last_message' (default) | 'trace' | 'file' */
31
+ target?: string;
32
+ /** target 'file': project-relative path or glob to read. */
33
+ pathPattern?: string;
34
+ tool?: string;
35
+ inputMatch?: string;
36
+ min?: number;
37
+ max?: number;
38
+ before?: string;
39
+ after?: string;
40
+ path?: string;
41
+ exists?: boolean;
42
+ criteria?: string;
43
+ /** llm: 'last_message' (default) | 'trace'. */
44
+ focus?: string;
45
+ /** baseline: the reference transcript file (case-relative). */
46
+ baselineFile?: string;
47
+ }
48
+ export interface EvalCase {
49
+ name: string;
50
+ dir: string;
51
+ prompt: string;
52
+ runs: number;
53
+ maxTurns: number;
54
+ timeoutSeconds: number;
55
+ allowedTools: string[];
56
+ tags: string[];
57
+ expectedOutcome?: string;
58
+ graders: EvalGrader[];
59
+ }
60
+ export interface DiscoveredSuite {
61
+ cases: EvalCase[];
62
+ /** Case dirs that failed to parse — reported, never silently dropped. */
63
+ errors: Array<{
64
+ dir: string;
65
+ error: string;
66
+ }>;
67
+ }
68
+ /** One tool call as the child's stream reported it. */
69
+ export interface EvalToolCall {
70
+ tool: string;
71
+ input: unknown;
72
+ }
73
+ /**
74
+ * What ONE run produced. `readFile` resolves a project-relative path or glob
75
+ * against the run's workspace — the caller owns the filesystem, so the graders
76
+ * stay pure.
77
+ */
78
+ export interface EvalRunTrace {
79
+ lastMessage: string;
80
+ toolCalls: EvalToolCall[];
81
+ files: string[];
82
+ readFile: (relOrGlob: string) => string | null;
83
+ /** USD when the server reported it; null = unreported (never invented). */
84
+ costUsd: number | null;
85
+ tokens: number;
86
+ exitCode: number;
87
+ error?: string;
88
+ }
89
+ export interface GraderVerdict {
90
+ passed: boolean;
91
+ why: string;
92
+ }
93
+ /** The injected model call for llm/baseline graders. null = could not judge. */
94
+ export type EvalJudge = (args: {
95
+ criteria: string;
96
+ focusText: string;
97
+ baselineText?: string;
98
+ }) => Promise<boolean | null>;
99
+ export interface CaseRunOutcome {
100
+ arm: 'with' | 'without';
101
+ run: number;
102
+ score: number;
103
+ graderResults: Array<{
104
+ file: string;
105
+ type: GraderType;
106
+ weight: number;
107
+ passed: boolean;
108
+ why: string;
109
+ }>;
110
+ costUsd: number | null;
111
+ costReported: boolean;
112
+ durationMs: number;
113
+ error?: string;
114
+ }
115
+ export interface CaseOutcome {
116
+ name: string;
117
+ with: CaseRunOutcome[];
118
+ without: CaseRunOutcome[];
119
+ withScore: number | null;
120
+ withoutScore: number | null;
121
+ delta: number | null;
122
+ passed: boolean;
123
+ }
124
+ export interface EvalReport {
125
+ schemaVersion: number;
126
+ plugin: string;
127
+ startedAt: number;
128
+ durationMs: number;
129
+ threshold: number;
130
+ runs: number;
131
+ judgeModel?: string;
132
+ partial?: boolean;
133
+ partialReason?: string;
134
+ cases: CaseOutcome[];
135
+ errors: Array<{
136
+ dir: string;
137
+ error: string;
138
+ }>;
139
+ aggregates: {
140
+ casesPassed: number;
141
+ casesTotal: number;
142
+ overallScore: number;
143
+ meanDelta: number | null;
144
+ };
145
+ costUsd: number;
146
+ costMissing: number;
147
+ }
148
+ /** `[a, b]` / `a, b` / `a` → ['a','b'] — the two shapes real files use. */
149
+ export declare function parseList(value: string | undefined): string[];
150
+ /**
151
+ * One grader file → EvalGrader. Throws on anything that would make the grader
152
+ * silently unscorable (unknown type, missing pattern/tool/path, bad count).
153
+ */
154
+ export declare function parseGraderFile(file: string, raw: string): EvalGrader;
155
+ /** One case directory → EvalCase. See the header for the layout. */
156
+ export declare function parseEvalCase(dir: string): EvalCase;
157
+ /** Every case under `<pluginDir>/evals` (the results dir is never a case). */
158
+ export declare function discoverEvalCases(pluginDir: string): DiscoveredSuite;
159
+ /** The text a grader's target names. Exposed for tests and the report's `why`. */
160
+ export declare function targetText(g: EvalGrader, trace: EvalRunTrace): string | null;
161
+ /**
162
+ * Score ONE grader over ONE run. Async because llm/baseline call the judge.
163
+ * A grader that cannot be evaluated FAILS (with the reason in `why`) — it must
164
+ * never pass by accident, or a broken suite would look green.
165
+ */
166
+ export declare function evaluateGrader(g: EvalGrader, trace: EvalRunTrace, judge: EvalJudge | undefined): Promise<GraderVerdict>;
167
+ /** Which graders count in an arm: with-only graders never score the baseline. */
168
+ export declare function gradersForArm(graders: EvalGrader[], arm: 'with' | 'without'): EvalGrader[];
169
+ /** Score one run: weighted fraction of its graders that passed. */
170
+ export declare function scoreRun(graders: EvalGrader[], trace: EvalRunTrace, judge: EvalJudge | undefined, arm: 'with' | 'without'): Promise<{
171
+ score: number;
172
+ results: CaseRunOutcome['graderResults'];
173
+ }>;
174
+ /**
175
+ * Fold run outcomes into the report. Case score = mean of the plugin-arm run
176
+ * scores; `delta` = with − without (null when the baseline did not run).
177
+ * A case PASSES at ≥ threshold (CC's default: every grader, every run).
178
+ */
179
+ export declare function aggregateReport(i: {
180
+ plugin: string;
181
+ startedAt: number;
182
+ durationMs: number;
183
+ threshold: number;
184
+ runs: number;
185
+ judgeModel?: string;
186
+ cases: Array<{
187
+ name: string;
188
+ with: CaseRunOutcome[];
189
+ without: CaseRunOutcome[];
190
+ }>;
191
+ errors: Array<{
192
+ dir: string;
193
+ error: string;
194
+ }>;
195
+ partial?: {
196
+ reason: string;
197
+ };
198
+ }): EvalReport;
199
+ //# sourceMappingURL=eval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval.d.ts","sourceRoot":"","sources":["../../src/plugins/eval.ts"],"names":[],"mappings":"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"}