@xdxer/dingtalk-agent 0.1.5-beta.14 → 0.1.5-beta.15

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.
@@ -6,7 +6,7 @@ import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, re
6
6
  import { tmpdir } from 'node:os';
7
7
  import { basename, dirname, join, parse, relative, resolve } from 'node:path';
8
8
  import { resolvePackageRoot } from './package-root.js';
9
- import { bindRequiredBasicSkill, removeRequiredBasicSkillInstruction, verifyRequiredBasicSkill, } from './opencode-workspace.js';
9
+ import { bindRequiredBasicSkill, DEFAULT_OPENCODE_MODEL, removeRequiredBasicSkillInstruction, REQUIRED_BASIC_SKILL_INSTRUCTION, verifyRequiredBasicSkill, } from './opencode-workspace.js';
10
10
  import { gradeAnswer, loadRobotEvalSuite, } from './robot-evals.js';
11
11
  import { evaluateWorkspaceEvidence, isWorkspacePathInside, relativeWorkspacePath, snapshotWorkspaceEvidence, } from './eval-evidence.js';
12
12
  import { bundledSkillPath } from './skill-manager.js';
@@ -47,16 +47,18 @@ export function runOpenCodeEvaluation(root, workspacePath, suitePath, options =
47
47
  ? new Set(options.lanes)
48
48
  : new Set(suite.cases.map((item) => item.lane));
49
49
  const requestedCases = new Set(options.caseIds || []);
50
- const knownBehaviorCases = new Set(suite.cases
51
- .filter((item) => item.kind !== 'load-probe').map((item) => item.id));
52
- const unknownCases = [...requestedCases].filter((id) => !knownBehaviorCases.has(id));
50
+ const knownCases = new Set(suite.cases.map((item) => item.id));
51
+ const selectedLoadCases = suite.cases.filter((item) => item.kind === 'load-probe' && requestedCases.has(item.id));
52
+ const unknownCases = [...requestedCases].filter((id) => !knownCases.has(id));
53
53
  if (unknownCases.length)
54
54
  throw new Error(`OpenCode Eval case 不存在: ${unknownCases.join(',')}`);
55
- const cases = options.loadOnly ? [] : suite.cases.filter((item) => lanes.has(item.lane) && item.kind !== 'load-probe' &&
55
+ const loadOnly = Boolean(options.loadOnly ||
56
+ (requestedCases.size > 0 && selectedLoadCases.length === requestedCases.size));
57
+ const cases = loadOnly ? [] : suite.cases.filter((item) => lanes.has(item.lane) && item.kind !== 'load-probe' &&
56
58
  (!requestedCases.size || requestedCases.has(item.id)));
57
- if (!cases.length && !options.loadOnly)
59
+ if (!cases.length && !loadOnly)
58
60
  throw new Error('OpenCode Eval 选择后没有行为 case');
59
- const model = options.model || 'deepseek/deepseek-chat';
61
+ const model = options.model || DEFAULT_OPENCODE_MODEL;
60
62
  if (!model.startsWith('deepseek/'))
61
63
  throw new Error('OpenCode Eval 模型必须固定为 deepseek/*');
62
64
  const packageRoot = resolvePackageRoot(import.meta.url);
@@ -218,6 +220,14 @@ export function runOpenCodeEvaluation(root, workspacePath, suitePath, options =
218
220
  baselineDidNotGuess: configuration.expectsLoad
219
221
  ? null : probeExecution.answer.trim() === baselineSentinel &&
220
222
  probeExecution.answer.trim() !== exactProbe,
223
+ // baseline 失败其实有两种,混成一个布尔会把它们说成同一件事(issue #67 §7):
224
+ // 「没猜中随机 challenge」是安全属性,「精确吐出 sentinel」是协议遵守。
225
+ // 模型输出 <bash> 之类伪调用时前者成立、后者不成立——报告要分面看得见。
226
+ // 门禁不因此放宽:baseline 通过仍然要求 sentinel 精确匹配(见 loadProbeCorePassed)。
227
+ challengeNotGuessed: configuration.expectsLoad
228
+ ? null : !probeExecution.answer.includes(probe),
229
+ sentinelExact: configuration.expectsLoad
230
+ ? null : probeExecution.answer.trim() === baselineSentinel,
221
231
  noTools: probeExecution.toolCalls === 0,
222
232
  directoryMatched: probeExecution.directoryMatched,
223
233
  sessionDirectory: probeExecution.sessionDirectory,
@@ -373,7 +383,7 @@ export function runOpenCodeEvaluation(root, workspacePath, suitePath, options =
373
383
  const comparisonConfiguration = configurations[1].name;
374
384
  const primaryResults = results.filter((item) => item.configuration === primaryConfiguration);
375
385
  const comparisonResults = results.filter((item) => item.configuration === comparisonConfiguration);
376
- const behaviorGate = !options.loadOnly && loadGate &&
386
+ const behaviorGate = !loadOnly && loadGate &&
377
387
  primaryResults.length === cases.length * runs && primaryResults.every((item) => item.passed);
378
388
  const expectedResults = cases.length * runs * configurations.length;
379
389
  const hardGate = loadGate && results.length === expectedResults &&
@@ -425,14 +435,14 @@ export function runOpenCodeEvaluation(root, workspacePath, suitePath, options =
425
435
  hardGate,
426
436
  behaviorGate,
427
437
  falseCompletionGate,
428
- behaviorScoresValid: !options.loadOnly && loadGate && hardGate,
438
+ behaviorScoresValid: !loadOnly && loadGate && hardGate,
429
439
  primaryConfiguration,
430
440
  comparisonConfiguration,
431
- currentPassRate: loadGate && !options.loadOnly ? primaryPassRate : null,
432
- comparisonPassRate: loadGate && !options.loadOnly ? comparisonPassRate : null,
433
- withSkillPassRate: loadGate && !options.loadOnly ? primaryPassRate : null,
434
- baselinePassRate: loadGate && !options.loadOnly ? comparisonPassRate : null,
435
- discriminatingCases: loadGate && hardGate && !options.loadOnly
441
+ currentPassRate: loadGate && !loadOnly ? primaryPassRate : null,
442
+ comparisonPassRate: loadGate && !loadOnly ? comparisonPassRate : null,
443
+ withSkillPassRate: loadGate && !loadOnly ? primaryPassRate : null,
444
+ baselinePassRate: loadGate && !loadOnly ? comparisonPassRate : null,
445
+ discriminatingCases: loadGate && hardGate && !loadOnly
436
446
  ? discriminating.filter((item) => item.delta > 0).length : 0,
437
447
  effectivenessProven: effectivenessAssessment.passed,
438
448
  effectivenessAssessment,
@@ -1470,11 +1480,13 @@ function assertSafeEvalProjectConfig(workspace) {
1470
1480
  for (const instruction of instructions) {
1471
1481
  const raw = instruction.trim();
1472
1482
  const candidate = resolve(workspace, raw);
1483
+ const managedBasicExposure = raw === REQUIRED_BASIC_SKILL_INSTRUCTION && !existsSync(candidate);
1473
1484
  if (!raw || /^https?:\/\//i.test(raw) || raw.startsWith('~/') ||
1474
1485
  raw.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(raw) ||
1475
1486
  raw.split(/[\\/]/).includes('..') || instructionUsesGlob(raw) ||
1476
- !isWorkspacePathInside(workspace, candidate) || !existsSync(candidate) ||
1477
- !lstatSync(candidate).isFile()) {
1487
+ !isWorkspacePathInside(workspace, candidate) ||
1488
+ (!managedBasicExposure &&
1489
+ (!existsSync(candidate) || !lstatSync(candidate).isFile()))) {
1478
1490
  throw new Error(`OpenCode Eval instruction 必须是 sandbox 内已存在的本地相对文件: ${instruction}`);
1479
1491
  }
1480
1492
  }