zelari-code 1.28.0 → 1.30.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.
- package/dist/cli/headless.js +14 -0
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js +11 -0
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/kraken/executor.js +725 -42
- package/dist/cli/kraken/executor.js.map +1 -1
- package/dist/cli/kraken/graphMemory.js +27 -5
- package/dist/cli/kraken/graphMemory.js.map +1 -1
- package/dist/cli/kraken/graphStatus.js +59 -0
- package/dist/cli/kraken/graphStatus.js.map +1 -1
- package/dist/cli/kraken/planner.js +189 -12
- package/dist/cli/kraken/planner.js.map +1 -1
- package/dist/cli/kraken/planner.test.js +43 -0
- package/dist/cli/kraken/planner.test.js.map +1 -0
- package/dist/cli/kraken/runtime/compile.js +73 -0
- package/dist/cli/kraken/runtime/compile.js.map +1 -0
- package/dist/cli/kraken/runtime/runScriptPlan.js +195 -0
- package/dist/cli/kraken/runtime/runScriptPlan.js.map +1 -0
- package/dist/cli/kraken/scriptPlanner.js +286 -0
- package/dist/cli/kraken/scriptPlanner.js.map +1 -0
- package/dist/cli/kraken/scriptPlanner.test.js +152 -0
- package/dist/cli/kraken/scriptPlanner.test.js.map +1 -0
- package/dist/cli/kraken/skillSuggest.js +97 -0
- package/dist/cli/kraken/skillSuggest.js.map +1 -0
- package/dist/cli/kraken/skillSuggest.test.js +157 -0
- package/dist/cli/kraken/skillSuggest.test.js.map +1 -0
- package/dist/cli/kraken/weaknessMeter.js +183 -0
- package/dist/cli/kraken/weaknessMeter.js.map +1 -0
- package/dist/cli/kraken/weaknessMeter.test.js +212 -0
- package/dist/cli/kraken/weaknessMeter.test.js.map +1 -0
- package/dist/cli/kraken/workbench.js +296 -0
- package/dist/cli/kraken/workbench.js.map +1 -0
- package/dist/cli/kraken/workbench.test.js +253 -0
- package/dist/cli/kraken/workbench.test.js.map +1 -0
- package/dist/cli/kraken/workbenchView.js +155 -0
- package/dist/cli/kraken/workbenchView.js.map +1 -0
- package/dist/cli/kraken/workbenchView.test.js +130 -0
- package/dist/cli/kraken/workbenchView.test.js.map +1 -0
- package/dist/cli/main.bundled.js +2930 -353
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/runHeadless.js +83 -8
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/slashCommands.js +16 -0
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/krakenFanout.js +200 -0
- package/dist/cli/slashHandlers/krakenFanout.js.map +1 -0
- package/dist/cli/slashHandlers/krakenGraph.js +10 -3
- package/dist/cli/slashHandlers/krakenGraph.js.map +1 -1
- package/dist/cli/slashHandlers/krakenWorkbench.js +49 -0
- package/dist/cli/slashHandlers/krakenWorkbench.js.map +1 -0
- package/dist/cli/tools/krakenCsvFanout.js +260 -0
- package/dist/cli/tools/krakenCsvFanout.js.map +1 -0
- package/dist/cli/tools/krakenCsvFanout.test.js +200 -0
- package/dist/cli/tools/krakenCsvFanout.test.js.map +1 -0
- package/dist/cli/tools/krakenModel.js +32 -0
- package/dist/cli/tools/krakenModel.js.map +1 -1
- package/dist/cli/tools/krakenRadio.js.map +1 -1
- package/dist/cli/tools/taskTool.js +2 -2
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kraken skill auto-suggest — tests.
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - `slugifyLabel` produces kebab-case ids
|
|
6
|
+
* - No suggestions when the run did not converge
|
|
7
|
+
* - No suggestions when no reviewer failed
|
|
8
|
+
* - One suggestion produced for a typical FAIL→fix pattern
|
|
9
|
+
* - Suggestion contains the failure findings and the promote command
|
|
10
|
+
* - Confidence scales with the depth of evidence
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import { slugifyLabel, suggestSkillsFromRun, renderSuggestionCard, } from './skillSuggest.js';
|
|
14
|
+
function makeRef(over) {
|
|
15
|
+
return {
|
|
16
|
+
scope: [],
|
|
17
|
+
findings: '',
|
|
18
|
+
...over,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function makeResult(opts) {
|
|
22
|
+
const tentaclesById = new Map();
|
|
23
|
+
for (const r of opts.refs)
|
|
24
|
+
tentaclesById.set(r.id, r);
|
|
25
|
+
return {
|
|
26
|
+
tentacles: tentaclesById,
|
|
27
|
+
mergeCount: 1,
|
|
28
|
+
converged: opts.converged,
|
|
29
|
+
cancelled: false,
|
|
30
|
+
durationMs: 1000,
|
|
31
|
+
unresolvedFindings: [],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
describe('slugifyLabel', () => {
|
|
35
|
+
it('converts a label to kebab-case', () => {
|
|
36
|
+
expect(slugifyLabel('Acceptance Test 1')).toBe('acceptance-test-1');
|
|
37
|
+
});
|
|
38
|
+
it('drops punctuation and trims', () => {
|
|
39
|
+
expect(slugifyLabel(' Hello, World! ')).toBe('hello-world');
|
|
40
|
+
});
|
|
41
|
+
it('falls back to "kraken-skill" for empty input', () => {
|
|
42
|
+
expect(slugifyLabel('')).toBe('kraken-skill');
|
|
43
|
+
expect(slugifyLabel('!!!')).toBe('kraken-skill');
|
|
44
|
+
});
|
|
45
|
+
it('caps at 40 chars', () => {
|
|
46
|
+
const long = 'a'.repeat(100);
|
|
47
|
+
expect(slugifyLabel(long).length).toBeLessThanOrEqual(40);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('suggestSkillsFromRun', () => {
|
|
51
|
+
it('returns [] when the run did not converge', () => {
|
|
52
|
+
const result = makeResult({
|
|
53
|
+
converged: false,
|
|
54
|
+
refs: [
|
|
55
|
+
makeRef({ id: 't0001', kind: 'verify', label: 'judge', status: 'done', verdict: 'fail', findings: 'x'.repeat(200) }),
|
|
56
|
+
makeRef({ id: 't0002', kind: 'fix', label: 'rework', status: 'done', findings: 'y'.repeat(200) }),
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
expect(suggestSkillsFromRun(result, { graphId: 'g1', goal: 'x' })).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
it('returns [] when no reviewer failed', () => {
|
|
62
|
+
const result = makeResult({
|
|
63
|
+
converged: true,
|
|
64
|
+
refs: [
|
|
65
|
+
makeRef({ id: 't0001', kind: 'general', label: 'do', status: 'done', findings: 'ok' }),
|
|
66
|
+
makeRef({ id: 't0002', kind: 'verify', label: 'judge', status: 'done', verdict: 'pass', findings: 'ok' }),
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
expect(suggestSkillsFromRun(result, { graphId: 'g2', goal: 'x' })).toEqual([]);
|
|
70
|
+
});
|
|
71
|
+
it('returns [] when there are no fix-nodes', () => {
|
|
72
|
+
const result = makeResult({
|
|
73
|
+
converged: true,
|
|
74
|
+
refs: [
|
|
75
|
+
makeRef({ id: 't0001', kind: 'general', label: 'do', status: 'done' }),
|
|
76
|
+
makeRef({ id: 't0002', kind: 'verify', label: 'judge', status: 'done', verdict: 'fail', findings: 'broken' }),
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
expect(suggestSkillsFromRun(result, { graphId: 'g3', goal: 'x' })).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
it('produces one suggestion for a FAIL→fix pattern', () => {
|
|
82
|
+
const result = makeResult({
|
|
83
|
+
converged: true,
|
|
84
|
+
refs: [
|
|
85
|
+
makeRef({ id: 't0001', kind: 'general', label: 'refactor auth', status: 'done', findings: 'wrote code' }),
|
|
86
|
+
makeRef({
|
|
87
|
+
id: 't0002',
|
|
88
|
+
kind: 'verify',
|
|
89
|
+
label: 'acceptance check',
|
|
90
|
+
status: 'done',
|
|
91
|
+
verdict: 'fail',
|
|
92
|
+
findings: 'function `slugify` is missing from src/util.ts',
|
|
93
|
+
}),
|
|
94
|
+
makeRef({
|
|
95
|
+
id: 't0003',
|
|
96
|
+
kind: 'fix',
|
|
97
|
+
label: 'rework: refactor auth',
|
|
98
|
+
status: 'done',
|
|
99
|
+
findings: 'added slugify, removed unused import',
|
|
100
|
+
}),
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
const out = suggestSkillsFromRun(result, { graphId: 'g-abc', goal: 'refactor auth' });
|
|
104
|
+
expect(out).toHaveLength(1);
|
|
105
|
+
const s = out[0];
|
|
106
|
+
expect(s.id).toMatch(/^kraken-skill-/);
|
|
107
|
+
expect(s.id).toContain('g-abc');
|
|
108
|
+
expect(s.title).toMatch(/acceptance check/);
|
|
109
|
+
expect(s.sourceKind).toBe('verify');
|
|
110
|
+
expect(s.body).toContain('slugify');
|
|
111
|
+
expect(s.body).toContain('/promote-skill');
|
|
112
|
+
expect(s.confidence).toBeGreaterThan(0);
|
|
113
|
+
});
|
|
114
|
+
it('confidence is high when findings are concrete and the goal is non-trivial', () => {
|
|
115
|
+
const failure = 'function `slugify` is missing from src/util.ts: it should take a string and return a slugified version. '.repeat(5);
|
|
116
|
+
const fix = 'added slugify, removed unused import, updated test to cover unicode. '.repeat(5);
|
|
117
|
+
const result = makeResult({
|
|
118
|
+
converged: true,
|
|
119
|
+
refs: [
|
|
120
|
+
makeRef({ id: 't0001', kind: 'general', label: 'do', status: 'done' }),
|
|
121
|
+
makeRef({ id: 't0002', kind: 'verify', label: 'judge', status: 'done', verdict: 'fail', findings: failure }),
|
|
122
|
+
makeRef({ id: 't0003', kind: 'fix', label: 'rework', status: 'done', findings: fix }),
|
|
123
|
+
makeRef({ id: 't0004', kind: 'merge', label: 'merge', status: 'done' }),
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
const out = suggestSkillsFromRun(result, { graphId: 'g-rich', goal: 'ship a working slugify that handles unicode' });
|
|
127
|
+
expect(out[0].confidence).toBeGreaterThanOrEqual(0.9);
|
|
128
|
+
});
|
|
129
|
+
it('handles spec and conformance reviewers the same way as verify', () => {
|
|
130
|
+
const result = makeResult({
|
|
131
|
+
converged: true,
|
|
132
|
+
refs: [
|
|
133
|
+
makeRef({ id: 't0001', kind: 'general', label: 'do', status: 'done' }),
|
|
134
|
+
makeRef({ id: 't0002', kind: 'spec', label: 'spec review', status: 'done', verdict: 'fail', findings: 'spec mismatch' }),
|
|
135
|
+
makeRef({ id: 't0003', kind: 'conformance', label: 'conformance', status: 'done', verdict: 'fail', findings: 'literal mismatch' }),
|
|
136
|
+
makeRef({ id: 't0004', kind: 'fix', label: 'rework', status: 'done', findings: 'fixed' }),
|
|
137
|
+
],
|
|
138
|
+
});
|
|
139
|
+
const out = suggestSkillsFromRun(result, { graphId: 'g-multi', goal: 'match the spec' });
|
|
140
|
+
expect(out).toHaveLength(1);
|
|
141
|
+
expect(out[0].sourceKind).toBe('spec'); // first reviewer fail wins
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe('renderSuggestionCard', () => {
|
|
145
|
+
it('returns the suggestion body unchanged', () => {
|
|
146
|
+
const s = {
|
|
147
|
+
id: 'test',
|
|
148
|
+
title: 'T',
|
|
149
|
+
body: 'hello',
|
|
150
|
+
sourceKind: 'verify',
|
|
151
|
+
failureFindings: 'broken',
|
|
152
|
+
confidence: 0.5,
|
|
153
|
+
};
|
|
154
|
+
expect(renderSuggestionCard(s)).toBe('hello');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
//# sourceMappingURL=skillSuggest.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skillSuggest.test.js","sourceRoot":"","sources":["../../../src/cli/kraken/skillSuggest.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weaknessMeter — LLM-as-weakness-meter wiring for the Kraken Spec Council.
|
|
3
|
+
*
|
|
4
|
+
* Background
|
|
5
|
+
* ----------
|
|
6
|
+
* The `WEAKNESS_METER_PROMPT` exported by `@zelari/core/weakness` asks a
|
|
7
|
+
* counter-LLM to enumerate the specific assumptions a reviewer made in
|
|
8
|
+
* a verify / spec / conformance reply. The result is a JSON object
|
|
9
|
+
* `{"specificity": 0..1, "assumptions": [...]}`. The inverse of
|
|
10
|
+
* specificity is the Bennett weakness score (1 = maximally general, 0
|
|
11
|
+
* = maximally specific).
|
|
12
|
+
*
|
|
13
|
+
* Until this module existed, the only weakness signal came from the
|
|
14
|
+
* local heuristic in `weaknessFromVerdict` (a regex scan over the same
|
|
15
|
+
* text). The heuristic is cheap (no I/O) and good enough for the
|
|
16
|
+
* default render, but a true LLM meter is more precise at distinguishing
|
|
17
|
+
* "vague assertion" from "specific claim with no markers in the body".
|
|
18
|
+
*
|
|
19
|
+
* This module is opt-in: it does nothing unless
|
|
20
|
+
* `ZELARI_KRAKEN_WEAKNESS_METER=1`. When enabled, the caller invokes
|
|
21
|
+
* {@link measureWeaknessViaLLM} with the persona's free text; the call
|
|
22
|
+
* is a single non-streaming chat completion against the active
|
|
23
|
+
* provider, with a short timeout and silent failure (the local
|
|
24
|
+
* heuristic is always available as a fallback).
|
|
25
|
+
*
|
|
26
|
+
* No I/O happens at import time. Pure side effects live in the helper
|
|
27
|
+
* functions; both are async and unit-testable through dependency
|
|
28
|
+
* injection of the fetch primitive.
|
|
29
|
+
*
|
|
30
|
+
* @since v1.31.x - Bennett's Razor meter wiring (Slice N+1 / CLI)
|
|
31
|
+
*/
|
|
32
|
+
import { WEAKNESS_METER_PROMPT, WeaknessMeterResponseSchema, weaknessFromMeter, } from '@zelari/core';
|
|
33
|
+
import { getProviderConfig, getModelForProvider, getCustomEndpoint } from '../providerConfig.js';
|
|
34
|
+
import { resolveApiKeyWithMeta } from '../keyStore.js';
|
|
35
|
+
/**
|
|
36
|
+
* Whether the meter is enabled. The check is cheap and the result is
|
|
37
|
+
* memoized per process so we don't re-read `process.env` on every call.
|
|
38
|
+
*/
|
|
39
|
+
let meterEnabledCache = null;
|
|
40
|
+
export function isWeaknessMeterEnabled(env = process.env) {
|
|
41
|
+
if (meterEnabledCache !== null)
|
|
42
|
+
return meterEnabledCache;
|
|
43
|
+
const raw = env.ZELARI_KRAKEN_WEAKNESS_METER;
|
|
44
|
+
const enabled = raw === '1' || raw === 'true' || raw === 'yes';
|
|
45
|
+
meterEnabledCache = enabled;
|
|
46
|
+
return enabled;
|
|
47
|
+
}
|
|
48
|
+
/** For tests: clear the memoized env read. */
|
|
49
|
+
export function _resetWeaknessMeterCacheForTests() {
|
|
50
|
+
meterEnabledCache = null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Override model for the meter. Falls back to the active provider's
|
|
54
|
+
* default model when unset. Exposed as a knob because the meter is a
|
|
55
|
+
* single-shot classification call — a smaller / cheaper model is
|
|
56
|
+
* usually fine and saves latency on a long run.
|
|
57
|
+
*/
|
|
58
|
+
function resolveMeterModel(env = process.env, fallback) {
|
|
59
|
+
const raw = env.ZELARI_KRAKEN_WEAKNESS_METER_MODEL;
|
|
60
|
+
if (raw && raw.trim() !== '')
|
|
61
|
+
return raw.trim();
|
|
62
|
+
return fallback;
|
|
63
|
+
}
|
|
64
|
+
/** Wall-clock budget for a single meter call. Default 12s. */
|
|
65
|
+
const METER_TIMEOUT_MS = (() => {
|
|
66
|
+
const raw = process.env.ZELARI_KRAKEN_WEAKNESS_METER_TIMEOUT_MS;
|
|
67
|
+
if (!raw)
|
|
68
|
+
return 12_000;
|
|
69
|
+
const n = Number.parseInt(raw, 10);
|
|
70
|
+
return Number.isFinite(n) && n > 0 ? n : 12_000;
|
|
71
|
+
})();
|
|
72
|
+
export async function measureWeaknessViaLLM(text, options = {}) {
|
|
73
|
+
const env = options.env ?? process.env;
|
|
74
|
+
if (!isWeaknessMeterEnabled(env))
|
|
75
|
+
return null;
|
|
76
|
+
if (typeof text !== 'string' || text.trim() === '')
|
|
77
|
+
return null;
|
|
78
|
+
// Resolve provider + model + key.
|
|
79
|
+
const provider = options.providerOverride ?? (await resolveActiveProvider(env));
|
|
80
|
+
if (!provider)
|
|
81
|
+
return null;
|
|
82
|
+
const model = options.modelOverride ?? resolveMeterModel(env, provider.model);
|
|
83
|
+
// Compose the prompt. We ask the model to score THIS text (the
|
|
84
|
+
// persona's free reply) — not the whole transcript — because the
|
|
85
|
+
// meter is per-message.
|
|
86
|
+
const userPayload = JSON.stringify({
|
|
87
|
+
task: 'measure weakness of the following reviewer reply',
|
|
88
|
+
reply: text,
|
|
89
|
+
});
|
|
90
|
+
const start = Date.now();
|
|
91
|
+
try {
|
|
92
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
93
|
+
const ac = new AbortController();
|
|
94
|
+
const timer = setTimeout(() => ac.abort(), METER_TIMEOUT_MS);
|
|
95
|
+
let response;
|
|
96
|
+
try {
|
|
97
|
+
response = await fetchImpl(provider.endpoint, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: {
|
|
100
|
+
'Content-Type': 'application/json',
|
|
101
|
+
Authorization: `Bearer ${provider.apiKey}`,
|
|
102
|
+
},
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
model,
|
|
105
|
+
messages: [
|
|
106
|
+
{ role: 'system', content: WEAKNESS_METER_PROMPT },
|
|
107
|
+
{ role: 'user', content: userPayload },
|
|
108
|
+
],
|
|
109
|
+
// The meter is a classification call; no need for a hot
|
|
110
|
+
// sampling distribution. Most providers accept this pair
|
|
111
|
+
// without complaint; the few that require it get a stable
|
|
112
|
+
// answer.
|
|
113
|
+
temperature: 0,
|
|
114
|
+
stream: false,
|
|
115
|
+
}),
|
|
116
|
+
signal: ac.signal,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
}
|
|
122
|
+
if (!response.ok)
|
|
123
|
+
return null;
|
|
124
|
+
const json = (await response.json());
|
|
125
|
+
const raw = json.choices?.[0]?.message?.content;
|
|
126
|
+
if (typeof raw !== 'string')
|
|
127
|
+
return null;
|
|
128
|
+
return parseMeterContent(raw, model, Date.now() - start);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Parse the model output, accept either a raw JSON object or a
|
|
136
|
+
* fenced ```json ... ``` block (the meter prompt asks for JSON only,
|
|
137
|
+
* but a model may still wrap). Return null on any parse failure.
|
|
138
|
+
*
|
|
139
|
+
* Exported (not just internal) so the unit tests can exercise the
|
|
140
|
+
* parser without re-implementing the JSON / fence handling.
|
|
141
|
+
*/
|
|
142
|
+
export function parseMeterContent(raw, model, durationMs) {
|
|
143
|
+
const trimmed = raw.trim();
|
|
144
|
+
// Strip an optional ```json fence.
|
|
145
|
+
const fenced = /^```(?:json)?\s*([\s\S]+?)\s*```$/i.exec(trimmed);
|
|
146
|
+
const body = fenced ? fenced[1] : trimmed;
|
|
147
|
+
let parsed;
|
|
148
|
+
try {
|
|
149
|
+
parsed = JSON.parse(body);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const validated = WeaknessMeterResponseSchema.safeParse(parsed);
|
|
155
|
+
if (!validated.success)
|
|
156
|
+
return null;
|
|
157
|
+
return {
|
|
158
|
+
meter: validated.data,
|
|
159
|
+
weakness: weaknessFromMeter(validated.data),
|
|
160
|
+
durationMs,
|
|
161
|
+
model,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Resolve the active provider configuration for the meter call. Reads
|
|
166
|
+
* the same on-disk / env provider config the rest of the CLI uses so
|
|
167
|
+
* the meter routes through the same channel the user already paid for.
|
|
168
|
+
* Returns null when no provider / key can be resolved.
|
|
169
|
+
*/
|
|
170
|
+
async function resolveActiveProvider(env) {
|
|
171
|
+
const cfg = await getProviderConfig();
|
|
172
|
+
if (!cfg)
|
|
173
|
+
return null;
|
|
174
|
+
const providerId = cfg.activeProviderId;
|
|
175
|
+
const key = await resolveApiKeyWithMeta(providerId, env);
|
|
176
|
+
if (!key?.apiKey)
|
|
177
|
+
return null;
|
|
178
|
+
const model = getModelForProvider(providerId) ?? cfg.modelByProvider[providerId] ?? '';
|
|
179
|
+
const endpoint = getCustomEndpoint(providerId) ??
|
|
180
|
+
`https://api.openai.com/v1/chat/completions`;
|
|
181
|
+
return { providerId, model, endpoint, apiKey: key.apiKey };
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=weaknessMeter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weaknessMeter.js","sourceRoot":"","sources":["../../../src/cli/kraken/weaknessMeter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the LLM weakness meter wiring.
|
|
3
|
+
*
|
|
4
|
+
* These tests exercise the parsing + env-gating only — they never call
|
|
5
|
+
* a real provider. A small `fetchImpl` stub stands in for the network.
|
|
6
|
+
*
|
|
7
|
+
* @since v1.31.x
|
|
8
|
+
*/
|
|
9
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
+
import { _resetWeaknessMeterCacheForTests, isWeaknessMeterEnabled, measureWeaknessViaLLM, parseMeterContent, } from './weaknessMeter.js';
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
_resetWeaknessMeterForTests();
|
|
13
|
+
vi.restoreAllMocks();
|
|
14
|
+
});
|
|
15
|
+
function _resetWeaknessMeterForTests() {
|
|
16
|
+
_resetWeaknessMeterCacheForTests();
|
|
17
|
+
}
|
|
18
|
+
describe('isWeaknessMeterEnabled', () => {
|
|
19
|
+
it('defaults to false', () => {
|
|
20
|
+
expect(isWeaknessMeterEnabled({})).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
it('treats "1", "true", "yes" as enabled', () => {
|
|
23
|
+
expect(isWeaknessMeterEnabled({ ZELARI_KRAKEN_WEAKNESS_METER: '1' })).toBe(true);
|
|
24
|
+
expect(isWeaknessMeterEnabled({ ZELARI_KRAKEN_WEAKNESS_METER: 'true' })).toBe(true);
|
|
25
|
+
expect(isWeaknessMeterEnabled({ ZELARI_KRAKEN_WEAKNESS_METER: 'yes' })).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
it('treats other truthy values as disabled (avoid surprise)', () => {
|
|
28
|
+
expect(isWeaknessMeterEnabled({ ZELARI_KRAKEN_WEAKNESS_METER: 'enabled' })).toBe(false);
|
|
29
|
+
expect(isWeaknessMeterEnabled({ ZELARI_KRAKEN_WEAKNESS_METER: 'on' })).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it('memoizes the env read for the process', () => {
|
|
32
|
+
expect(isWeaknessMeterEnabled({ ZELARI_KRAKEN_WEAKNESS_METER: '1' })).toBe(true);
|
|
33
|
+
// Even if we re-call without the var, the cache wins until reset.
|
|
34
|
+
expect(isWeaknessMeterEnabled({})).toBe(true);
|
|
35
|
+
_resetWeaknessMeterForTests();
|
|
36
|
+
expect(isWeaknessMeterEnabled({})).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('measureWeaknessViaLLM (gating)', () => {
|
|
40
|
+
it('returns null when the meter is disabled (default)', async () => {
|
|
41
|
+
const out = await measureWeaknessViaLLM('hello', {
|
|
42
|
+
env: {},
|
|
43
|
+
providerOverride: {
|
|
44
|
+
providerId: 'openai-compatible',
|
|
45
|
+
model: 'gpt-4o-mini',
|
|
46
|
+
endpoint: 'https://example.invalid',
|
|
47
|
+
apiKey: 'k',
|
|
48
|
+
},
|
|
49
|
+
fetchImpl: vi.fn(),
|
|
50
|
+
});
|
|
51
|
+
expect(out).toBeNull();
|
|
52
|
+
});
|
|
53
|
+
it('returns null on empty / whitespace text even when enabled', async () => {
|
|
54
|
+
const out = await measureWeaknessViaLLM(' \n ', {
|
|
55
|
+
env: { ZELARI_KRAKEN_WEAKNESS_METER: '1' },
|
|
56
|
+
providerOverride: {
|
|
57
|
+
providerId: 'openai-compatible',
|
|
58
|
+
model: 'gpt-4o-mini',
|
|
59
|
+
endpoint: 'https://example.invalid',
|
|
60
|
+
apiKey: 'k',
|
|
61
|
+
},
|
|
62
|
+
fetchImpl: vi.fn(),
|
|
63
|
+
});
|
|
64
|
+
expect(out).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('measureWeaknessViaLLM (HTTP path)', () => {
|
|
68
|
+
function goodFetch() {
|
|
69
|
+
return vi.fn(async () => {
|
|
70
|
+
return new Response(JSON.stringify({
|
|
71
|
+
choices: [
|
|
72
|
+
{
|
|
73
|
+
message: {
|
|
74
|
+
content: JSON.stringify({
|
|
75
|
+
specificity: 0.42,
|
|
76
|
+
assumptions: ['the file is at /x', 'version is 1.2.3'],
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
}), { status: 200, headers: { 'content-type': 'application/json' } });
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
it('parses a clean JSON payload', async () => {
|
|
85
|
+
const out = await measureWeaknessViaLLM('I MUST always EXACTLY at line 42 version 1.2.3.', {
|
|
86
|
+
env: { ZELARI_KRAKEN_WEAKNESS_METER: '1' },
|
|
87
|
+
providerOverride: {
|
|
88
|
+
providerId: 'openai-compatible',
|
|
89
|
+
model: 'gpt-4o-mini',
|
|
90
|
+
endpoint: 'https://example.invalid',
|
|
91
|
+
apiKey: 'k',
|
|
92
|
+
},
|
|
93
|
+
fetchImpl: goodFetch(),
|
|
94
|
+
});
|
|
95
|
+
expect(out).not.toBeNull();
|
|
96
|
+
expect(out.meter.specificity).toBe(0.42);
|
|
97
|
+
expect(out.meter.assumptions).toHaveLength(2);
|
|
98
|
+
expect(out.weakness).toBeCloseTo(0.58, 5);
|
|
99
|
+
expect(out.model).toBe('gpt-4o-mini');
|
|
100
|
+
});
|
|
101
|
+
it('parses a ```json fenced payload', async () => {
|
|
102
|
+
const fetchImpl = vi.fn(async () => {
|
|
103
|
+
return new Response(JSON.stringify({
|
|
104
|
+
choices: [
|
|
105
|
+
{
|
|
106
|
+
message: {
|
|
107
|
+
content: '```json\n{"specificity": 0.9, "assumptions": []}\n```',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
}), { status: 200 });
|
|
112
|
+
});
|
|
113
|
+
const out = await measureWeaknessViaLLM('Looks good.', {
|
|
114
|
+
env: { ZELARI_KRAKEN_WEAKNESS_METER: '1' },
|
|
115
|
+
providerOverride: {
|
|
116
|
+
providerId: 'openai-compatible',
|
|
117
|
+
model: 'gpt-4o-mini',
|
|
118
|
+
endpoint: 'https://example.invalid',
|
|
119
|
+
apiKey: 'k',
|
|
120
|
+
},
|
|
121
|
+
fetchImpl,
|
|
122
|
+
});
|
|
123
|
+
expect(out).not.toBeNull();
|
|
124
|
+
expect(out.weakness).toBeCloseTo(0.1, 5);
|
|
125
|
+
});
|
|
126
|
+
it('returns null on HTTP non-2xx', async () => {
|
|
127
|
+
const fetchImpl = vi.fn(async () => {
|
|
128
|
+
return new Response('rate limited', { status: 429 });
|
|
129
|
+
});
|
|
130
|
+
const out = await measureWeaknessViaLLM('Whatever.', {
|
|
131
|
+
env: { ZELARI_KRAKEN_WEAKNESS_METER: '1' },
|
|
132
|
+
providerOverride: {
|
|
133
|
+
providerId: 'openai-compatible',
|
|
134
|
+
model: 'gpt-4o-mini',
|
|
135
|
+
endpoint: 'https://example.invalid',
|
|
136
|
+
apiKey: 'k',
|
|
137
|
+
},
|
|
138
|
+
fetchImpl,
|
|
139
|
+
});
|
|
140
|
+
expect(out).toBeNull();
|
|
141
|
+
});
|
|
142
|
+
it('returns null on malformed JSON (silent failure)', async () => {
|
|
143
|
+
const fetchImpl = vi.fn(async () => {
|
|
144
|
+
return new Response(JSON.stringify({ choices: [{ message: { content: '{not valid' } }] }), { status: 200 });
|
|
145
|
+
});
|
|
146
|
+
const out = await measureWeaknessViaLLM('Whatever.', {
|
|
147
|
+
env: { ZELARI_KRAKEN_WEAKNESS_METER: '1' },
|
|
148
|
+
providerOverride: {
|
|
149
|
+
providerId: 'openai-compatible',
|
|
150
|
+
model: 'gpt-4o-mini',
|
|
151
|
+
endpoint: 'https://example.invalid',
|
|
152
|
+
apiKey: 'k',
|
|
153
|
+
},
|
|
154
|
+
fetchImpl,
|
|
155
|
+
});
|
|
156
|
+
expect(out).toBeNull();
|
|
157
|
+
});
|
|
158
|
+
it('returns null when the payload fails zod validation', async () => {
|
|
159
|
+
const fetchImpl = vi.fn(async () => {
|
|
160
|
+
// specificity out of range — zod should reject.
|
|
161
|
+
return new Response(JSON.stringify({
|
|
162
|
+
choices: [{ message: { content: JSON.stringify({ specificity: 1.7, assumptions: [] }) } }],
|
|
163
|
+
}), { status: 200 });
|
|
164
|
+
});
|
|
165
|
+
const out = await measureWeaknessViaLLM('Whatever.', {
|
|
166
|
+
env: { ZELARI_KRAKEN_WEAKNESS_METER: '1' },
|
|
167
|
+
providerOverride: {
|
|
168
|
+
providerId: 'openai-compatible',
|
|
169
|
+
model: 'gpt-4o-mini',
|
|
170
|
+
endpoint: 'https://example.invalid',
|
|
171
|
+
apiKey: 'k',
|
|
172
|
+
},
|
|
173
|
+
fetchImpl,
|
|
174
|
+
});
|
|
175
|
+
expect(out).toBeNull();
|
|
176
|
+
});
|
|
177
|
+
it('returns null on a fetch rejection (network / timeout)', async () => {
|
|
178
|
+
const fetchImpl = vi.fn(async () => {
|
|
179
|
+
throw new Error('socket hang up');
|
|
180
|
+
});
|
|
181
|
+
const out = await measureWeaknessViaLLM('Whatever.', {
|
|
182
|
+
env: { ZELARI_KRAKEN_WEAKNESS_METER: '1' },
|
|
183
|
+
providerOverride: {
|
|
184
|
+
providerId: 'openai-compatible',
|
|
185
|
+
model: 'gpt-4o-mini',
|
|
186
|
+
endpoint: 'https://example.invalid',
|
|
187
|
+
apiKey: 'k',
|
|
188
|
+
},
|
|
189
|
+
fetchImpl,
|
|
190
|
+
});
|
|
191
|
+
expect(out).toBeNull();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
describe('parseMeterContent (unit)', () => {
|
|
195
|
+
it('accepts raw JSON', () => {
|
|
196
|
+
const out = parseMeterContent(JSON.stringify({ specificity: 0.2, assumptions: ['a'] }), 'm', 42);
|
|
197
|
+
expect(out).not.toBeNull();
|
|
198
|
+
expect(out.meter.specificity).toBe(0.2);
|
|
199
|
+
expect(out.durationMs).toBe(42);
|
|
200
|
+
});
|
|
201
|
+
it('accepts a ```json fenced payload', () => {
|
|
202
|
+
const out = parseMeterContent('```json\n{"specificity": 0.5, "assumptions": []}\n```', 'm', 0);
|
|
203
|
+
expect(out).not.toBeNull();
|
|
204
|
+
expect(out.meter.specificity).toBe(0.5);
|
|
205
|
+
});
|
|
206
|
+
it('returns null on a too-many-assumptions payload (>12)', () => {
|
|
207
|
+
const tooMany = Array.from({ length: 13 }, (_, i) => `a${i}`);
|
|
208
|
+
const out = parseMeterContent(JSON.stringify({ specificity: 0.5, assumptions: tooMany }), 'm', 0);
|
|
209
|
+
expect(out).toBeNull();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
//# sourceMappingURL=weaknessMeter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weaknessMeter.test.js","sourceRoot":"","sources":["../../../src/cli/kraken/weaknessMeter.test.ts"],"names":[],"mappings":"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"}
|