oh-my-knowledge 0.43.0 → 0.45.0

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.
@@ -108,6 +108,16 @@ export function validateSamples(samples) {
108
108
  // Pure documentation/diagnostic fields; do NOT participate in grading/judge/verdict.
109
109
  const VALID_DIFFICULTY = new Set(['easy', 'medium', 'hard']);
110
110
  const VALID_PROVENANCE = new Set(['human', 'llm-generated', 'production-trace']);
111
+ const VALID_COVERAGE_TARGET_KIND = new Set([
112
+ 'skill',
113
+ 'skill_file',
114
+ 'frontmatter',
115
+ 'reference',
116
+ 'script',
117
+ 'hard_rule',
118
+ 'workflow',
119
+ 'workflow_node',
120
+ ]);
111
121
  for (const [i, sample] of samples.entries()) {
112
122
  if (!sample.sample_id || typeof sample.sample_id !== 'string') {
113
123
  throw new Error(`samples[${i}] missing or invalid required field: sample_id (must be a non-empty string)`);
@@ -135,6 +145,23 @@ export function validateSamples(samples) {
135
145
  if (sample.construct !== undefined && typeof sample.construct !== 'string') {
136
146
  throw new Error(`samples[${i}] (${sample.sample_id}) invalid construct: must be a string (got ${typeof sample.construct})`);
137
147
  }
148
+ if (sample.covers !== undefined) {
149
+ if (!Array.isArray(sample.covers)) {
150
+ throw new Error(`samples[${i}] (${sample.sample_id}) invalid covers: must be an array of coverage target objects`);
151
+ }
152
+ for (const [j, target] of sample.covers.entries()) {
153
+ if (!target || typeof target !== 'object' || Array.isArray(target)) {
154
+ throw new Error(`samples[${i}] (${sample.sample_id}) covers[${j}] must be an object with targetKind/ref`);
155
+ }
156
+ const record = target;
157
+ if (typeof record.targetKind !== 'string' || !VALID_COVERAGE_TARGET_KIND.has(record.targetKind)) {
158
+ throw new Error(`samples[${i}] (${sample.sample_id}) covers[${j}] invalid targetKind: ${JSON.stringify(record.targetKind)}, expected one of [${[...VALID_COVERAGE_TARGET_KIND].join(', ')}]`);
159
+ }
160
+ if (typeof record.ref !== 'string' || !record.ref.trim()) {
161
+ throw new Error(`samples[${i}] (${sample.sample_id}) covers[${j}] invalid ref: must be a non-empty string`);
162
+ }
163
+ }
164
+ }
138
165
  // tools_called / tools_not_called: values 必须非空。空 values 在 grader 里
139
166
  // 永远 passed=true 但 weight=0,是无意义占位,污染断言计数(N/M 看上去比真实
140
167
  // 通过率高)+ 让 generator 输出可观测的 garbage 沉淀到磁盘。直接拒掉,作者
@@ -149,9 +149,10 @@ export function renderRunList(runs, lang = DEFAULT_LANG) {
149
149
  // 顶部 verdict pill 主导视觉,id+date 是身份,scores 是成绩,底部 meta 是元数据,
150
150
  // delete 默认隐藏在 hover 出现避免误点。批量报告共用同一组件,batch pill 替代 verdict。
151
151
  const formatDateFromId = (id, fallbackTs) => {
152
- // 兼容两代 run id 后缀:旧 `…YYYYMMDD-HHmm`、新 `…YYYYMMDD-HHmmss-rand4`(含秒+随机)。
152
+ // 兼容三代 run id 后缀:旧 `…YYYYMMDD-HHmm`、过渡期 `…YYYYMMDD-HHmmss-rand4`、
153
+ // 统一后 `…YYYYMMDDTHHmmss-rand4`(含秒+随机)。
153
154
  // 都从 id 直接派生本地展示时间(确定性,不走时区敏感的 toLocaleString);新增的秒段 + 随机后缀可选匹配。
154
- const idMatch = id.match(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(?:\d{2}-[a-z0-9]+)?$/);
155
+ const idMatch = id.match(/(\d{4})(\d{2})(\d{2})(?:T|-)(\d{2})(\d{2})(?:\d{2})?(?:-[a-z0-9]+)?$/);
155
156
  if (idMatch)
156
157
  return `${idMatch[2]}/${idMatch[3]} ${idMatch[4]}:${idMatch[5]}`;
157
158
  return fallbackTs ? new Date(fallbackTs).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }) : '';