@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.
- package/README.md +4 -3
- package/dist/agent/compaction.d.ts.map +1 -1
- package/dist/agent/compaction.js +17 -13
- package/dist/agent/hooks.d.ts +13 -0
- package/dist/agent/hooks.d.ts.map +1 -1
- package/dist/agent/hooks.js +1 -0
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +67 -2
- package/dist/agent/sharedTasks.d.ts +21 -6
- package/dist/agent/sharedTasks.d.ts.map +1 -1
- package/dist/agent/sharedTasks.js +69 -17
- package/dist/agent/taskEvents.d.ts +73 -0
- package/dist/agent/taskEvents.d.ts.map +1 -0
- package/dist/agent/taskEvents.js +297 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/plugins/eval.d.ts +199 -0
- package/dist/plugins/eval.d.ts.map +1 -0
- package/dist/plugins/eval.js +470 -0
- package/dist/tools/executor.d.ts +6 -0
- package/dist/tools/executor.d.ts.map +1 -1
- package/dist/tools/executor.js +116 -6
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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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"}
|