caveat-cli 0.19.9 → 0.19.10

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/dist/index.js CHANGED
@@ -37,6 +37,8 @@ import {
37
37
  isPrivateOwnerStat,
38
38
  isWindows,
39
39
  isWindowsEnv,
40
+ jevObservationId,
41
+ listJevObservations,
40
42
  listRecent,
41
43
  listStale,
42
44
  loadConfig,
@@ -59,6 +61,7 @@ import {
59
61
  releaseReindexLock,
60
62
  resetAutoSyncFailureState,
61
63
  resolvePaths,
64
+ reviewJevObservation,
62
65
  runAutoSync,
63
66
  runWindowsAcl,
64
67
  runtimeErrorsDiagnostics,
@@ -71,10 +74,12 @@ import {
71
74
  toolErrorReminderText,
72
75
  triggerAutoSync,
73
76
  updateEntry,
77
+ updateJevDelivery,
74
78
  userPromptSubmitReminderText,
75
79
  writeDigestMarker,
80
+ writeJevObservation,
76
81
  writeUserConfigPatch
77
- } from "./chunk-ITQLGM4K.js";
82
+ } from "./chunk-PFYFYJNS.js";
78
83
 
79
84
  // ../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/code.js
80
85
  var require_code = __commonJS({
@@ -8390,7 +8395,7 @@ function runStats(ctx) {
8390
8395
 
8391
8396
  // src/commands/serve.ts
8392
8397
  async function runServe(opts) {
8393
- const { startServer } = await import("./server-H4BWLLZ2.js");
8398
+ const { startServer } = await import("./server-JXBPTQNU.js");
8394
8399
  const { port, host } = startServer({ port: opts.port });
8395
8400
  process.stdout.write(`[caveat] web portal: http://${host}:${port}/
8396
8401
  `);
@@ -33274,12 +33279,23 @@ async function judgeStruggle(key, turns, ask = callJev) {
33274
33279
  criteria: { ...options, none: "No listed term would retrieve useful knowledge about this problem." }
33275
33280
  };
33276
33281
  const response = await ask(key, { turns }, questions);
33277
- const struggling = noul(response.answers.repeated_problem) >= STRUGGLE_THRESHOLD && noul(response.answers.clearly_stuck) >= STRUGGLE_THRESHOLD;
33278
- if (!struggling || terms.length === 0) return { struggling, terms: [] };
33279
- return { struggling, terms: searchTermChoices(response.answers.search_term, terms) };
33282
+ const repeatedProblem = noul(response.answers.repeated_problem);
33283
+ const clearlyStuck = noul(response.answers.clearly_stuck);
33284
+ const struggling = repeatedProblem >= STRUGGLE_THRESHOLD && clearlyStuck >= STRUGGLE_THRESHOLD;
33285
+ const choice = terms.length > 0 ? response.answers.search_term : void 0;
33286
+ const selected = terms.length > 0 ? searchTermChoices(choice, terms) : [];
33287
+ const termChoices = terms.map((term, index) => ({ term, probability: choice.probabilities[`term_${index}`] }));
33288
+ return {
33289
+ struggling,
33290
+ terms: struggling ? selected : [],
33291
+ repeatedProblem,
33292
+ clearlyStuck,
33293
+ termChoices,
33294
+ noneProbability: choice ? choice.probabilities.none : null
33295
+ };
33280
33296
  }
33281
33297
  async function rankKnowledge(key, turns, candidates, ask = callJev) {
33282
- if (candidates.length === 0) return null;
33298
+ if (candidates.length === 0) return { selected: null, scores: [] };
33283
33299
  const state = {
33284
33300
  turns,
33285
33301
  currentHostOs: fingerprint().os,
@@ -33302,14 +33318,16 @@ async function rankKnowledge(key, turns, candidates, ask = callJev) {
33302
33318
  const response = await ask(key, state, questions);
33303
33319
  let best = null;
33304
33320
  let bestScore = MATCH_THRESHOLD;
33321
+ const scores = [];
33305
33322
  for (let index = 0; index < candidates.length; index++) {
33306
33323
  const score = noul(response.answers[`candidate_${index}`]);
33324
+ scores.push(score);
33307
33325
  if (score >= MATCH_THRESHOLD && (best === null || score > bestScore)) {
33308
33326
  best = candidates[index];
33309
33327
  bestScore = score;
33310
33328
  }
33311
33329
  }
33312
- return best;
33330
+ return { selected: best, scores };
33313
33331
  }
33314
33332
  function quotePowerShell(value) {
33315
33333
  return `'${value.replaceAll("'", "''")}'`;
@@ -33374,13 +33392,46 @@ async function runJevStruggle(ctx, host, payload, deps = {}) {
33374
33392
  const key = (deps.readKey ?? readJevKey)(ctx.caveatHome);
33375
33393
  const ask = deps.ask ?? callJev;
33376
33394
  const judgment = await judgeStruggle(key, context.turns, ask);
33395
+ const observationId = jevObservationId(host, sessionId, turnKey);
33396
+ const base = {
33397
+ schema: "caveat.jev_observation.v1",
33398
+ id: observationId,
33399
+ observedAt: (/* @__PURE__ */ new Date()).toISOString(),
33400
+ host,
33401
+ sessionId,
33402
+ projectRoot,
33403
+ transcriptPath,
33404
+ turns: context.turns.map(({ originSessionId, turnNumber, truncated }) => ({ originSessionId, turnNumber, truncated })),
33405
+ thinkingAvailable: context.thinkingAvailable,
33406
+ model: MODEL,
33407
+ repeatedProblem: judgment.repeatedProblem,
33408
+ clearlyStuck: judgment.clearlyStuck,
33409
+ struggleThreshold: STRUGGLE_THRESHOLD,
33410
+ struggling: judgment.struggling,
33411
+ termChoices: judgment.termChoices,
33412
+ noneProbability: judgment.noneProbability,
33413
+ selectedTerms: judgment.terms,
33414
+ query: judgment.terms.join(" "),
33415
+ matchThreshold: MATCH_THRESHOLD,
33416
+ review: null
33417
+ };
33377
33418
  state.lastTurn = turnKey;
33378
33419
  if (!judgment.struggling) {
33420
+ writeJevObservation(ctx.caveatHome, {
33421
+ ...base,
33422
+ ftsHits: [],
33423
+ candidates: [],
33424
+ selectedRef: null,
33425
+ noticeText: null,
33426
+ decision: "below_struggle_threshold",
33427
+ delivery: "none"
33428
+ });
33379
33429
  saveState(ctx.caveatHome, sessionId, state);
33380
33430
  return null;
33381
33431
  }
33382
33432
  let candidates = [];
33383
33433
  const query = judgment.terms.join(" ");
33434
+ let ftsHits = [];
33384
33435
  if (query) {
33385
33436
  if (!existsSync14(ctx.paths.dbPath)) throw new Error("jev_knowledge_index_missing");
33386
33437
  const db = openDb({ path: ctx.paths.dbPath });
@@ -33388,26 +33439,41 @@ async function runJevStruggle(ctx, host, payload, deps = {}) {
33388
33439
  const taskText = context.turns.map((turn) => `${turn.user}
33389
33440
  ${turn.assistant}
33390
33441
  ${turn.thinking}`).join("\n");
33391
- candidates = search(db, { query, limit: MAX_CANDIDATES }).filter((hit) => environmentAppliesToTask(hit.environment, taskText, fingerprint().os)).map((hit) => ({ hit, entry: get(db, hit.id, hit.source) })).filter((item) => item.entry !== null).filter((item) => Boolean(item.entry.sections.Resolution));
33442
+ const hits = search(db, { query, limit: MAX_CANDIDATES });
33443
+ ftsHits = hits.map((hit) => `${hit.source}/${hit.id}`);
33444
+ candidates = hits.filter((hit) => environmentAppliesToTask(hit.environment, taskText, fingerprint().os)).map((hit) => ({ hit, entry: get(db, hit.id, hit.source) })).filter((item) => item.entry !== null).filter((item) => Boolean(item.entry.sections.Resolution));
33392
33445
  } finally {
33393
33446
  db.close();
33394
33447
  }
33395
33448
  }
33396
- const selected = await rankKnowledge(key, context.turns, candidates, ask);
33449
+ const ranked = await rankKnowledge(key, context.turns, candidates, ask);
33450
+ const selected = ranked.selected;
33397
33451
  const id = selected ? `${selected.hit.source}/${selected.hit.id}` : null;
33398
33452
  const term = query || "_none";
33399
- if (id && (state.deliveredIds.includes(id) || wasHitDelivered(ctx.caveatHome, sessionId, id)) || !id && state.notifiedTerms.includes(term)) {
33453
+ const suppressed = Boolean(id && (state.deliveredIds.includes(id) || wasHitDelivered(ctx.caveatHome, sessionId, id)) || !id && state.notifiedTerms.includes(term));
33454
+ const noticeText = reminder(selected);
33455
+ writeJevObservation(ctx.caveatHome, {
33456
+ ...base,
33457
+ ftsHits,
33458
+ candidates: candidates.map((candidate, index) => ({ ref: `${candidate.hit.source}/${candidate.hit.id}`, score: ranked.scores[index] })),
33459
+ selectedRef: id,
33460
+ noticeText: suppressed ? null : noticeText,
33461
+ decision: id ? "matched" : !query ? "no_terms" : candidates.length === 0 ? "no_candidates" : "below_match_threshold",
33462
+ delivery: suppressed ? "suppressed" : "pending"
33463
+ });
33464
+ if (suppressed) {
33400
33465
  saveState(ctx.caveatHome, sessionId, state);
33401
33466
  return null;
33402
33467
  }
33403
33468
  return {
33404
- text: reminder(selected),
33469
+ text: noticeText,
33405
33470
  ref: id,
33406
33471
  delivered: () => {
33407
33472
  const current = loadState(ctx.caveatHome, sessionId);
33408
33473
  current.lastTurn = turnKey;
33409
33474
  if (id && !current.deliveredIds.includes(id)) current.deliveredIds.push(id);
33410
33475
  if (!id && !current.notifiedTerms.includes(term)) current.notifiedTerms.push(term);
33476
+ updateJevDelivery(ctx.caveatHome, observationId);
33411
33477
  saveState(ctx.caveatHome, sessionId, current);
33412
33478
  if (selected) {
33413
33479
  const db = openDb({ path: ctx.paths.dbPath });
@@ -34689,6 +34755,15 @@ function jevStatus(ctx) {
34689
34755
  })}
34690
34756
  `);
34691
34757
  }
34758
+ function listJevCases(ctx) {
34759
+ process.stdout.write(`${JSON.stringify({ schema: "caveat.jev_cases.v1", cases: listJevObservations(ctx.caveatHome) })}
34760
+ `);
34761
+ }
34762
+ function reviewJevCase(ctx, id, verdict, note) {
34763
+ const item = reviewJevObservation(ctx.caveatHome, id, verdict, note);
34764
+ process.stdout.write(`${JSON.stringify({ id: item.id, review: item.review })}
34765
+ `);
34766
+ }
34692
34767
 
34693
34768
  // src/index.ts
34694
34769
  var program = new Command();
@@ -34717,6 +34792,13 @@ jev.command("disable").action(() => {
34717
34792
  jev.command("status").action(() => {
34718
34793
  jevStatus(buildContext(stdoutLogger));
34719
34794
  });
34795
+ jev.command("cases").requiredOption("--json", "emit case records").action(() => {
34796
+ listJevCases(buildContext(stdoutLogger));
34797
+ });
34798
+ jev.command("review <id> <verdict>").description("Review one case: correct | incorrect | missed | none | unclear").option("--note <text>", "short reason for the verdict", "").action((id, verdict, opts) => {
34799
+ if (!["correct", "incorrect", "missed", "none", "unclear"].includes(verdict)) throw new Error("jev_review_verdict_invalid");
34800
+ reviewJevCase(buildContext(stdoutLogger), id, verdict, opts.note);
34801
+ });
34720
34802
  var parseLimit = (value) => {
34721
34803
  const parsed = parseCursor(value);
34722
34804
  if (parsed < 1 || parsed > 256) throw Error("invalid_limit");