@zelari/core 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.
Files changed (52) hide show
  1. package/dist/kraken/graph.d.ts +5 -2
  2. package/dist/kraken/graph.d.ts.map +1 -1
  3. package/dist/kraken/graph.js.map +1 -1
  4. package/dist/kraken/index.d.ts +11 -2
  5. package/dist/kraken/index.d.ts.map +1 -1
  6. package/dist/kraken/index.js +11 -2
  7. package/dist/kraken/index.js.map +1 -1
  8. package/dist/kraken/personas/conformance.d.ts +25 -0
  9. package/dist/kraken/personas/conformance.d.ts.map +1 -0
  10. package/dist/kraken/personas/conformance.js +89 -0
  11. package/dist/kraken/personas/conformance.js.map +1 -0
  12. package/dist/kraken/personas/index.d.ts +12 -0
  13. package/dist/kraken/personas/index.d.ts.map +1 -0
  14. package/dist/kraken/personas/index.js +12 -0
  15. package/dist/kraken/personas/index.js.map +1 -0
  16. package/dist/kraken/personas/registry.d.ts +62 -0
  17. package/dist/kraken/personas/registry.d.ts.map +1 -0
  18. package/dist/kraken/personas/registry.js +49 -0
  19. package/dist/kraken/personas/registry.js.map +1 -0
  20. package/dist/kraken/personas/specReviewer.d.ts +21 -0
  21. package/dist/kraken/personas/specReviewer.d.ts.map +1 -0
  22. package/dist/kraken/personas/specReviewer.js +74 -0
  23. package/dist/kraken/personas/specReviewer.js.map +1 -0
  24. package/dist/kraken/runtime/index.d.ts +13 -0
  25. package/dist/kraken/runtime/index.d.ts.map +1 -0
  26. package/dist/kraken/runtime/index.js +16 -0
  27. package/dist/kraken/runtime/index.js.map +1 -0
  28. package/dist/kraken/runtime/runner.d.ts +80 -0
  29. package/dist/kraken/runtime/runner.d.ts.map +1 -0
  30. package/dist/kraken/runtime/runner.js +253 -0
  31. package/dist/kraken/runtime/runner.js.map +1 -0
  32. package/dist/kraken/runtime/sandbox.d.ts +65 -0
  33. package/dist/kraken/runtime/sandbox.d.ts.map +1 -0
  34. package/dist/kraken/runtime/sandbox.js +158 -0
  35. package/dist/kraken/runtime/sandbox.js.map +1 -0
  36. package/dist/kraken/runtime/sdk.d.ts +32 -0
  37. package/dist/kraken/runtime/sdk.d.ts.map +1 -0
  38. package/dist/kraken/runtime/sdk.js +61 -0
  39. package/dist/kraken/runtime/sdk.js.map +1 -0
  40. package/dist/kraken/runtime/types.d.ts +220 -0
  41. package/dist/kraken/runtime/types.d.ts.map +1 -0
  42. package/dist/kraken/runtime/types.js +35 -0
  43. package/dist/kraken/runtime/types.js.map +1 -0
  44. package/dist/kraken/verdict.d.ts +118 -0
  45. package/dist/kraken/verdict.d.ts.map +1 -0
  46. package/dist/kraken/verdict.js +143 -0
  47. package/dist/kraken/verdict.js.map +1 -0
  48. package/dist/kraken/weakness.d.ts +160 -0
  49. package/dist/kraken/weakness.d.ts.map +1 -0
  50. package/dist/kraken/weakness.js +305 -0
  51. package/dist/kraken/weakness.js.map +1 -0
  52. package/package.json +1 -1
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Kraken graph engine — weakness-based hypothesis ranking (Bennett 2023).
3
+ *
4
+ * "The Optimal Choice of Hypothesis Is the Weakest, Not the Shortest"
5
+ * Michael Timothy Bennett, AGI 2023 — arXiv:2301.12987v4.
6
+ *
7
+ * Bennett's formal result, restated for a coding-agent context:
8
+ *
9
+ * In a lattice of declarative programs, the **weakness** of a statement
10
+ * `l` is the cardinality of its extension `|Z_l|` — the number of
11
+ * statements `l` is a sub-statement of. For an unknown parent task
12
+ * `ω` of a known child `α`, the probability of a model `h ∈ M_α`
13
+ * generalising to `ω` is
14
+ *
15
+ * p(h ∈ M_ω | h ∈ M_α, α ⊏ ω) = 2^|Z_{S_α} ∩ Z_h| / 2^|Z_{S_α}|
16
+ *
17
+ * which is monotonically increasing in `|Z_h|`. The weakest sufficient
18
+ * hypothesis maximises the probability of generalisation — and in
19
+ * Bennett's experiments (binary 8-bit add / mult) weakness generalised
20
+ * at 1.1×–5× the rate of MDL (Occam's Razor as length).
21
+ *
22
+ * "Explanations should be no more specific than necessary."
23
+ * — Bennett's Razor
24
+ *
25
+ * For a natural-language plan we can't compute `|Z_h|` exactly. This
26
+ * module provides three usable approximations, cheapest first:
27
+ *
28
+ * 1. `weaknessFromVerdict(text)` — heuristic string scan for
29
+ * "specificity markers" (e.g. "exactly", "must", "always",
30
+ * "guaranteed", "line N"). Catches gross over-claiming, free.
31
+ * 2. `measureSpecificity(text)` — caller-agnostic shape of an LLM
32
+ * meter prompt; the CLI uses {@link WEAKNESS_METER_PROMPT} plus a
33
+ * model call to get a principled specificity score.
34
+ * 3. `extensionSize` (if a caller computes it themselves) — feeds
35
+ * `rankByWeakness` directly.
36
+ *
37
+ * `rankByWeakness(candidates)` is the single ranking entry point; it
38
+ * combines the three into a normalised weakness score in `[0, 1]`.
39
+ *
40
+ * No CLI dependencies (see CORREZIONE-1 in the engine plan).
41
+ *
42
+ * @since v1.31.x — weakness-based hypothesis selection
43
+ */
44
+ import { z } from 'zod';
45
+ /**
46
+ * Bennett's Razor in one sentence, suitable for inclusion in system prompts.
47
+ *
48
+ * "Explanations should be no more specific than necessary." — Bennett 2023.
49
+ *
50
+ * The expanded form below spells out the operational meaning: when two
51
+ * solutions both satisfy a task, prefer the one that makes fewer specific
52
+ * claims about the world. It is deliberately phrased as a *tie-breaker*,
53
+ * not a goal in itself — a plan that is too weak to act on is still useless.
54
+ */
55
+ export declare const BENNETTS_RAZOR: string;
56
+ /**
57
+ * Short form — a single sentence, for inclusion in compact prompts.
58
+ */
59
+ export declare const BENNETTS_RAZOR_SHORT = "Prefer the solution that is no more specific than necessary.";
60
+ /**
61
+ * Compute a heuristic `specificity` score in `[0, 1]` for `text`.
62
+ *
63
+ * Returns `0` for an empty / whitespace-only string (a maximally weak claim).
64
+ * A score of `1` means "highly specific — many marker hits and/or many
65
+ * clauses"; a score of `0` means "no specificity signals at all".
66
+ */
67
+ export declare function weaknessFromVerdict(text: string | undefined | null): number;
68
+ /**
69
+ * Weakness = `1 - specificity`, in `[0, 1]`. The default tie-breaker
70
+ * when no `extensionSize` or LLM meter is available.
71
+ */
72
+ export declare function weaknessScoreFromText(text: string | undefined | null): number;
73
+ /**
74
+ * The meter prompt. A caller wraps this with their own model invocation
75
+ * (see `src/cli/kraken/weaknessMeter.ts`) and parses the JSON response.
76
+ *
77
+ * Kept as a string here (not a function) so it can be snapshotted in
78
+ * tests and re-used across providers without re-importing the CLI.
79
+ */
80
+ export declare const WEAKNESS_METER_PROMPT = "You are measuring the SPECIFICITY of a candidate solution to a software task.\n\nSpecificity means: how many specific commitments does this solution make that a more general plan would not have to make? Examples of specific commitments: exact file paths, exact line numbers, exact semver versions, exact function signatures, guarantees about runtime behaviour, assertions about what other agents/users will do.\n\nA maximally general solution is one that asserts nothing beyond the task itself (\"just do the task\"). A maximally specific solution is one that pins every possible value, path, and invariant.\n\nOutput ONLY a JSON object of the form:\n{\"specificity\": <float in [0,1]>, \"assumptions\": [<short string>, ...]}\n\nwhere\n- specificity = 0.0 \u2192 solution asserts nothing beyond the task\n- specificity = 1.0 \u2192 solution pins every value, path, version, and invariant\n- assumptions = the list of specific commitments you identified, each \u2264 12 words, deduped, sorted by strength (most specific first). Cap the list at 12.\n\nDo not add prose, do not add a code fence, do not explain your reasoning. JSON only.";
81
+ /**
82
+ * Zod schema for the meter response. Use this in the CLI to parse the
83
+ * model's output — gives type safety + a clear error path for malformed
84
+ * JSON, which is the most common failure mode of meter calls.
85
+ */
86
+ export declare const WeaknessMeterResponseSchema: z.ZodObject<{
87
+ specificity: z.ZodNumber;
88
+ assumptions: z.ZodArray<z.ZodString>;
89
+ }, z.core.$strip>;
90
+ export type WeaknessMeterResponse = z.infer<typeof WeaknessMeterResponseSchema>;
91
+ /**
92
+ * Convenience: turn a meter response into a weakness score in `[0, 1]`.
93
+ * Pure: no I/O. The meter itself is the only thing that costs.
94
+ */
95
+ export declare function weaknessFromMeter(meter: WeaknessMeterResponse): number;
96
+ /**
97
+ * Convenience: turn an `assumptions` list (from a meter response) into a
98
+ * heuristic *specificity* score. Used as a fallback when the meter returns
99
+ * a `specificity` outside `[0,1]` or one of the JSON fields is missing
100
+ * after parsing. Each assumption is treated as one specificity hit.
101
+ */
102
+ export declare function specificityFromAssumptions(assumptions: readonly string[]): number;
103
+ /**
104
+ * A hypothesis / plan / skill candidate. At least one of the weakness
105
+ * signals should be populated; `rankByWeakness` will use the strongest
106
+ * signal available.
107
+ */
108
+ export interface HypothesisCandidate {
109
+ /** Stable id (writer node id, skill name, plan hash, …). */
110
+ id: string;
111
+ /** The text we may scan heuristically if no other signal is set. */
112
+ text?: string;
113
+ /**
114
+ * Optional: caller's pre-computed extension size `|Z_h|`. When set,
115
+ * overrides everything else. Use this if you have a real lattice
116
+ * representation (Kraken's Spec Council does not, today).
117
+ */
118
+ extensionSize?: number;
119
+ /**
120
+ * Optional: result of {@link WeaknessMeterResponseSchema}.parsed.
121
+ * Overrides the heuristic scan.
122
+ */
123
+ meter?: WeaknessMeterResponse;
124
+ }
125
+ /** A candidate plus the score it received. */
126
+ export interface RankedHypothesis<T extends HypothesisCandidate = HypothesisCandidate> {
127
+ candidate: T;
128
+ /** Normalised weakness score in `[0, 1]`. Higher = weaker = more general. */
129
+ weaknessScore: number;
130
+ /** 1-based rank, weakest first. Ties get the same rank. */
131
+ rank: number;
132
+ /** Which signal drove the score (for debugging / auditing). */
133
+ source: 'extensionSize' | 'meter' | 'heuristic';
134
+ }
135
+ /**
136
+ * Rank candidates by weakness, weakest first. Stable: equal scores keep
137
+ * input order. The function is pure and synchronous; no LLM, no I/O.
138
+ *
139
+ * Strategy per candidate:
140
+ * 1. `extensionSize` (raw count) → normalised across the candidate set
141
+ * to `[0, 1]` by dividing by the max within the set.
142
+ * 2. `meter.specificity` → `1 - specificity` if present and valid.
143
+ * 3. `weaknessScoreFromText(candidate.text)` → heuristic.
144
+ * 4. `0` if nothing is set (the candidate is "as general as the empty claim").
145
+ */
146
+ export declare function rankByWeakness<T extends HypothesisCandidate>(candidates: readonly T[]): RankedHypothesis<T>[];
147
+ /**
148
+ * Pick the weakest candidate (rank 1) from a list. Convenience for the
149
+ * Spec Council's "all PASS, pick one" branch. Returns `undefined` if the
150
+ * input is empty.
151
+ */
152
+ export declare function pickWeakest<T extends HypothesisCandidate>(candidates: readonly T[]): T | undefined;
153
+ /**
154
+ * Filter candidates to those with weakness ≥ `threshold` (in `[0, 1]`).
155
+ * Useful for "among all PASS solutions, keep only the ones that are
156
+ * *enough* general" — e.g. drop a solution whose heuristic scan flags
157
+ * ≥ 4 specific markers.
158
+ */
159
+ export declare function filterByWeakness<T extends HypothesisCandidate>(candidates: readonly T[], threshold: number): T[];
160
+ //# sourceMappingURL=weakness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weakness.d.ts","sourceRoot":"","sources":["../../src/kraken/weakness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,QAMhB,CAAC;AAEZ;;GAEG;AACH,eAAO,MAAM,oBAAoB,iEAC+B,CAAC;AAkDjE;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAmB3E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAE7E;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,snCAcmD,CAAC;AAEtF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B;;;iBAGtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAEtE;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAOjF;AAMD;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,qBAAqB,CAAC;CAC/B;AAED,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,mBAAmB,GAAG,mBAAmB;IACnF,SAAS,EAAE,CAAC,CAAC;IACb,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,MAAM,EAAE,eAAe,GAAG,OAAO,GAAG,WAAW,CAAC;CACjD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,mBAAmB,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,EAAE,CA+D7G;AAyBD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,mBAAmB,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,GAAG,SAAS,CAGlG;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,mBAAmB,EAC5D,UAAU,EAAE,SAAS,CAAC,EAAE,EACxB,SAAS,EAAE,MAAM,GAChB,CAAC,EAAE,CAGL"}
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Kraken graph engine — weakness-based hypothesis ranking (Bennett 2023).
3
+ *
4
+ * "The Optimal Choice of Hypothesis Is the Weakest, Not the Shortest"
5
+ * Michael Timothy Bennett, AGI 2023 — arXiv:2301.12987v4.
6
+ *
7
+ * Bennett's formal result, restated for a coding-agent context:
8
+ *
9
+ * In a lattice of declarative programs, the **weakness** of a statement
10
+ * `l` is the cardinality of its extension `|Z_l|` — the number of
11
+ * statements `l` is a sub-statement of. For an unknown parent task
12
+ * `ω` of a known child `α`, the probability of a model `h ∈ M_α`
13
+ * generalising to `ω` is
14
+ *
15
+ * p(h ∈ M_ω | h ∈ M_α, α ⊏ ω) = 2^|Z_{S_α} ∩ Z_h| / 2^|Z_{S_α}|
16
+ *
17
+ * which is monotonically increasing in `|Z_h|`. The weakest sufficient
18
+ * hypothesis maximises the probability of generalisation — and in
19
+ * Bennett's experiments (binary 8-bit add / mult) weakness generalised
20
+ * at 1.1×–5× the rate of MDL (Occam's Razor as length).
21
+ *
22
+ * "Explanations should be no more specific than necessary."
23
+ * — Bennett's Razor
24
+ *
25
+ * For a natural-language plan we can't compute `|Z_h|` exactly. This
26
+ * module provides three usable approximations, cheapest first:
27
+ *
28
+ * 1. `weaknessFromVerdict(text)` — heuristic string scan for
29
+ * "specificity markers" (e.g. "exactly", "must", "always",
30
+ * "guaranteed", "line N"). Catches gross over-claiming, free.
31
+ * 2. `measureSpecificity(text)` — caller-agnostic shape of an LLM
32
+ * meter prompt; the CLI uses {@link WEAKNESS_METER_PROMPT} plus a
33
+ * model call to get a principled specificity score.
34
+ * 3. `extensionSize` (if a caller computes it themselves) — feeds
35
+ * `rankByWeakness` directly.
36
+ *
37
+ * `rankByWeakness(candidates)` is the single ranking entry point; it
38
+ * combines the three into a normalised weakness score in `[0, 1]`.
39
+ *
40
+ * No CLI dependencies (see CORREZIONE-1 in the engine plan).
41
+ *
42
+ * @since v1.31.x — weakness-based hypothesis selection
43
+ */
44
+ import { z } from 'zod';
45
+ // ---------------------------------------------------------------------------
46
+ // Bennett's Razor
47
+ // ---------------------------------------------------------------------------
48
+ /**
49
+ * Bennett's Razor in one sentence, suitable for inclusion in system prompts.
50
+ *
51
+ * "Explanations should be no more specific than necessary." — Bennett 2023.
52
+ *
53
+ * The expanded form below spells out the operational meaning: when two
54
+ * solutions both satisfy a task, prefer the one that makes fewer specific
55
+ * claims about the world. It is deliberately phrased as a *tie-breaker*,
56
+ * not a goal in itself — a plan that is too weak to act on is still useless.
57
+ */
58
+ export const BENNETTS_RAZOR = [
59
+ "Bennett's Razor (arXiv:2301.12987): explanations should be no more specific than necessary.",
60
+ 'When two solutions both satisfy the task, prefer the one that assumes the least.',
61
+ 'A claim is "more specific" when it pins down an exact value, path, version, or invariant that a more general plan would not have to commit to.',
62
+ 'Specificity is a tie-breaker, not a goal: a plan that is too vague to act on is still useless.',
63
+ 'Weakness ranking only applies among solutions that already meet the bar; do not weaken a passing plan below the bar in the name of weakness.',
64
+ ].join(' ');
65
+ /**
66
+ * Short form — a single sentence, for inclusion in compact prompts.
67
+ */
68
+ export const BENNETTS_RAZOR_SHORT = 'Prefer the solution that is no more specific than necessary.';
69
+ // ---------------------------------------------------------------------------
70
+ // Heuristic specificity scan (no LLM call, deterministic)
71
+ // ---------------------------------------------------------------------------
72
+ /**
73
+ * Lexical markers that correlate with a *specific* claim. Each match adds a
74
+ * small weight to {@link SPECIFICITY_MARKER_WEIGHT}; the sum is clamped to
75
+ * `[0, 1]` after the scan.
76
+ *
77
+ * The list is intentionally small and conservative: false positives in the
78
+ * "specific" direction (under-ranking a weak plan) are worse than false
79
+ * positives in the "general" direction, because a too-weak plan is just
80
+ * under-rendered in the workbench, while a too-strong one may be chosen
81
+ * over a better one.
82
+ */
83
+ const SPECIFICITY_MARKERS = [
84
+ /\bguarantee[ds]?\b/i,
85
+ /\bexact(?:ly)?\b/i,
86
+ /\bmust\b/i,
87
+ /\bshall\b/i,
88
+ /\balways\b/i,
89
+ /\bnever\b/i,
90
+ /\brequire[ds]?\b/i,
91
+ /\bmandatory\b/i,
92
+ /\bline\s+\d+/i, // "line 42"
93
+ /\bversion\s+[\d.]+/i, // "version 1.2.3"
94
+ /\bv?\d+\.\d+\.\d+\b/, // bare semver
95
+ /\b[0-9a-f]{7,40}\b/i, // git short SHA / commit hash
96
+ /\bthe\s+(?:file|path)\s+(?:is|at)\b/i,
97
+ /\bprecise(?:ly)?\b/i,
98
+ /\bassert(?:s|ed|ion)?\b/i,
99
+ /\bconfirm(?:s|ed)?\b/i,
100
+ /\bwill\s+(?:definitely|certainly|always)\b/i,
101
+ ];
102
+ /** Per-marker weight. Chosen so ~3 matches already saturate the score. */
103
+ const SPECIFICITY_MARKER_WEIGHT = 0.25;
104
+ /**
105
+ * Per-claim count penalty. Every *new sentence containing a verb* adds a
106
+ * tiny weight. This catches the "I will do A, then B, then C, then D"
107
+ * pattern that no individual marker hits but is clearly over-specified.
108
+ */
109
+ const SPECIFICITY_CLAUSE_WEIGHT = 0.05;
110
+ /** Maximum number of clause-penalty increments (caps runaway). */
111
+ const SPECIFICITY_CLAUSE_MAX = 6;
112
+ /**
113
+ * Compute a heuristic `specificity` score in `[0, 1]` for `text`.
114
+ *
115
+ * Returns `0` for an empty / whitespace-only string (a maximally weak claim).
116
+ * A score of `1` means "highly specific — many marker hits and/or many
117
+ * clauses"; a score of `0` means "no specificity signals at all".
118
+ */
119
+ export function weaknessFromVerdict(text) {
120
+ if (typeof text !== 'string')
121
+ return 0;
122
+ const trimmed = text.trim();
123
+ if (trimmed === '')
124
+ return 0;
125
+ let score = 0;
126
+ for (const re of SPECIFICITY_MARKERS) {
127
+ if (re.test(trimmed))
128
+ score += SPECIFICITY_MARKER_WEIGHT;
129
+ }
130
+ // Clause count: count sentences / semicolon-separated statements with a verb.
131
+ const clauses = trimmed
132
+ .split(/[.!?;]+|\n+/)
133
+ .map((c) => c.trim())
134
+ .filter((c) => c.length > 0 && /\b\w+ing\b|\b\w+ed\b|\bwill\b|\bcan\b|\bmust\b|\bshould\b|\bmay\b/i.test(c));
135
+ const clausePenalty = Math.min(clauses.length, SPECIFICITY_CLAUSE_MAX) * SPECIFICITY_CLAUSE_WEIGHT;
136
+ score += clausePenalty;
137
+ return clamp01(score);
138
+ }
139
+ /**
140
+ * Weakness = `1 - specificity`, in `[0, 1]`. The default tie-breaker
141
+ * when no `extensionSize` or LLM meter is available.
142
+ */
143
+ export function weaknessScoreFromText(text) {
144
+ return 1 - weaknessFromVerdict(text);
145
+ }
146
+ // ---------------------------------------------------------------------------
147
+ // LLM-as-weakness-meter
148
+ // ---------------------------------------------------------------------------
149
+ /**
150
+ * The meter prompt. A caller wraps this with their own model invocation
151
+ * (see `src/cli/kraken/weaknessMeter.ts`) and parses the JSON response.
152
+ *
153
+ * Kept as a string here (not a function) so it can be snapshotted in
154
+ * tests and re-used across providers without re-importing the CLI.
155
+ */
156
+ export const WEAKNESS_METER_PROMPT = `You are measuring the SPECIFICITY of a candidate solution to a software task.
157
+
158
+ Specificity means: how many specific commitments does this solution make that a more general plan would not have to make? Examples of specific commitments: exact file paths, exact line numbers, exact semver versions, exact function signatures, guarantees about runtime behaviour, assertions about what other agents/users will do.
159
+
160
+ A maximally general solution is one that asserts nothing beyond the task itself ("just do the task"). A maximally specific solution is one that pins every possible value, path, and invariant.
161
+
162
+ Output ONLY a JSON object of the form:
163
+ {"specificity": <float in [0,1]>, "assumptions": [<short string>, ...]}
164
+
165
+ where
166
+ - specificity = 0.0 → solution asserts nothing beyond the task
167
+ - specificity = 1.0 → solution pins every value, path, version, and invariant
168
+ - assumptions = the list of specific commitments you identified, each ≤ 12 words, deduped, sorted by strength (most specific first). Cap the list at 12.
169
+
170
+ Do not add prose, do not add a code fence, do not explain your reasoning. JSON only.`;
171
+ /**
172
+ * Zod schema for the meter response. Use this in the CLI to parse the
173
+ * model's output — gives type safety + a clear error path for malformed
174
+ * JSON, which is the most common failure mode of meter calls.
175
+ */
176
+ export const WeaknessMeterResponseSchema = z.object({
177
+ specificity: z.number().min(0).max(1),
178
+ assumptions: z.array(z.string().min(1).max(200)).max(12),
179
+ });
180
+ /**
181
+ * Convenience: turn a meter response into a weakness score in `[0, 1]`.
182
+ * Pure: no I/O. The meter itself is the only thing that costs.
183
+ */
184
+ export function weaknessFromMeter(meter) {
185
+ return clamp01(1 - clamp01(meter.specificity));
186
+ }
187
+ /**
188
+ * Convenience: turn an `assumptions` list (from a meter response) into a
189
+ * heuristic *specificity* score. Used as a fallback when the meter returns
190
+ * a `specificity` outside `[0,1]` or one of the JSON fields is missing
191
+ * after parsing. Each assumption is treated as one specificity hit.
192
+ */
193
+ export function specificityFromAssumptions(assumptions) {
194
+ if (!Array.isArray(assumptions) || assumptions.length === 0)
195
+ return 0;
196
+ // 1 assumption = low specificity; 6+ = saturate. Empirically: most
197
+ // reasonable solutions have 0–6 real assumptions; beyond that the
198
+ // plan is almost certainly over-specified.
199
+ const raw = Math.min(assumptions.length, 6) / 6;
200
+ return clamp01(raw);
201
+ }
202
+ /**
203
+ * Rank candidates by weakness, weakest first. Stable: equal scores keep
204
+ * input order. The function is pure and synchronous; no LLM, no I/O.
205
+ *
206
+ * Strategy per candidate:
207
+ * 1. `extensionSize` (raw count) → normalised across the candidate set
208
+ * to `[0, 1]` by dividing by the max within the set.
209
+ * 2. `meter.specificity` → `1 - specificity` if present and valid.
210
+ * 3. `weaknessScoreFromText(candidate.text)` → heuristic.
211
+ * 4. `0` if nothing is set (the candidate is "as general as the empty claim").
212
+ */
213
+ export function rankByWeakness(candidates) {
214
+ if (candidates.length === 0)
215
+ return [];
216
+ const raws = candidates.map((c) => computeRaw(c));
217
+ const extScores = raws.filter((r) => r.source === 'extensionSize').map((r) => r.raw);
218
+ const extMax = extScores.length > 0 ? Math.max(...extScores) : 1;
219
+ const extMin = extScores.length > 0 ? Math.min(...extScores) : 0;
220
+ const extRange = extMax - extMin;
221
+ const scored = raws.map((r) => {
222
+ let score;
223
+ switch (r.source) {
224
+ case 'extensionSize':
225
+ // Normalise within-set. If all extensionSize are equal, every
226
+ // candidate gets the same normalised score (the max), which
227
+ // preserves "they're all equally weak" → stable order.
228
+ score = extRange > 0 ? (r.raw - extMin) / extRange : 1;
229
+ break;
230
+ case 'meter':
231
+ score = clamp01(1 - clamp01(r.raw));
232
+ break;
233
+ case 'heuristic':
234
+ score = clamp01(r.raw);
235
+ break;
236
+ }
237
+ return { candidate: r.candidate, score, source: r.source };
238
+ });
239
+ // Pass 2: stable sort by score desc, then by input order.
240
+ const indexed = scored.map((s, i) => ({ ...s, originalIndex: i }));
241
+ indexed.sort((a, b) => {
242
+ if (b.score !== a.score)
243
+ return b.score - a.score;
244
+ return a.originalIndex - b.originalIndex;
245
+ });
246
+ // Pass 3: assign 1-based ranks with ties. Equal scores → same rank; the
247
+ // next distinct score jumps to reflect the count of earlier ties.
248
+ let lastScore;
249
+ let lastRank = 0;
250
+ let seen = 0;
251
+ return indexed.map((s) => {
252
+ seen += 1;
253
+ if (lastScore === undefined || s.score !== lastScore) {
254
+ lastRank = seen;
255
+ lastScore = s.score;
256
+ }
257
+ return {
258
+ candidate: s.candidate,
259
+ weaknessScore: s.score,
260
+ rank: lastRank,
261
+ source: s.source,
262
+ };
263
+ });
264
+ }
265
+ function computeRaw(c) {
266
+ if (typeof c.extensionSize === 'number' && Number.isFinite(c.extensionSize) && c.extensionSize >= 0) {
267
+ return { candidate: c, raw: c.extensionSize, source: 'extensionSize' };
268
+ }
269
+ if (c.meter && typeof c.meter.specificity === 'number' && Number.isFinite(c.meter.specificity)) {
270
+ return { candidate: c, raw: clamp01(c.meter.specificity), source: 'meter' };
271
+ }
272
+ return { candidate: c, raw: weaknessScoreFromText(c.text), source: 'heuristic' };
273
+ }
274
+ // ---------------------------------------------------------------------------
275
+ // Helpers
276
+ // ---------------------------------------------------------------------------
277
+ function clamp01(n) {
278
+ if (!Number.isFinite(n))
279
+ return 0;
280
+ if (n < 0)
281
+ return 0;
282
+ if (n > 1)
283
+ return 1;
284
+ return n;
285
+ }
286
+ /**
287
+ * Pick the weakest candidate (rank 1) from a list. Convenience for the
288
+ * Spec Council's "all PASS, pick one" branch. Returns `undefined` if the
289
+ * input is empty.
290
+ */
291
+ export function pickWeakest(candidates) {
292
+ const ranked = rankByWeakness(candidates);
293
+ return ranked.length > 0 ? ranked[0].candidate : undefined;
294
+ }
295
+ /**
296
+ * Filter candidates to those with weakness ≥ `threshold` (in `[0, 1]`).
297
+ * Useful for "among all PASS solutions, keep only the ones that are
298
+ * *enough* general" — e.g. drop a solution whose heuristic scan flags
299
+ * ≥ 4 specific markers.
300
+ */
301
+ export function filterByWeakness(candidates, threshold) {
302
+ const ranked = rankByWeakness(candidates);
303
+ return ranked.filter((r) => r.weaknessScore >= threshold).map((r) => r.candidate);
304
+ }
305
+ //# sourceMappingURL=weakness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weakness.js","sourceRoot":"","sources":["../../src/kraken/weakness.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zelari/core",
3
- "version": "1.28.0",
3
+ "version": "1.30.0",
4
4
  "description": "Zelari Code core library — provider-neutral agent loop (AgentHarness), multi-agent council orchestration, and built-in skills. Used by the zelari-code CLI; consumable by other agent frontends.",
5
5
  "author": "Anathema Studio <https://anathema-studio.com/>",
6
6
  "license": "MIT",