dsh-llm-verifier 0.1.2 → 0.1.4

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/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
- import { a as TopLogprobCapabilityCache, i as emptyUsage, n as addUsage, r as callVerifier, t as RequestLimiter } from "./caller-CGlgZ-Su.js";
1
+ import { a as TopLogprobCapabilityCache, i as emptyUsage, n as addUsage, r as callVerifier, t as RequestLimiter } from "./caller-BXrUTAoL.js";
2
2
  import { DEFAULT_CRITERIA, DEFAULT_GROUND_TRUTH_NOTE, GRANULARITY, LETTERS, SCALE_DESCRIPTION, accumulatePairs, bradleyTerry, buildPairwisePrompt, buildProgressPrompt, extractProgressScore, extractScore, normalizeScoreLetter, pivotRoundPairs, rankScores, ringCycle, seededRandom, topPivots } from "./core.js";
3
3
  import { defineTool } from "@deepseek-ai/dsh-tools";
4
+ import { createUserMessage } from "@deepseek-ai/dsh-llm";
4
5
  import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
5
6
  import z from "schemastery";
6
7
  import { createHash, randomUUID } from "node:crypto";
@@ -10,6 +11,17 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
10
11
  const VERIFIER_SETTINGS_NAMESPACE = settingsNamespace("llm-verifier");
11
12
  const Config = z.object({
12
13
  enabled: z.boolean().default(true),
14
+ autoVerifyMode: z.union([
15
+ "manual",
16
+ "smart",
17
+ "strict"
18
+ ]).default("smart"),
19
+ autoVerifyThreshold: z.number().min(0).max(1).default(.65),
20
+ autoVerifyRepeats: z.number().step(1).min(1).default(1),
21
+ autoVerifyMinToolCalls: z.number().step(1).min(1).default(3),
22
+ autoVerifyMaxChars: z.number().step(1).min(1e3).default(8e4),
23
+ autoVerifyMaxPerTask: z.number().step(1).min(1).default(2),
24
+ autoVerifyMaxPerSession: z.number().step(1).min(1).default(8),
13
25
  provider: z.string().default("deepseek-official"),
14
26
  model: z.string().default("deepseek-v4-flash"),
15
27
  reasoningEffort: z.string(),
@@ -28,7 +40,20 @@ function resolveConfig(config = {}) {
28
40
  const model = (config.model ?? "deepseek-v4-flash").trim();
29
41
  if (!provider) throw new Error("llm-verifier: provider must be non-empty");
30
42
  if (!model) throw new Error("llm-verifier: model must be non-empty");
43
+ const autoVerifyMode = config.autoVerifyMode ?? "smart";
44
+ if (![
45
+ "manual",
46
+ "smart",
47
+ "strict"
48
+ ].includes(autoVerifyMode)) throw new Error("llm-verifier: autoVerifyMode must be manual, smart, or strict");
49
+ const autoVerifyThreshold = config.autoVerifyThreshold ?? .65;
50
+ if (!Number.isFinite(autoVerifyThreshold) || autoVerifyThreshold < 0 || autoVerifyThreshold > 1) throw new Error("llm-verifier: autoVerifyThreshold must be between 0 and 1");
31
51
  const values = {
52
+ autoVerifyRepeats: config.autoVerifyRepeats ?? 1,
53
+ autoVerifyMinToolCalls: config.autoVerifyMinToolCalls ?? 3,
54
+ autoVerifyMaxChars: config.autoVerifyMaxChars ?? 8e4,
55
+ autoVerifyMaxPerTask: config.autoVerifyMaxPerTask ?? 2,
56
+ autoVerifyMaxPerSession: config.autoVerifyMaxPerSession ?? 8,
32
57
  maxTokens: config.maxTokens ?? 32768,
33
58
  timeoutMs: config.timeoutMs ?? 3e5,
34
59
  maxConcurrency: config.maxConcurrency ?? 8,
@@ -36,6 +61,7 @@ function resolveConfig(config = {}) {
36
61
  cacheMaxEntries: config.cacheMaxEntries ?? 1e4
37
62
  };
38
63
  for (const [name, value] of Object.entries(values)) if (!Number.isSafeInteger(value) || value <= 0) throw new Error("llm-verifier: " + name + " must be a positive safe integer");
64
+ if (values.autoVerifyMaxChars < 1e3) throw new Error("llm-verifier: autoVerifyMaxChars must be at least 1000");
39
65
  const maxRetries = config.maxRetries ?? 3;
40
66
  if (!Number.isSafeInteger(maxRetries) || maxRetries < 0) throw new Error("llm-verifier: maxRetries must be a non-negative safe integer");
41
67
  const cacheDir = (config.cacheDir ?? ".dsh-verifier-cache").trim();
@@ -46,6 +72,8 @@ function resolveConfig(config = {}) {
46
72
  const reasoningEffort = config.reasoningEffort?.trim();
47
73
  return {
48
74
  enabled: config.enabled ?? true,
75
+ autoVerifyMode,
76
+ autoVerifyThreshold,
49
77
  provider,
50
78
  model,
51
79
  ...reasoningEffort ? { reasoningEffort } : {},
@@ -514,6 +542,167 @@ async function extractSession(agent, loadImage, options = {}) {
514
542
  };
515
543
  }
516
544
  //#endregion
545
+ //#region src/auto.ts
546
+ const PASSIVE_TOOLS = /* @__PURE__ */ new Set([
547
+ "read",
548
+ "read_image",
549
+ "glob",
550
+ "grep",
551
+ "web_search",
552
+ "ssh_list",
553
+ "job_list",
554
+ "job_output",
555
+ "list_agents",
556
+ "get_goal",
557
+ "skill",
558
+ "mcp__codegraph__codegraph_explore"
559
+ ]);
560
+ const VERIFIER_TOOLS = /* @__PURE__ */ new Set([
561
+ "verifier_compare",
562
+ "verifier_select",
563
+ "verifier_track",
564
+ "verifier_current_session"
565
+ ]);
566
+ const CONSEQUENTIAL_TOOLS = /* @__PURE__ */ new Set([
567
+ "edit",
568
+ "write",
569
+ "pwsh",
570
+ "bash",
571
+ "run_code",
572
+ "codex_image_generate",
573
+ "ssh_exec",
574
+ "ssh_upload",
575
+ "ssh_download",
576
+ "ssh_tunnel",
577
+ "ssh_cluster",
578
+ "job_kill",
579
+ "workbench_session_delete",
580
+ "create_goal",
581
+ "update_goal"
582
+ ]);
583
+ function latestDirectUserSeq(events) {
584
+ for (let index = events.length - 1; index >= 0; index -= 1) {
585
+ const event = events[index];
586
+ if (event?.type === "user/message" && event.data.source.kind === "user") return event.seq;
587
+ }
588
+ }
589
+ function isConsequential(name) {
590
+ if (CONSEQUENTIAL_TOOLS.has(name)) return true;
591
+ if (PASSIVE_TOOLS.has(name) || VERIFIER_TOOLS.has(name)) return false;
592
+ return /(?:edit|write|patch|apply|deploy|upload|delete|remove|kill|exec|shell|command|migration|database|tunnel|cluster)/iu.test(name);
593
+ }
594
+ function analyzeAutoTask(events, policy) {
595
+ const taskStartSeq = latestDirectUserSeq(events);
596
+ if (taskStartSeq === void 0) return {
597
+ taskStartSeq: 0,
598
+ toolCalls: 0,
599
+ completedToolResults: 0,
600
+ consequentialToolCalls: 0,
601
+ hasManualSessionVerification: false,
602
+ eligible: false,
603
+ reason: "no-direct-user-task"
604
+ };
605
+ const relevant = events.filter((event) => event.seq >= taskStartSeq);
606
+ const calls = relevant.filter((event) => event.type === "tool/call");
607
+ const toolCalls = calls.filter((event) => !VERIFIER_TOOLS.has(event.data.name)).length;
608
+ const completedToolResults = relevant.filter((event) => event.type === "tool/result" && event.data.error === void 0 && event.data.message.content.every((block) => block.isError !== true)).length;
609
+ const consequentialToolCalls = calls.filter((event) => isConsequential(event.data.name)).length;
610
+ const hasManualSessionVerification = calls.some((event) => event.data.name === "verifier_current_session");
611
+ if (policy.mode === "manual") return {
612
+ taskStartSeq,
613
+ toolCalls,
614
+ completedToolResults,
615
+ consequentialToolCalls,
616
+ hasManualSessionVerification,
617
+ eligible: false,
618
+ reason: "manual-mode"
619
+ };
620
+ if (hasManualSessionVerification) return {
621
+ taskStartSeq,
622
+ toolCalls,
623
+ completedToolResults,
624
+ consequentialToolCalls,
625
+ hasManualSessionVerification,
626
+ eligible: false,
627
+ reason: "already-verified"
628
+ };
629
+ if (consequentialToolCalls === 0) return {
630
+ taskStartSeq,
631
+ toolCalls,
632
+ completedToolResults,
633
+ consequentialToolCalls,
634
+ hasManualSessionVerification,
635
+ eligible: false,
636
+ reason: "no-consequential-work"
637
+ };
638
+ if (completedToolResults === 0) return {
639
+ taskStartSeq,
640
+ toolCalls,
641
+ completedToolResults,
642
+ consequentialToolCalls,
643
+ hasManualSessionVerification,
644
+ eligible: false,
645
+ reason: "no-completed-evidence"
646
+ };
647
+ if (policy.mode === "smart" && toolCalls < policy.minToolCalls) return {
648
+ taskStartSeq,
649
+ toolCalls,
650
+ completedToolResults,
651
+ consequentialToolCalls,
652
+ hasManualSessionVerification,
653
+ eligible: false,
654
+ reason: "insufficient-tool-evidence"
655
+ };
656
+ return {
657
+ taskStartSeq,
658
+ toolCalls,
659
+ completedToolResults,
660
+ consequentialToolCalls,
661
+ hasManualSessionVerification,
662
+ eligible: true,
663
+ reason: policy.mode + "-eligible"
664
+ };
665
+ }
666
+ var AutoVerificationBudget = class {
667
+ states = /* @__PURE__ */ new Map();
668
+ claim(agent, evidence, policy) {
669
+ if (!evidence.eligible) return false;
670
+ const id = String(agent.id);
671
+ const state = this.states.get(id) ?? {
672
+ taskStartSeq: evidence.taskStartSeq,
673
+ taskAttempts: 0,
674
+ sessionAttempts: 0,
675
+ lastEvaluatedSeq: -1
676
+ };
677
+ if (state.taskStartSeq !== evidence.taskStartSeq) {
678
+ state.taskStartSeq = evidence.taskStartSeq;
679
+ state.taskAttempts = 0;
680
+ state.lastEvaluatedSeq = -1;
681
+ }
682
+ const lastSeq = agent.session.events.at(-1)?.seq ?? -1;
683
+ if (lastSeq <= state.lastEvaluatedSeq || state.taskAttempts >= policy.maxPerTask || state.sessionAttempts >= policy.maxPerSession) {
684
+ this.states.set(id, state);
685
+ return false;
686
+ }
687
+ state.taskAttempts += 1;
688
+ state.sessionAttempts += 1;
689
+ state.lastEvaluatedSeq = lastSeq;
690
+ this.states.set(id, state);
691
+ return true;
692
+ }
693
+ release(agent) {
694
+ this.states.delete(String(agent.id));
695
+ }
696
+ };
697
+ function automaticFeedback(score, baselineScore, winner, threshold) {
698
+ const percent = (value) => (value * 100).toFixed(1) + "%";
699
+ return [
700
+ "[Automatic verifier gate]",
701
+ `The independent verifier did not clear this task for completion: evidence score ${percent(score)}, baseline ${percent(baselineScore)}, verdict ${winner}, required ${percent(threshold)}.`,
702
+ "Re-open the task requirements, inspect the actual tool outputs for unresolved errors or missing proof, make any necessary corrections, and run a directly relevant verification command before concluding. Do not merely restate that the task is complete."
703
+ ].join("\n");
704
+ }
705
+ //#endregion
517
706
  //#region src/statistics.ts
518
707
  const VERIFIER_TOOL_NAMES = [
519
708
  "verifier_compare",
@@ -927,6 +1116,7 @@ function rpcFailure(message) {
927
1116
  };
928
1117
  }
929
1118
  function apply(ctx, config = {}) {
1119
+ const services = ctx;
930
1120
  const entry = resolveConfig(config);
931
1121
  let limiter = new RequestLimiter(entry.maxConcurrency);
932
1122
  const current = installVerifierSettings(ctx, entry, () => {
@@ -936,6 +1126,7 @@ function apply(ctx, config = {}) {
936
1126
  const cache = new ScoreCache(cacheFile, entry.cacheMaxEntries);
937
1127
  const statistics = new StatisticsStore(resolveStatisticsFile(cacheFile));
938
1128
  const topLogprobCapabilities = new TopLogprobCapabilityCache();
1129
+ const autoBudget = new AutoVerificationBudget();
939
1130
  const engine = async () => {
940
1131
  const selected = current();
941
1132
  await ctx.llm.resolveCallConfig({
@@ -949,7 +1140,7 @@ function apply(ctx, config = {}) {
949
1140
  ...selected,
950
1141
  ctx,
951
1142
  llm: ctx.llm,
952
- attachments: ctx.attachments,
1143
+ attachments: services.attachments,
953
1144
  topLogprobCapabilities,
954
1145
  limiter
955
1146
  }, selected.maxConcurrency, cache, {
@@ -967,7 +1158,7 @@ function apply(ctx, config = {}) {
967
1158
  const requireEnabled = () => {
968
1159
  if (!current().enabled) throw new Error("llm-verifier: verifier tools are disabled — enable them in Settings → LLM Verifier");
969
1160
  };
970
- const record = async (toolName, exec, operation) => {
1161
+ const record = async (toolName, agent, operation) => {
971
1162
  const startedAt = Date.now();
972
1163
  let selected = current();
973
1164
  try {
@@ -979,7 +1170,7 @@ function apply(ctx, config = {}) {
979
1170
  };
980
1171
  await statistics.record({
981
1172
  toolName,
982
- ...exec.agent ? { sessionId: String(exec.agent.id) } : {},
1173
+ ...agent ? { sessionId: String(agent.id) } : {},
983
1174
  startedAt,
984
1175
  success: true,
985
1176
  provider: selected.provider,
@@ -991,7 +1182,7 @@ function apply(ctx, config = {}) {
991
1182
  const details = errorDetails(error);
992
1183
  await statistics.record({
993
1184
  toolName,
994
- ...exec.agent ? { sessionId: String(exec.agent.id) } : {},
1185
+ ...agent ? { sessionId: String(agent.id) } : {},
995
1186
  startedAt,
996
1187
  success: false,
997
1188
  ...details,
@@ -1002,7 +1193,45 @@ function apply(ctx, config = {}) {
1002
1193
  throw error;
1003
1194
  }
1004
1195
  };
1005
- ctx.effect(() => ctx.connection.rpc.handle("/llm-verifier", async (endpoint, payload) => {
1196
+ const verifySession = async (agent, options, signal) => record("verifier_current_session", agent, async () => {
1197
+ const extracted = await extractSession(agent, async (ref) => {
1198
+ const stored = await services.attachments.readImage(ref, signal);
1199
+ return {
1200
+ data: stored.data,
1201
+ mediaType: stored.ref.mediaType
1202
+ };
1203
+ }, {
1204
+ fromSeq: options.fromSeq,
1205
+ toSeq: options.toSeq,
1206
+ includeAssistantText: options.includeAssistantText,
1207
+ redactPatterns: options.redactPatterns,
1208
+ maxChars: options.maxChars
1209
+ });
1210
+ const { verifier, selected } = await engine();
1211
+ const compared = await verifier.compare({
1212
+ problem: extracted.problem,
1213
+ candidateA: extracted.trace,
1214
+ candidateB: "(No useful work or verification was performed.)",
1215
+ repeats: positive(options.repeats, 2, "repeats"),
1216
+ images: extracted.images
1217
+ }, signal);
1218
+ return {
1219
+ result: {
1220
+ sessionId: extracted.sessionId,
1221
+ problem: extracted.problem,
1222
+ score: compared.scoreA,
1223
+ baselineScore: compared.scoreB,
1224
+ winner: compared.winner,
1225
+ fromSeq: extracted.fromSeq,
1226
+ toSeq: extracted.toSeq,
1227
+ omittedCharacters: extracted.omittedCharacters,
1228
+ calls: compared.calls,
1229
+ stats: compared.stats
1230
+ },
1231
+ selected
1232
+ };
1233
+ });
1234
+ ctx.effect(() => services.connection.rpc.handle("/llm-verifier", async (endpoint, payload) => {
1006
1235
  if (endpoint !== "statistics") return rpcFailure("unknown llm-verifier endpoint");
1007
1236
  if (typeof payload !== "object" || payload === null) return rpcFailure("statistics payload must be an object");
1008
1237
  const row = payload;
@@ -1019,6 +1248,55 @@ function apply(ctx, config = {}) {
1019
1248
  return rpcFailure(error instanceof Error ? error.message : String(error));
1020
1249
  }
1021
1250
  }, { authority: "loopback" }), "llm-verifier: statistics rpc");
1251
+ ctx.on("agent/disposed", ({ agent }) => {
1252
+ autoBudget.release(agent);
1253
+ });
1254
+ ctx.on("agent/turn-stopping", async ({ agent, signal }) => {
1255
+ const selected = current();
1256
+ if (!selected.enabled || selected.autoVerifyMode === "manual" || signal.aborted) return;
1257
+ const evidence = analyzeAutoTask(agent.session.events, {
1258
+ mode: selected.autoVerifyMode,
1259
+ minToolCalls: selected.autoVerifyMinToolCalls,
1260
+ maxPerTask: selected.autoVerifyMaxPerTask,
1261
+ maxPerSession: selected.autoVerifyMaxPerSession
1262
+ });
1263
+ if (!autoBudget.claim(agent, evidence, {
1264
+ mode: selected.autoVerifyMode,
1265
+ minToolCalls: selected.autoVerifyMinToolCalls,
1266
+ maxPerTask: selected.autoVerifyMaxPerTask,
1267
+ maxPerSession: selected.autoVerifyMaxPerSession
1268
+ })) return;
1269
+ try {
1270
+ const result = await verifySession(agent, {
1271
+ fromSeq: evidence.taskStartSeq,
1272
+ includeAssistantText: true,
1273
+ maxChars: selected.autoVerifyMaxChars,
1274
+ repeats: selected.autoVerifyRepeats
1275
+ }, signal);
1276
+ if (!(result.winner === "A" && result.score >= selected.autoVerifyThreshold)) agent.steer(createUserMessage({
1277
+ content: [{
1278
+ type: "text",
1279
+ text: automaticFeedback(result.score, result.baselineScore, result.winner, selected.autoVerifyThreshold)
1280
+ }],
1281
+ source: {
1282
+ kind: "plugin",
1283
+ plugin: "dsh-llm-verifier"
1284
+ }
1285
+ }));
1286
+ } catch (error) {
1287
+ ctx.logger.warn("llm-verifier automatic session verification failed: " + (error instanceof Error ? error.message : String(error)));
1288
+ if (selected.autoVerifyMode === "strict" && !signal.aborted) agent.steer(createUserMessage({
1289
+ content: [{
1290
+ type: "text",
1291
+ text: "[Automatic verifier gate]\nThe required strict verification could not run: " + (error instanceof Error ? error.message : String(error)) + "\nInspect the task evidence and run a directly relevant verification command before concluding."
1292
+ }],
1293
+ source: {
1294
+ kind: "plugin",
1295
+ plugin: "dsh-llm-verifier"
1296
+ }
1297
+ }));
1298
+ }
1299
+ });
1022
1300
  ctx.tools.register(defineTool({
1023
1301
  name: "verifier_compare",
1024
1302
  description: "Use autonomously when exactly two substantive answers, patches, plans, or execution trajectories need an independent evidence-based comparison and the choice is consequential or uncertain. Do not use for trivial deterministic questions or when there is only one candidate. Uses the verifier model selected in DSH Settings, with top-logprob A–T expectations when supported and explicit-tag fallback otherwise.",
@@ -1087,7 +1365,7 @@ function apply(ctx, config = {}) {
1087
1365
  timeoutMs: entry.timeoutMs * 20,
1088
1366
  async execute(args, exec) {
1089
1367
  requireEnabled();
1090
- return record("verifier_compare", exec, async () => {
1368
+ return record("verifier_compare", exec.agent, async () => {
1091
1369
  const { verifier, selected } = await engine();
1092
1370
  return {
1093
1371
  result: await verifier.compare({
@@ -1105,7 +1383,7 @@ function apply(ctx, config = {}) {
1105
1383
  }));
1106
1384
  ctx.tools.register(defineTool({
1107
1385
  name: "verifier_select",
1108
- description: "Use autonomously when three or more substantive candidate answers, patches, plans, or trajectories must be ranked and an independent choice is valuable. Use verifier_compare for exactly two candidates; do not generate extra candidates merely to invoke this tool. Uses the configured DSH verifier model and the O(Nk) Probabilistic Pivot Tournament.",
1386
+ description: "Use autonomously when three or more substantive candidate answers, patches, plans, or trajectories must be ranked and an independent choice is valuable. Use verifier_compare for exactly two candidates; do not generate extra candidates merely to invoke this tool. Deterministic orchestrators should call this directly once they have three or more real candidates.",
1109
1387
  parameters: {
1110
1388
  problem: {
1111
1389
  type: "string",
@@ -1175,7 +1453,7 @@ function apply(ctx, config = {}) {
1175
1453
  timeoutMs: entry.timeoutMs * 100,
1176
1454
  async execute(args, exec) {
1177
1455
  requireEnabled();
1178
- return record("verifier_select", exec, async () => {
1456
+ return record("verifier_select", exec.agent, async () => {
1179
1457
  const { verifier, selected } = await engine();
1180
1458
  return {
1181
1459
  result: await verifier.select({
@@ -1194,7 +1472,7 @@ function apply(ctx, config = {}) {
1194
1472
  }));
1195
1473
  ctx.tools.register(defineTool({
1196
1474
  name: "verifier_track",
1197
- description: "Use autonomously for a genuinely multi-step task when progress at explicit checkpoints is uncertain or needs evidence-based measurement. Do not use for a single completed answer or invent checkpoints that were not supplied by the task history. Trusts observed output rather than narration.",
1475
+ description: "Use autonomously for a genuinely multi-step task when progress at explicit checkpoints is uncertain or needs evidence-based measurement. Deterministic goal/workflow orchestrators should call this directly when real checkpoints already exist. Do not use for a single completed answer or invent checkpoints.",
1198
1476
  parameters: {
1199
1477
  problem: {
1200
1478
  type: "string",
@@ -1254,7 +1532,7 @@ function apply(ctx, config = {}) {
1254
1532
  timeoutMs: entry.timeoutMs * 20,
1255
1533
  async execute(args, exec) {
1256
1534
  requireEnabled();
1257
- return record("verifier_track", exec, async () => {
1535
+ return record("verifier_track", exec.agent, async () => {
1258
1536
  const { verifier, selected } = await engine();
1259
1537
  return {
1260
1538
  result: await verifier.track(args.problem, args.steps, args.checkpoints, positive(args.repeats, 2, "repeats"), exec.signal, await images(args.images, exec.signal)),
@@ -1265,7 +1543,7 @@ function apply(ctx, config = {}) {
1265
1543
  }));
1266
1544
  ctx.tools.register(defineTool({
1267
1545
  name: "verifier_current_session",
1268
- description: "Use autonomously near the end of a non-trivial coding, debugging, migration, deployment, or operations task when independent completion verification would materially reduce risk and the session contains real tool evidence. Do not use for routine conversation, simple factual answers, or every turn. Extracts the current DSH session, applies secret redaction, bounds and truncation, then sends the evidence to the verifier model selected in Settings.",
1546
+ description: "Explicitly verify the current DSH session. Smart/strict policy can also invoke this gate automatically at the turn-stopping lifecycle boundary after consequential work with real tool evidence. Extracts the session, applies redaction and bounds, then sends the evidence to the configured verifier model.",
1269
1547
  parameters: {
1270
1548
  from_seq: { type: "integer" },
1271
1549
  to_seq: { type: "integer" },
@@ -1342,50 +1620,20 @@ function apply(ctx, config = {}) {
1342
1620
  timeoutMs: entry.timeoutMs * 20,
1343
1621
  async execute(args, exec) {
1344
1622
  requireEnabled();
1345
- return record("verifier_current_session", exec, async () => {
1346
- const agent = exec.agent ?? ctx.agents.currentInitiator();
1347
- if (agent === void 0) throw new Error("llm-verifier: verifier_current_session requires an agent-owned tool call");
1348
- const extracted = await extractSession(agent, async (ref) => {
1349
- const stored = await ctx.attachments.readImage(ref, exec.signal);
1350
- return {
1351
- data: stored.data,
1352
- mediaType: stored.ref.mediaType
1353
- };
1354
- }, {
1355
- fromSeq: args.from_seq,
1356
- toSeq: args.to_seq,
1357
- includeAssistantText: args.include_assistant_text,
1358
- redactPatterns: args.redact_patterns,
1359
- maxChars: args.max_chars
1360
- });
1361
- const { verifier, selected } = await engine();
1362
- const compared = await verifier.compare({
1363
- problem: extracted.problem,
1364
- candidateA: extracted.trace,
1365
- candidateB: "(No useful work or verification was performed.)",
1366
- repeats: positive(args.repeats, 2, "repeats"),
1367
- images: extracted.images
1368
- }, exec.signal);
1369
- return {
1370
- result: {
1371
- sessionId: extracted.sessionId,
1372
- problem: extracted.problem,
1373
- score: compared.scoreA,
1374
- baselineScore: compared.scoreB,
1375
- winner: compared.winner,
1376
- fromSeq: extracted.fromSeq,
1377
- toSeq: extracted.toSeq,
1378
- omittedCharacters: extracted.omittedCharacters,
1379
- calls: compared.calls,
1380
- stats: compared.stats
1381
- },
1382
- selected
1383
- };
1384
- });
1623
+ const agent = exec.agent ?? ctx.agents.currentInitiator();
1624
+ if (agent === void 0) throw new Error("llm-verifier: verifier_current_session requires an agent-owned tool call");
1625
+ return verifySession(agent, {
1626
+ fromSeq: args.from_seq,
1627
+ toSeq: args.to_seq,
1628
+ includeAssistantText: args.include_assistant_text,
1629
+ redactPatterns: args.redact_patterns,
1630
+ maxChars: args.max_chars,
1631
+ repeats: args.repeats
1632
+ }, exec.signal);
1385
1633
  }
1386
1634
  }));
1387
1635
  }
1388
1636
  //#endregion
1389
- export { Config, DEFAULT_CRITERIA, DEFAULT_GROUND_TRUTH_NOTE, GRANULARITY, LETTERS, RequestLimiter, SCALE_DESCRIPTION, ScoreCache, StatisticsStore, VERIFIER_TOOL_NAMES, VerifierEngine, accumulatePairs, apply, bradleyTerry, buildPairwisePrompt, buildProgressPrompt, callVerifier, emptyRunStats, errorDetails, extractProgressScore, extractScore, inject, name, normalizeCriteria, normalizeScoreLetter, pivotRoundPairs, rankScores, resolveCacheFile, resolveStatisticsFile, ringCycle, seededRandom, stableHash, topPivots };
1637
+ export { AutoVerificationBudget, Config, DEFAULT_CRITERIA, DEFAULT_GROUND_TRUTH_NOTE, GRANULARITY, LETTERS, RequestLimiter, SCALE_DESCRIPTION, ScoreCache, StatisticsStore, VERIFIER_TOOL_NAMES, VerifierEngine, accumulatePairs, analyzeAutoTask, apply, automaticFeedback, bradleyTerry, buildPairwisePrompt, buildProgressPrompt, callVerifier, emptyRunStats, errorDetails, extractProgressScore, extractScore, inject, name, normalizeCriteria, normalizeScoreLetter, pivotRoundPairs, rankScores, resolveCacheFile, resolveStatisticsFile, ringCycle, seededRandom, stableHash, topPivots };
1390
1638
 
1391
1639
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,26 @@
1
+ import type { Agent } from '@deepseek-ai/dsh-agent';
2
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
3
+ export type AutoVerifyMode = 'manual' | 'smart' | 'strict';
4
+ export interface AutoVerifyPolicy {
5
+ mode: AutoVerifyMode;
6
+ minToolCalls: number;
7
+ maxPerTask: number;
8
+ maxPerSession: number;
9
+ }
10
+ export interface AutoTaskEvidence {
11
+ taskStartSeq: number;
12
+ toolCalls: number;
13
+ completedToolResults: number;
14
+ consequentialToolCalls: number;
15
+ hasManualSessionVerification: boolean;
16
+ eligible: boolean;
17
+ reason: string;
18
+ }
19
+ export declare function analyzeAutoTask(events: readonly SessionEvent[], policy: AutoVerifyPolicy): AutoTaskEvidence;
20
+ export declare class AutoVerificationBudget {
21
+ private readonly states;
22
+ claim(agent: Agent, evidence: AutoTaskEvidence, policy: AutoVerifyPolicy): boolean;
23
+ release(agent: Agent): void;
24
+ }
25
+ export declare function automaticFeedback(score: number, baselineScore: number, winner: 'A' | 'B' | 'tie', threshold: number): string;
26
+ //# sourceMappingURL=auto.d.ts.map
@@ -0,0 +1,86 @@
1
+ const PASSIVE_TOOLS = new Set([
2
+ 'read', 'read_image', 'glob', 'grep', 'web_search', 'ssh_list', 'job_list',
3
+ 'job_output', 'list_agents', 'get_goal', 'skill', 'mcp__codegraph__codegraph_explore',
4
+ ]);
5
+ const VERIFIER_TOOLS = new Set([
6
+ 'verifier_compare', 'verifier_select', 'verifier_track', 'verifier_current_session',
7
+ ]);
8
+ const CONSEQUENTIAL_TOOLS = new Set([
9
+ 'edit', 'write', 'pwsh', 'bash', 'run_code', 'codex_image_generate',
10
+ 'ssh_exec', 'ssh_upload', 'ssh_download', 'ssh_tunnel', 'ssh_cluster',
11
+ 'job_kill', 'workbench_session_delete', 'create_goal', 'update_goal',
12
+ ]);
13
+ function latestDirectUserSeq(events) {
14
+ for (let index = events.length - 1; index >= 0; index -= 1) {
15
+ const event = events[index];
16
+ if (event?.type === 'user/message' && event.data.source.kind === 'user')
17
+ return event.seq;
18
+ }
19
+ return undefined;
20
+ }
21
+ function isConsequential(name) {
22
+ if (CONSEQUENTIAL_TOOLS.has(name))
23
+ return true;
24
+ if (PASSIVE_TOOLS.has(name) || VERIFIER_TOOLS.has(name))
25
+ return false;
26
+ return /(?:edit|write|patch|apply|deploy|upload|delete|remove|kill|exec|shell|command|migration|database|tunnel|cluster)/iu.test(name);
27
+ }
28
+ export function analyzeAutoTask(events, policy) {
29
+ const taskStartSeq = latestDirectUserSeq(events);
30
+ if (taskStartSeq === undefined)
31
+ return { taskStartSeq: 0, toolCalls: 0, completedToolResults: 0, consequentialToolCalls: 0, hasManualSessionVerification: false, eligible: false, reason: 'no-direct-user-task' };
32
+ const relevant = events.filter(event => event.seq >= taskStartSeq);
33
+ const calls = relevant.filter((event) => event.type === 'tool/call');
34
+ const toolCalls = calls.filter(event => !VERIFIER_TOOLS.has(event.data.name)).length;
35
+ const completedToolResults = relevant.filter(event => event.type === 'tool/result' && event.data.error === undefined && event.data.message.content.every(block => block.isError !== true)).length;
36
+ const consequentialToolCalls = calls.filter(event => isConsequential(event.data.name)).length;
37
+ const hasManualSessionVerification = calls.some(event => event.data.name === 'verifier_current_session');
38
+ if (policy.mode === 'manual')
39
+ return { taskStartSeq, toolCalls, completedToolResults, consequentialToolCalls, hasManualSessionVerification, eligible: false, reason: 'manual-mode' };
40
+ if (hasManualSessionVerification)
41
+ return { taskStartSeq, toolCalls, completedToolResults, consequentialToolCalls, hasManualSessionVerification, eligible: false, reason: 'already-verified' };
42
+ if (consequentialToolCalls === 0)
43
+ return { taskStartSeq, toolCalls, completedToolResults, consequentialToolCalls, hasManualSessionVerification, eligible: false, reason: 'no-consequential-work' };
44
+ if (completedToolResults === 0)
45
+ return { taskStartSeq, toolCalls, completedToolResults, consequentialToolCalls, hasManualSessionVerification, eligible: false, reason: 'no-completed-evidence' };
46
+ if (policy.mode === 'smart' && toolCalls < policy.minToolCalls)
47
+ return { taskStartSeq, toolCalls, completedToolResults, consequentialToolCalls, hasManualSessionVerification, eligible: false, reason: 'insufficient-tool-evidence' };
48
+ return { taskStartSeq, toolCalls, completedToolResults, consequentialToolCalls, hasManualSessionVerification, eligible: true, reason: policy.mode + '-eligible' };
49
+ }
50
+ export class AutoVerificationBudget {
51
+ states = new Map();
52
+ claim(agent, evidence, policy) {
53
+ if (!evidence.eligible)
54
+ return false;
55
+ const id = String(agent.id);
56
+ const previous = this.states.get(id);
57
+ const state = previous ?? { taskStartSeq: evidence.taskStartSeq, taskAttempts: 0, sessionAttempts: 0, lastEvaluatedSeq: -1 };
58
+ if (state.taskStartSeq !== evidence.taskStartSeq) {
59
+ state.taskStartSeq = evidence.taskStartSeq;
60
+ state.taskAttempts = 0;
61
+ state.lastEvaluatedSeq = -1;
62
+ }
63
+ const lastSeq = agent.session.events.at(-1)?.seq ?? -1;
64
+ if (lastSeq <= state.lastEvaluatedSeq || state.taskAttempts >= policy.maxPerTask || state.sessionAttempts >= policy.maxPerSession) {
65
+ this.states.set(id, state);
66
+ return false;
67
+ }
68
+ state.taskAttempts += 1;
69
+ state.sessionAttempts += 1;
70
+ state.lastEvaluatedSeq = lastSeq;
71
+ this.states.set(id, state);
72
+ return true;
73
+ }
74
+ release(agent) {
75
+ this.states.delete(String(agent.id));
76
+ }
77
+ }
78
+ export function automaticFeedback(score, baselineScore, winner, threshold) {
79
+ const percent = (value) => (value * 100).toFixed(1) + '%';
80
+ return [
81
+ '[Automatic verifier gate]',
82
+ `The independent verifier did not clear this task for completion: evidence score ${percent(score)}, baseline ${percent(baselineScore)}, verdict ${winner}, required ${percent(threshold)}.`,
83
+ 'Re-open the task requirements, inspect the actual tool outputs for unresolved errors or missing proof, make any necessary corrections, and run a directly relevant verification command before concluding. Do not merely restate that the task is complete.',
84
+ ].join('\n');
85
+ }
86
+ //# sourceMappingURL=auto.js.map