dsh-session-eval 0.2.0 → 0.2.1
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/CHANGELOG.md +30 -0
- package/cordis.patch.yml +6 -0
- package/lib/index.d.ts +49 -2
- package/lib/index.js +132 -6
- package/package.json +10 -10
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.1 — 2026-09-26
|
|
4
|
+
|
|
5
|
+
- **Fix: ship the bundle overlay.** v0.2.0 declared `dsh.bundle.patch: ./cordis.patch.yml`
|
|
6
|
+
in `package.json` but the file was never committed, so the published tarball did not
|
|
7
|
+
contain it and `dsh web` failed at startup with
|
|
8
|
+
`failed to read overlay .../dsh-session-eval/cordis.patch.yml: ENOENT`.
|
|
9
|
+
The overlay (mounting `/eval`, `/eval-diff`, `/eval-history`) is now in the tarball;
|
|
10
|
+
verified with `npm pack --dry-run`.
|
|
11
|
+
- **Fix: peer ranges match the current harness.** `@deepseek-ai/*` peers and dev
|
|
12
|
+
dependencies moved from `^0.1.1-rc.2` to `^0.1.5-rc.3` (semver prerelease ranges do not
|
|
13
|
+
cross `0.1.1-rc.x` → `0.1.5-rc.3`, which let stale `0.1.1-rc.2` copies install into
|
|
14
|
+
profiles and shadow the running harness). `@deepseek-ai/cordis` pinned to `4.0.2`,
|
|
15
|
+
matching the official plugins at this harness level.
|
|
16
|
+
- Added this changelog; `files` always listed it but it was never written.
|
|
17
|
+
|
|
18
|
+
## 0.2.0 — 2026-09-25
|
|
19
|
+
|
|
20
|
+
- New `/eval-history [N] [--json]` command: lists the current workspace's recent sessions
|
|
21
|
+
(same `sessionQuery` seam as `/archive`), grades the last N, and diffs first-vs-last for
|
|
22
|
+
a trend verdict. Caps the manual id-picking that `/eval-diff` required.
|
|
23
|
+
- Version string single-sourced from `src/version.ts` (replaces four hardcoded `v0.1.0`
|
|
24
|
+
strings).
|
|
25
|
+
- 8 new tests (35 total) and `typecheck` script.
|
|
26
|
+
|
|
27
|
+
## 0.1.0 — 2026-09-24
|
|
28
|
+
|
|
29
|
+
- Initial release: `/eval` grade cards from persisted session logs (tool-error rate,
|
|
30
|
+
follow-up signals, tool load), `/eval-diff` regression comparison, no LLM judge.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# The dsh-session-eval bundle patch: mounts /eval, /eval-diff, and /eval-history.
|
|
2
|
+
# Applied as one insert; requires `commands` and `sessionQuery` services,
|
|
3
|
+
# which the shipped dsh-base / web bundles already provide.
|
|
4
|
+
- insert:
|
|
5
|
+
- id: session-eval
|
|
6
|
+
name: 'dsh-session-eval'
|
package/lib/index.d.ts
CHANGED
|
@@ -132,10 +132,57 @@ declare function executeEval(ctx: Context, invocation: CommandInvocation, seam?:
|
|
|
132
132
|
/** Execute /eval-diff against the session-query seam. */
|
|
133
133
|
declare function executeEvalDiff(ctx: Context, invocation: CommandInvocation, seam?: QuerySeam): Promise<CommandResult>;
|
|
134
134
|
//#endregion
|
|
135
|
+
//#region src/historyCommand.d.ts
|
|
136
|
+
declare const EVAL_HISTORY_USAGE = "Usage: /eval-history [N] [--json] (N = last N sessions, 1-20, default 5)";
|
|
137
|
+
/** Args for /eval-history. */
|
|
138
|
+
interface EvalHistoryArgs {
|
|
139
|
+
readonly count: number;
|
|
140
|
+
readonly json: boolean;
|
|
141
|
+
}
|
|
142
|
+
/** Parse /eval-history input; returns args or a usage-error string. */
|
|
143
|
+
declare function parseEvalHistoryArgs(rawInput: string): EvalHistoryArgs | string;
|
|
144
|
+
/** One graded session in a history report. */
|
|
145
|
+
interface HistoryEntry {
|
|
146
|
+
readonly sessionId: string;
|
|
147
|
+
readonly createdAt: number | null;
|
|
148
|
+
readonly metrics: SessionMetrics;
|
|
149
|
+
readonly card: GradeCard;
|
|
150
|
+
}
|
|
151
|
+
/** The full /eval-history report. */
|
|
152
|
+
interface HistoryReport {
|
|
153
|
+
readonly entries: readonly HistoryEntry[];
|
|
154
|
+
readonly trend: TrendReport | null;
|
|
155
|
+
}
|
|
156
|
+
/** The slice of sessionQuery this command consumes (structural, for testability). */
|
|
157
|
+
interface HistorySeam {
|
|
158
|
+
listSessions(): Promise<ReadonlyArray<{
|
|
159
|
+
header: {
|
|
160
|
+
id: unknown;
|
|
161
|
+
createdAt: unknown;
|
|
162
|
+
cwd?: unknown;
|
|
163
|
+
};
|
|
164
|
+
}>>;
|
|
165
|
+
readSession(id: ReturnType<typeof SessionId>): Promise<{
|
|
166
|
+
events: readonly unknown[];
|
|
167
|
+
}>;
|
|
168
|
+
}
|
|
169
|
+
/** Render a history report as terminal text. */
|
|
170
|
+
declare function renderHistory(report: HistoryReport): string;
|
|
171
|
+
/** Execute /eval-history against the session-query seam. */
|
|
172
|
+
declare function executeEvalHistory(ctx: Context, invocation: CommandInvocation, seam?: HistorySeam): Promise<CommandResult>;
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/version.d.ts
|
|
175
|
+
/**
|
|
176
|
+
* Single source of truth for the plugin version string.
|
|
177
|
+
*
|
|
178
|
+
* @module dsh-session-eval/version
|
|
179
|
+
*/
|
|
180
|
+
declare const VERSION = "0.2.1";
|
|
181
|
+
//#endregion
|
|
135
182
|
//#region src/index.d.ts
|
|
136
183
|
declare const name = "session-eval";
|
|
137
184
|
declare const inject: string[];
|
|
138
|
-
/** Plugin entry: mount the /eval and /eval-
|
|
185
|
+
/** Plugin entry: mount the /eval, /eval-diff and /eval-history commands. */
|
|
139
186
|
declare function apply(ctx: Context): void;
|
|
140
187
|
//#endregion
|
|
141
|
-
export { type DimensionDelta, type DimensionGrade, EVAL_DIFF_USAGE, EVAL_USAGE, type EvalArgs, type EvalDiffArgs, type EvalEvent, type EvalEventKind, type Grade, type GradeCard, type SessionMetrics, type TrendReport, adaptEvents, apply, compareSessions, computeMetrics, executeEval, executeEvalDiff, gradeSession, id8, inject, name, parseEvalArgs, parseEvalDiffArgs, renderCard, renderTrend };
|
|
188
|
+
export { type DimensionDelta, type DimensionGrade, EVAL_DIFF_USAGE, EVAL_HISTORY_USAGE, EVAL_USAGE, type EvalArgs, type EvalDiffArgs, type EvalEvent, type EvalEventKind, type EvalHistoryArgs, type Grade, type GradeCard, type HistoryEntry, type HistoryReport, type HistorySeam, type SessionMetrics, type TrendReport, VERSION, adaptEvents, apply, compareSessions, computeMetrics, executeEval, executeEvalDiff, executeEvalHistory, gradeSession, id8, inject, name, parseEvalArgs, parseEvalDiffArgs, parseEvalHistoryArgs, renderCard, renderHistory, renderTrend };
|
package/lib/index.js
CHANGED
|
@@ -221,6 +221,14 @@ function compareSessions(before, after) {
|
|
|
221
221
|
};
|
|
222
222
|
}
|
|
223
223
|
//#endregion
|
|
224
|
+
//#region src/version.ts
|
|
225
|
+
/**
|
|
226
|
+
* Single source of truth for the plugin version string.
|
|
227
|
+
*
|
|
228
|
+
* @module dsh-session-eval/version
|
|
229
|
+
*/
|
|
230
|
+
const VERSION = "0.2.1";
|
|
231
|
+
//#endregion
|
|
224
232
|
//#region src/evalCommand.ts
|
|
225
233
|
const EVAL_USAGE = "Usage: /eval [--id <sessionId>] [--json]";
|
|
226
234
|
const EVAL_DIFF_USAGE = "Usage: /eval-diff <beforeSessionId> <afterSessionId> [--json]";
|
|
@@ -319,14 +327,14 @@ async function executeEval(ctx, invocation, seam = ctx.sessionQuery) {
|
|
|
319
327
|
if (parsed.json === true) return {
|
|
320
328
|
kind: "success",
|
|
321
329
|
text: JSON.stringify({
|
|
322
|
-
generator: "dsh-session-eval v0.1
|
|
330
|
+
generator: "dsh-session-eval v0.2.1",
|
|
323
331
|
metrics,
|
|
324
332
|
card
|
|
325
333
|
}, null, 2)
|
|
326
334
|
};
|
|
327
335
|
return {
|
|
328
336
|
kind: "success",
|
|
329
|
-
text: renderCard(card, "dsh-session-eval v0.1
|
|
337
|
+
text: renderCard(card, "dsh-session-eval v0.2.1")
|
|
330
338
|
};
|
|
331
339
|
}
|
|
332
340
|
/** Execute /eval-diff against the session-query seam. */
|
|
@@ -350,20 +358,133 @@ async function executeEvalDiff(ctx, invocation, seam = ctx.sessionQuery) {
|
|
|
350
358
|
if (parsed.json === true) return {
|
|
351
359
|
kind: "success",
|
|
352
360
|
text: JSON.stringify({
|
|
353
|
-
generator: "dsh-session-eval v0.1
|
|
361
|
+
generator: "dsh-session-eval v0.2.1",
|
|
362
|
+
report
|
|
363
|
+
}, null, 2)
|
|
364
|
+
};
|
|
365
|
+
return {
|
|
366
|
+
kind: "success",
|
|
367
|
+
text: renderTrend(report, "dsh-session-eval v0.2.1")
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
//#endregion
|
|
371
|
+
//#region src/historyCommand.ts
|
|
372
|
+
const EVAL_HISTORY_USAGE = "Usage: /eval-history [N] [--json] (N = last N sessions, 1-20, default 5)";
|
|
373
|
+
/** Parse /eval-history input; returns args or a usage-error string. */
|
|
374
|
+
function parseEvalHistoryArgs(rawInput) {
|
|
375
|
+
const tokens = rawInput.trim().split(/\s+/).filter((t) => t.length > 0);
|
|
376
|
+
let count = 5;
|
|
377
|
+
let json = false;
|
|
378
|
+
let sawCount = false;
|
|
379
|
+
for (const token of tokens) {
|
|
380
|
+
if (token === "--json") {
|
|
381
|
+
if (json) return `--json may be given only once.\n${EVAL_HISTORY_USAGE}`;
|
|
382
|
+
json = true;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (token.startsWith("--")) return `Unknown argument: ${token}\n${EVAL_HISTORY_USAGE}`;
|
|
386
|
+
if (sawCount) return `Expected at most one count argument.\n${EVAL_HISTORY_USAGE}`;
|
|
387
|
+
if (!/^\d+$/.test(token)) return `N must be a positive integer.\n${EVAL_HISTORY_USAGE}`;
|
|
388
|
+
const value = Number(token);
|
|
389
|
+
if (value < 1 || value > 20) return `N must be between 1 and 20.\n${EVAL_HISTORY_USAGE}`;
|
|
390
|
+
count = value;
|
|
391
|
+
sawCount = true;
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
count,
|
|
395
|
+
json
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function toMillis(createdAt) {
|
|
399
|
+
if (typeof createdAt === "number" && Number.isFinite(createdAt)) return createdAt;
|
|
400
|
+
if (typeof createdAt === "string") {
|
|
401
|
+
const parsed = Date.parse(createdAt);
|
|
402
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
403
|
+
}
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
function stamp(ms) {
|
|
407
|
+
if (ms === null) return "?";
|
|
408
|
+
return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
|
|
409
|
+
}
|
|
410
|
+
/** Render a history report as terminal text. */
|
|
411
|
+
function renderHistory(report) {
|
|
412
|
+
const lines = [];
|
|
413
|
+
if (report.entries.length === 0) return "No sessions found in this workspace; nothing to grade.";
|
|
414
|
+
lines.push(`Session history — last ${report.entries.length} in this workspace`);
|
|
415
|
+
for (const entry of report.entries) {
|
|
416
|
+
const m = entry.metrics;
|
|
417
|
+
lines.push(` ${stamp(entry.createdAt)} ${id8(entry.sessionId)} ${String(entry.card.overall).padEnd(2)} ${m.userTurns} turns, ${m.toolCalls} tools`);
|
|
418
|
+
}
|
|
419
|
+
const trend = report.trend;
|
|
420
|
+
if (trend !== null) lines.push(`Trend (first → last): ${trend.overall} — ${trend.summary}`);
|
|
421
|
+
return lines.join("\n");
|
|
422
|
+
}
|
|
423
|
+
/** Execute /eval-history against the session-query seam. */
|
|
424
|
+
async function executeEvalHistory(ctx, invocation, seam = ctx.sessionQuery) {
|
|
425
|
+
const parsed = parseEvalHistoryArgs(invocation.rawInput);
|
|
426
|
+
if (typeof parsed === "string") return {
|
|
427
|
+
kind: "error",
|
|
428
|
+
text: parsed
|
|
429
|
+
};
|
|
430
|
+
const ownCwd = invocation.agent.session.header?.cwd;
|
|
431
|
+
let records;
|
|
432
|
+
try {
|
|
433
|
+
records = await seam.listSessions();
|
|
434
|
+
} catch (error) {
|
|
435
|
+
return {
|
|
436
|
+
kind: "error",
|
|
437
|
+
text: `Could not list sessions: ${error instanceof Error ? error.message : String(error)}`
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
const recent = records.map((record) => record.header).filter((header) => header != null).filter((header) => ownCwd === void 0 || header.cwd === ownCwd).map((header) => ({
|
|
441
|
+
id: String(header.id),
|
|
442
|
+
createdAt: toMillis(header.createdAt)
|
|
443
|
+
})).sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0)).slice(-parsed.count);
|
|
444
|
+
if (recent.length === 0) return {
|
|
445
|
+
kind: "success",
|
|
446
|
+
text: "No sessions found in this workspace; nothing to grade."
|
|
447
|
+
};
|
|
448
|
+
const entries = [];
|
|
449
|
+
for (const header of recent) {
|
|
450
|
+
let events;
|
|
451
|
+
try {
|
|
452
|
+
events = (await seam.readSession(SessionId(header.id))).events;
|
|
453
|
+
} catch (error) {
|
|
454
|
+
return {
|
|
455
|
+
kind: "error",
|
|
456
|
+
text: `Could not read session ${id8(header.id)}: ${error instanceof Error ? error.message : String(error)}`
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
const metrics = computeMetrics(header.id, adaptEvents(events));
|
|
460
|
+
entries.push({
|
|
461
|
+
sessionId: header.id,
|
|
462
|
+
createdAt: header.createdAt,
|
|
463
|
+
metrics,
|
|
464
|
+
card: gradeSession(metrics)
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
const report = {
|
|
468
|
+
entries,
|
|
469
|
+
trend: entries.length >= 2 ? compareSessions(entries[0].metrics, entries[entries.length - 1].metrics) : null
|
|
470
|
+
};
|
|
471
|
+
if (parsed.json === true) return {
|
|
472
|
+
kind: "success",
|
|
473
|
+
text: JSON.stringify({
|
|
474
|
+
generator: `dsh-session-eval v${VERSION}`,
|
|
354
475
|
report
|
|
355
476
|
}, null, 2)
|
|
356
477
|
};
|
|
357
478
|
return {
|
|
358
479
|
kind: "success",
|
|
359
|
-
text:
|
|
480
|
+
text: renderHistory(report)
|
|
360
481
|
};
|
|
361
482
|
}
|
|
362
483
|
//#endregion
|
|
363
484
|
//#region src/index.ts
|
|
364
485
|
const name = "session-eval";
|
|
365
486
|
const inject = ["commands", "sessionQuery"];
|
|
366
|
-
/** Plugin entry: mount the /eval and /eval-
|
|
487
|
+
/** Plugin entry: mount the /eval, /eval-diff and /eval-history commands. */
|
|
367
488
|
function apply(ctx) {
|
|
368
489
|
ctx.effect(function* () {
|
|
369
490
|
yield ctx.commands.register({
|
|
@@ -376,7 +497,12 @@ function apply(ctx) {
|
|
|
376
497
|
description: "Compare two sessions (before, after) and report which dimensions improved or regressed",
|
|
377
498
|
handler: (invocation) => executeEvalDiff(ctx, invocation)
|
|
378
499
|
});
|
|
500
|
+
yield ctx.commands.register({
|
|
501
|
+
name: "eval-history",
|
|
502
|
+
description: "Grade the last N sessions in this workspace (default 5) and show whether the trend is improving",
|
|
503
|
+
handler: (invocation) => executeEvalHistory(ctx, invocation)
|
|
504
|
+
});
|
|
379
505
|
}, "session-eval lifecycle");
|
|
380
506
|
}
|
|
381
507
|
//#endregion
|
|
382
|
-
export { EVAL_DIFF_USAGE, EVAL_USAGE, adaptEvents, apply, compareSessions, computeMetrics, executeEval, executeEvalDiff, gradeSession, id8, inject, name, parseEvalArgs, parseEvalDiffArgs, renderCard, renderTrend };
|
|
508
|
+
export { EVAL_DIFF_USAGE, EVAL_HISTORY_USAGE, EVAL_USAGE, VERSION, adaptEvents, apply, compareSessions, computeMetrics, executeEval, executeEvalDiff, executeEvalHistory, gradeSession, id8, inject, name, parseEvalArgs, parseEvalDiffArgs, parseEvalHistoryArgs, renderCard, renderHistory, renderTrend };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-session-eval",
|
|
3
3
|
"description": "Retrospective agent-session evaluation for DeepSeek Harness: deterministic grade cards from persisted session logs, cross-session regression diffs, era comparison — no benchmark runs, no LLM judges.",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -38,21 +38,21 @@
|
|
|
38
38
|
"regression"
|
|
39
39
|
],
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@deepseek-ai/cordis": "
|
|
42
|
-
"@deepseek-ai/dsh-commands": "^0.1.
|
|
43
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
44
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
45
|
-
"@deepseek-ai/dsh-session-query": "^0.1.
|
|
46
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
41
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
42
|
+
"@deepseek-ai/dsh-commands": "^0.1.5-rc.3",
|
|
43
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-rc.3",
|
|
44
|
+
"@deepseek-ai/dsh-session": "^0.1.5-rc.3",
|
|
45
|
+
"@deepseek-ai/dsh-session-query": "^0.1.5-rc.3",
|
|
46
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-rc.3"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/node": "^26.2.0",
|
|
50
50
|
"tsdown": "^0.22.14",
|
|
51
51
|
"typescript": "^7.0.2",
|
|
52
52
|
"vitest": "^4.1.11",
|
|
53
|
-
"@deepseek-ai/cordis": "
|
|
54
|
-
"@deepseek-ai/dsh-commands": "^0.1.
|
|
55
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
53
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
54
|
+
"@deepseek-ai/dsh-commands": "^0.1.5-rc.3",
|
|
55
|
+
"@deepseek-ai/dsh-session": "^0.1.5-rc.3"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"bundle": "tsdown",
|