caveat-cli 0.19.8 → 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/{chunk-ITQLGM4K.js → chunk-PFYFYJNS.js} +103 -41
- package/dist/chunk-PFYFYJNS.js.map +1 -0
- package/dist/index.js +108 -18
- package/dist/index.js.map +1 -1
- package/dist/{server-H4BWLLZ2.js → server-JXBPTQNU.js} +55 -2
- package/dist/server-JXBPTQNU.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-ITQLGM4K.js.map +0 -1
- package/dist/server-H4BWLLZ2.js.map +0 -1
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-
|
|
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-
|
|
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
|
`);
|
|
@@ -33210,6 +33215,7 @@ var API_URL = "https://api.typesafe.ai/v1/systemone";
|
|
|
33210
33215
|
var STRUGGLE_THRESHOLD = 0.85;
|
|
33211
33216
|
var MATCH_THRESHOLD = 0.8;
|
|
33212
33217
|
var MAX_TERMS = 48;
|
|
33218
|
+
var MAX_SEARCH_TERMS = 3;
|
|
33213
33219
|
var MAX_CANDIDATES = 20;
|
|
33214
33220
|
async function callJev(key, state, questions, fetcher = fetch) {
|
|
33215
33221
|
const response = await fetcher(API_URL, {
|
|
@@ -33232,9 +33238,16 @@ function noul(answer) {
|
|
|
33232
33238
|
}
|
|
33233
33239
|
return answer.noul;
|
|
33234
33240
|
}
|
|
33235
|
-
function
|
|
33236
|
-
if (answer?.type !== "choice" || !
|
|
33237
|
-
|
|
33241
|
+
function searchTermChoices(answer, terms) {
|
|
33242
|
+
if (answer?.type !== "choice" || !isRecord(answer.probabilities)) throw new Error("jev_choice_invalid");
|
|
33243
|
+
const keys = terms.map((_, index) => `term_${index}`);
|
|
33244
|
+
const allowed = /* @__PURE__ */ new Set([...keys, "none"]);
|
|
33245
|
+
if (!allowed.has(answer.choice) || Object.keys(answer.probabilities).length !== allowed.size || Object.entries(answer.probabilities).some(([key, probability]) => !allowed.has(key) || typeof probability !== "number" || !Number.isFinite(probability) || probability < 0 || probability > 1)) {
|
|
33246
|
+
throw new Error("jev_choice_invalid");
|
|
33247
|
+
}
|
|
33248
|
+
if (answer.choice === "none") return [];
|
|
33249
|
+
const noneProbability = answer.probabilities.none;
|
|
33250
|
+
return keys.map((key, index) => ({ index, probability: answer.probabilities[key] })).filter(({ probability }) => probability > noneProbability).sort((a, b) => b.probability - a.probability || a.index - b.index).slice(0, MAX_SEARCH_TERMS).map(({ index }) => terms[index]);
|
|
33238
33251
|
}
|
|
33239
33252
|
function candidateTerms(turns) {
|
|
33240
33253
|
const found = /* @__PURE__ */ new Map();
|
|
@@ -33262,17 +33275,27 @@ async function judgeStruggle(key, turns, ask = callJev) {
|
|
|
33262
33275
|
};
|
|
33263
33276
|
if (terms.length > 0) questions.search_term = {
|
|
33264
33277
|
type: "choice",
|
|
33265
|
-
instructions: "If the agent is struggling, which exact term from these options would best retrieve a useful Caveat knowledge entry about the concrete unresolved problem? Select none when no option identifies it.",
|
|
33278
|
+
instructions: "If the agent is struggling, which exact term from these options would best retrieve a useful Caveat knowledge entry about the concrete unresolved problem? Select none when no option identifies it. The search will use up to three of your highest-ranked terms together as an AND query.",
|
|
33266
33279
|
criteria: { ...options, none: "No listed term would retrieve useful knowledge about this problem." }
|
|
33267
33280
|
};
|
|
33268
33281
|
const response = await ask(key, { turns }, questions);
|
|
33269
|
-
const
|
|
33270
|
-
|
|
33271
|
-
const
|
|
33272
|
-
|
|
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
|
+
};
|
|
33273
33296
|
}
|
|
33274
33297
|
async function rankKnowledge(key, turns, candidates, ask = callJev) {
|
|
33275
|
-
if (candidates.length === 0) return null;
|
|
33298
|
+
if (candidates.length === 0) return { selected: null, scores: [] };
|
|
33276
33299
|
const state = {
|
|
33277
33300
|
turns,
|
|
33278
33301
|
currentHostOs: fingerprint().os,
|
|
@@ -33295,14 +33318,16 @@ async function rankKnowledge(key, turns, candidates, ask = callJev) {
|
|
|
33295
33318
|
const response = await ask(key, state, questions);
|
|
33296
33319
|
let best = null;
|
|
33297
33320
|
let bestScore = MATCH_THRESHOLD;
|
|
33321
|
+
const scores = [];
|
|
33298
33322
|
for (let index = 0; index < candidates.length; index++) {
|
|
33299
33323
|
const score = noul(response.answers[`candidate_${index}`]);
|
|
33324
|
+
scores.push(score);
|
|
33300
33325
|
if (score >= MATCH_THRESHOLD && (best === null || score > bestScore)) {
|
|
33301
33326
|
best = candidates[index];
|
|
33302
33327
|
bestScore = score;
|
|
33303
33328
|
}
|
|
33304
33329
|
}
|
|
33305
|
-
return best;
|
|
33330
|
+
return { selected: best, scores };
|
|
33306
33331
|
}
|
|
33307
33332
|
function quotePowerShell(value) {
|
|
33308
33333
|
return `'${value.replaceAll("'", "''")}'`;
|
|
@@ -33367,39 +33392,88 @@ async function runJevStruggle(ctx, host, payload, deps = {}) {
|
|
|
33367
33392
|
const key = (deps.readKey ?? readJevKey)(ctx.caveatHome);
|
|
33368
33393
|
const ask = deps.ask ?? callJev;
|
|
33369
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
|
+
};
|
|
33370
33418
|
state.lastTurn = turnKey;
|
|
33371
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
|
+
});
|
|
33372
33429
|
saveState(ctx.caveatHome, sessionId, state);
|
|
33373
33430
|
return null;
|
|
33374
33431
|
}
|
|
33375
33432
|
let candidates = [];
|
|
33376
|
-
|
|
33433
|
+
const query = judgment.terms.join(" ");
|
|
33434
|
+
let ftsHits = [];
|
|
33435
|
+
if (query) {
|
|
33377
33436
|
if (!existsSync14(ctx.paths.dbPath)) throw new Error("jev_knowledge_index_missing");
|
|
33378
33437
|
const db = openDb({ path: ctx.paths.dbPath });
|
|
33379
33438
|
try {
|
|
33380
33439
|
const taskText = context.turns.map((turn) => `${turn.user}
|
|
33381
33440
|
${turn.assistant}
|
|
33382
33441
|
${turn.thinking}`).join("\n");
|
|
33383
|
-
|
|
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));
|
|
33384
33445
|
} finally {
|
|
33385
33446
|
db.close();
|
|
33386
33447
|
}
|
|
33387
33448
|
}
|
|
33388
|
-
const
|
|
33449
|
+
const ranked = await rankKnowledge(key, context.turns, candidates, ask);
|
|
33450
|
+
const selected = ranked.selected;
|
|
33389
33451
|
const id = selected ? `${selected.hit.source}/${selected.hit.id}` : null;
|
|
33390
|
-
const term =
|
|
33391
|
-
|
|
33452
|
+
const term = query || "_none";
|
|
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) {
|
|
33392
33465
|
saveState(ctx.caveatHome, sessionId, state);
|
|
33393
33466
|
return null;
|
|
33394
33467
|
}
|
|
33395
33468
|
return {
|
|
33396
|
-
text:
|
|
33469
|
+
text: noticeText,
|
|
33397
33470
|
ref: id,
|
|
33398
33471
|
delivered: () => {
|
|
33399
33472
|
const current = loadState(ctx.caveatHome, sessionId);
|
|
33400
33473
|
current.lastTurn = turnKey;
|
|
33401
33474
|
if (id && !current.deliveredIds.includes(id)) current.deliveredIds.push(id);
|
|
33402
33475
|
if (!id && !current.notifiedTerms.includes(term)) current.notifiedTerms.push(term);
|
|
33476
|
+
updateJevDelivery(ctx.caveatHome, observationId);
|
|
33403
33477
|
saveState(ctx.caveatHome, sessionId, current);
|
|
33404
33478
|
if (selected) {
|
|
33405
33479
|
const db = openDb({ path: ctx.paths.dbPath });
|
|
@@ -34681,6 +34755,15 @@ function jevStatus(ctx) {
|
|
|
34681
34755
|
})}
|
|
34682
34756
|
`);
|
|
34683
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
|
+
}
|
|
34684
34767
|
|
|
34685
34768
|
// src/index.ts
|
|
34686
34769
|
var program = new Command();
|
|
@@ -34709,6 +34792,13 @@ jev.command("disable").action(() => {
|
|
|
34709
34792
|
jev.command("status").action(() => {
|
|
34710
34793
|
jevStatus(buildContext(stdoutLogger));
|
|
34711
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
|
+
});
|
|
34712
34802
|
var parseLimit = (value) => {
|
|
34713
34803
|
const parsed = parseCursor(value);
|
|
34714
34804
|
if (parsed < 1 || parsed > 256) throw Error("invalid_limit");
|