@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.
- package/dist/kraken/graph.d.ts +5 -2
- package/dist/kraken/graph.d.ts.map +1 -1
- package/dist/kraken/graph.js.map +1 -1
- package/dist/kraken/index.d.ts +11 -2
- package/dist/kraken/index.d.ts.map +1 -1
- package/dist/kraken/index.js +11 -2
- package/dist/kraken/index.js.map +1 -1
- package/dist/kraken/personas/conformance.d.ts +25 -0
- package/dist/kraken/personas/conformance.d.ts.map +1 -0
- package/dist/kraken/personas/conformance.js +89 -0
- package/dist/kraken/personas/conformance.js.map +1 -0
- package/dist/kraken/personas/index.d.ts +12 -0
- package/dist/kraken/personas/index.d.ts.map +1 -0
- package/dist/kraken/personas/index.js +12 -0
- package/dist/kraken/personas/index.js.map +1 -0
- package/dist/kraken/personas/registry.d.ts +62 -0
- package/dist/kraken/personas/registry.d.ts.map +1 -0
- package/dist/kraken/personas/registry.js +49 -0
- package/dist/kraken/personas/registry.js.map +1 -0
- package/dist/kraken/personas/specReviewer.d.ts +21 -0
- package/dist/kraken/personas/specReviewer.d.ts.map +1 -0
- package/dist/kraken/personas/specReviewer.js +74 -0
- package/dist/kraken/personas/specReviewer.js.map +1 -0
- package/dist/kraken/runtime/index.d.ts +13 -0
- package/dist/kraken/runtime/index.d.ts.map +1 -0
- package/dist/kraken/runtime/index.js +16 -0
- package/dist/kraken/runtime/index.js.map +1 -0
- package/dist/kraken/runtime/runner.d.ts +80 -0
- package/dist/kraken/runtime/runner.d.ts.map +1 -0
- package/dist/kraken/runtime/runner.js +253 -0
- package/dist/kraken/runtime/runner.js.map +1 -0
- package/dist/kraken/runtime/sandbox.d.ts +65 -0
- package/dist/kraken/runtime/sandbox.d.ts.map +1 -0
- package/dist/kraken/runtime/sandbox.js +158 -0
- package/dist/kraken/runtime/sandbox.js.map +1 -0
- package/dist/kraken/runtime/sdk.d.ts +32 -0
- package/dist/kraken/runtime/sdk.d.ts.map +1 -0
- package/dist/kraken/runtime/sdk.js +61 -0
- package/dist/kraken/runtime/sdk.js.map +1 -0
- package/dist/kraken/runtime/types.d.ts +220 -0
- package/dist/kraken/runtime/types.d.ts.map +1 -0
- package/dist/kraken/runtime/types.js +35 -0
- package/dist/kraken/runtime/types.js.map +1 -0
- package/dist/kraken/verdict.d.ts +118 -0
- package/dist/kraken/verdict.d.ts.map +1 -0
- package/dist/kraken/verdict.js +143 -0
- package/dist/kraken/verdict.js.map +1 -0
- package/dist/kraken/weakness.d.ts +160 -0
- package/dist/kraken/weakness.d.ts.map +1 -0
- package/dist/kraken/weakness.js +305 -0
- package/dist/kraken/weakness.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kraken script runtime — public types.
|
|
3
|
+
*
|
|
4
|
+
* A Kraken "script plan" is a TypeScript module the planner emits (or a user
|
|
5
|
+
* hands in) that imports the SDK from `@zelari/kraken-runtime` and calls
|
|
6
|
+
* `tentacle()`, `merge()`, `checkpoint()`, etc. The runtime compiles the
|
|
7
|
+
* module with esbuild (CLI side), loads it in a Node `vm` context with a
|
|
8
|
+
* capability-based sandbox, and runs it. Every call into the SDK is a
|
|
9
|
+
* bridge from the sandboxed script to the parent process — there is no
|
|
10
|
+
* direct filesystem, process, or network access in the script.
|
|
11
|
+
*
|
|
12
|
+
* Safety invariants (see F1.1 in `.zelari/docs/kraken-best-in-class-roadmap.md`):
|
|
13
|
+
* - The script cannot read `process.env` or `fs` outside the SDK.
|
|
14
|
+
* - Every `tentacle()` call increments a counter; the run aborts past
|
|
15
|
+
* `MAX_TENTACLES` (default 200) regardless of what the script does.
|
|
16
|
+
* - The script runs under a wall-clock budget (`ZELARI_KRAKEN_PLAN_TIMEOUT_MS`,
|
|
17
|
+
* default 30 minutes) enforced by the `vm` timeout.
|
|
18
|
+
* - `merge()` is one-shot per plan: a second call throws `PlanError` and
|
|
19
|
+
* the run is settled.
|
|
20
|
+
*
|
|
21
|
+
* @since Kraken v1.30.x — workflow script runtime (F1.1)
|
|
22
|
+
*/
|
|
23
|
+
import type { z } from 'zod';
|
|
24
|
+
import type { TaskNodeKind, TaskNodeStatus } from '../graph.js';
|
|
25
|
+
/** A node the script plans to spawn. Mirrors the JSON-DAG shape for parity. */
|
|
26
|
+
export interface TentacleOptions<T = unknown> {
|
|
27
|
+
/** What kind of subagent this is. */
|
|
28
|
+
kind: TaskNodeKind;
|
|
29
|
+
/** Short label for status / radio (max 200 chars). */
|
|
30
|
+
label: string;
|
|
31
|
+
/** Full self-contained prompt handed to the subagent. */
|
|
32
|
+
prompt: string;
|
|
33
|
+
/** Path/glob allowlist. Required for parallel writers. */
|
|
34
|
+
scope?: string[];
|
|
35
|
+
/** Acceptance checklist enforced by the verify tentacle. */
|
|
36
|
+
acceptance?: string[];
|
|
37
|
+
/** Upstream tentacle refs whose results are injected as context. */
|
|
38
|
+
deps?: TentacleRef[];
|
|
39
|
+
/**
|
|
40
|
+
* Optional Zod schema. If set, the subagent is asked to emit JSON matching
|
|
41
|
+
* the schema as its final message; the parsed result is exposed as
|
|
42
|
+
* `TentacleRef.result`. Use for structured hand-offs between tentacles.
|
|
43
|
+
*/
|
|
44
|
+
outputSchema?: z.ZodType<T>;
|
|
45
|
+
/** Tool-call budget. Default: `deep` for writers, `medium` for readers. */
|
|
46
|
+
thoroughness?: 'quick' | 'medium' | 'deep';
|
|
47
|
+
/** Model override. Default: routed by `krakenModel` per kind. */
|
|
48
|
+
model?: string;
|
|
49
|
+
/** Max retries before the node is left `error` (no fix-node spawn). */
|
|
50
|
+
maxRetries?: number;
|
|
51
|
+
/** Wall-clock cap (ms) for this single tentacle. 0 disables. */
|
|
52
|
+
maxRuntimeMs?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A reference to a tentacle the script spawned. The `result` is populated
|
|
56
|
+
* only when the subagent emitted a parseable JSON matching `outputSchema`;
|
|
57
|
+
* otherwise the raw `findings` text is the fallback.
|
|
58
|
+
*/
|
|
59
|
+
export interface TentacleRef<T = unknown> {
|
|
60
|
+
id: string;
|
|
61
|
+
kind: TaskNodeKind;
|
|
62
|
+
label: string;
|
|
63
|
+
status: TaskNodeStatus;
|
|
64
|
+
/** Parsed JSON if `outputSchema` was set and the subagent emitted it. */
|
|
65
|
+
result?: T;
|
|
66
|
+
/** Raw text conclusion (always populated, capped to `MAX_FINDINGS_CHARS`). */
|
|
67
|
+
findings: string;
|
|
68
|
+
/** Verdict trailer for `verify` / `spec` / `conformance` tentacles. */
|
|
69
|
+
verdict?: 'pass' | 'fail' | 'unknown';
|
|
70
|
+
scope?: string[];
|
|
71
|
+
durationMs?: number;
|
|
72
|
+
worktree?: string | null;
|
|
73
|
+
}
|
|
74
|
+
/** How a `merge()` call should land the worktrees in the parent HEAD. */
|
|
75
|
+
export type MergeStrategy = 'squash' | 'squash-sequential' | 'rebase' | 'manual';
|
|
76
|
+
export interface MergeOptions {
|
|
77
|
+
strategy?: MergeStrategy;
|
|
78
|
+
message?: string;
|
|
79
|
+
/** Delete the worktrees after a successful merge. Default: true. */
|
|
80
|
+
cleanup?: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** Per-tentacle outcome from the runtime's perspective. */
|
|
83
|
+
export interface MergeResult {
|
|
84
|
+
merged: string[];
|
|
85
|
+
conflicts: {
|
|
86
|
+
nodeId: string;
|
|
87
|
+
reason: string;
|
|
88
|
+
}[];
|
|
89
|
+
/** True when every input tentacle was merged without conflict. */
|
|
90
|
+
ok: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** Snapshot of the running plan, persisted as JSON. */
|
|
93
|
+
export interface PlanSnapshot {
|
|
94
|
+
graphId: string;
|
|
95
|
+
goal: string;
|
|
96
|
+
takenAt: string;
|
|
97
|
+
completed: TentacleRef[];
|
|
98
|
+
inFlight: TentacleRef[];
|
|
99
|
+
failed: TentacleRef[];
|
|
100
|
+
/** Tentacles the script already declared but not yet started. */
|
|
101
|
+
pending: TentacleRef[];
|
|
102
|
+
}
|
|
103
|
+
/** What the user (or a hook) can emit to the radio / workbench. */
|
|
104
|
+
export interface EmitPayload {
|
|
105
|
+
kind: string;
|
|
106
|
+
detail?: string;
|
|
107
|
+
data?: Record<string, unknown>;
|
|
108
|
+
}
|
|
109
|
+
/** Error categories the script runtime can raise. */
|
|
110
|
+
export type PlanErrorKind = 'budget_exceeded' | 'merge_already_done' | 'merge_conflict' | 'compile_error' | 'runtime_error' | 'sandbox_breach' | 'cancelled';
|
|
111
|
+
/** Structured error from the runtime; scripts should not throw raw values. */
|
|
112
|
+
export declare class PlanError extends Error {
|
|
113
|
+
readonly name = "PlanError";
|
|
114
|
+
readonly kind: PlanErrorKind;
|
|
115
|
+
readonly cause?: unknown;
|
|
116
|
+
constructor(kind: PlanErrorKind, message: string, cause?: unknown);
|
|
117
|
+
}
|
|
118
|
+
/** Capabilities injected into the script sandbox. Pure values + functions. */
|
|
119
|
+
export interface PlanCapabilities {
|
|
120
|
+
tentacle<T = unknown>(opts: TentacleOptions<T>): Promise<TentacleRef<T>>;
|
|
121
|
+
/** Wait for N tentacles to complete (parallel); returns the same refs typed. */
|
|
122
|
+
barrier<T extends readonly TentacleRef[]>(refs: T): Promise<{
|
|
123
|
+
-readonly [K in keyof T]: T[K];
|
|
124
|
+
}>;
|
|
125
|
+
/** Race N tentacles; first to complete wins, others are cancelled. */
|
|
126
|
+
race<T extends readonly TentacleRef[]>(refs: T): Promise<T[number]>;
|
|
127
|
+
/** Bounded loop helper. Throws PlanError('budget_exceeded') past `maxIter`. */
|
|
128
|
+
while_<T>(cond: () => boolean | Promise<boolean>, body: () => Promise<T>, maxIter: number): Promise<T[]>;
|
|
129
|
+
until<T>(cond: () => boolean | Promise<boolean>, body: () => Promise<T>, maxIter: number): Promise<T[]>;
|
|
130
|
+
/** Sequential merge of N worktrees into the parent HEAD. */
|
|
131
|
+
merge(refs: readonly TentacleRef[], opts?: MergeOptions): Promise<MergeResult>;
|
|
132
|
+
/** Snapshot the current plan state to `.zelari/kraken/snapshots/<id>.json`. */
|
|
133
|
+
checkpoint(label?: string): Promise<PlanSnapshot>;
|
|
134
|
+
/** Structured log line to the radio + workbench. */
|
|
135
|
+
log(msg: string, data?: Record<string, unknown>): void;
|
|
136
|
+
/** Emit a custom radio event for downstream tools. */
|
|
137
|
+
emit(payload: EmitPayload): void;
|
|
138
|
+
/** Read-only access to the plan-level context (graph id, goal, etc.). */
|
|
139
|
+
getContext(): PlanContext;
|
|
140
|
+
/** Cross-session message: enqueue a message for `peerId` (a TentacleRef). */
|
|
141
|
+
sendTo(peerId: string, payload: EmitPayload): void;
|
|
142
|
+
}
|
|
143
|
+
/** Read-only metadata exposed to the script via `getContext()`. */
|
|
144
|
+
export interface PlanContext {
|
|
145
|
+
graphId: string;
|
|
146
|
+
goal: string;
|
|
147
|
+
parentCwd: string;
|
|
148
|
+
sessionId: string;
|
|
149
|
+
/** Tentacles emitted so far, by id. */
|
|
150
|
+
tentacles: ReadonlyMap<string, TentacleRef>;
|
|
151
|
+
/** Tentacle cap (`ZELARI_KRAKEN_MAX_TENTACLES`, default 200). */
|
|
152
|
+
maxTentacles: number;
|
|
153
|
+
/** Wall-clock budget for the whole plan (ms). */
|
|
154
|
+
planTimeoutMs: number;
|
|
155
|
+
}
|
|
156
|
+
/** Bridge between the host (executor) and the script runtime. */
|
|
157
|
+
export interface PlanHostBridge {
|
|
158
|
+
/**
|
|
159
|
+
* Run one tentacle, returning the raw host result. The runner turns this
|
|
160
|
+
* into a `TentacleRef` and parses verdicts where appropriate.
|
|
161
|
+
*
|
|
162
|
+
* `worktree` is the worktree path the host created for this tentacle, or
|
|
163
|
+
* `null` if none (read-only kinds). The script runtime uses it to drive
|
|
164
|
+
* `merge()` later.
|
|
165
|
+
*/
|
|
166
|
+
runTentacle(args: {
|
|
167
|
+
node: TentacleOptions;
|
|
168
|
+
parentCwd: string;
|
|
169
|
+
sessionId: string;
|
|
170
|
+
}): Promise<HostTentacleResult>;
|
|
171
|
+
/** Merge N worktrees into the parent HEAD, sequentially. */
|
|
172
|
+
mergeWorktrees(args: {
|
|
173
|
+
refs: readonly TentacleRef[];
|
|
174
|
+
parentCwd: string;
|
|
175
|
+
strategy: MergeStrategy;
|
|
176
|
+
message?: string;
|
|
177
|
+
cleanup?: boolean;
|
|
178
|
+
}): Promise<MergeResult>;
|
|
179
|
+
/** Append a structured log line to the radio + workbench. */
|
|
180
|
+
log(line: string): void;
|
|
181
|
+
/** Persist a snapshot JSON to disk. */
|
|
182
|
+
saveSnapshot(snapshot: PlanSnapshot, dir: string): Promise<string>;
|
|
183
|
+
/** Read the abort signal; the script should respect it between awaits. */
|
|
184
|
+
signal(): AbortSignal | undefined;
|
|
185
|
+
}
|
|
186
|
+
/** Raw tentacle outcome from the host. The runner wraps this in a
|
|
187
|
+
* `TentacleRef` and stashes it for snapshot / merge. */
|
|
188
|
+
export interface HostTentacleResult {
|
|
189
|
+
ok: boolean;
|
|
190
|
+
/** Subagent's final text conclusion. Undefined on failure. */
|
|
191
|
+
result?: string;
|
|
192
|
+
/** Error message. Undefined on success. */
|
|
193
|
+
error?: string;
|
|
194
|
+
/** Wall-clock duration (ms). Optional; the host may not measure this. */
|
|
195
|
+
durationMs?: number;
|
|
196
|
+
/** Worktree path the host created, or null. */
|
|
197
|
+
worktree?: string | null;
|
|
198
|
+
}
|
|
199
|
+
/** What `runScriptPlan` returns to the executor — the same shape the JSON
|
|
200
|
+
* DAG path returns, so the executor doesn't need to special-case the two. */
|
|
201
|
+
export interface ScriptRunResult {
|
|
202
|
+
/** All tentacles that ran, keyed by id. */
|
|
203
|
+
tentacles: ReadonlyMap<string, TentacleRef>;
|
|
204
|
+
/** Number of `merge()` calls made. 0 = never merged, ≥1 = merged at least once. */
|
|
205
|
+
mergeCount: number;
|
|
206
|
+
/** Aggregate verdict surfaced to the workbench / digest. */
|
|
207
|
+
converged: boolean;
|
|
208
|
+
/** When the run stopped because the abort signal fired. */
|
|
209
|
+
cancelled: boolean;
|
|
210
|
+
/** Wall-clock duration of the whole plan (ms). */
|
|
211
|
+
durationMs: number;
|
|
212
|
+
/** Any unresolved findings (verify FAIL with rework budget spent, etc.). */
|
|
213
|
+
unresolvedFindings: {
|
|
214
|
+
nodeId: string;
|
|
215
|
+
label: string;
|
|
216
|
+
reason: string;
|
|
217
|
+
findings: string;
|
|
218
|
+
}[];
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/kraken/runtime/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kraken script runtime — public types.
|
|
3
|
+
*
|
|
4
|
+
* A Kraken "script plan" is a TypeScript module the planner emits (or a user
|
|
5
|
+
* hands in) that imports the SDK from `@zelari/kraken-runtime` and calls
|
|
6
|
+
* `tentacle()`, `merge()`, `checkpoint()`, etc. The runtime compiles the
|
|
7
|
+
* module with esbuild (CLI side), loads it in a Node `vm` context with a
|
|
8
|
+
* capability-based sandbox, and runs it. Every call into the SDK is a
|
|
9
|
+
* bridge from the sandboxed script to the parent process — there is no
|
|
10
|
+
* direct filesystem, process, or network access in the script.
|
|
11
|
+
*
|
|
12
|
+
* Safety invariants (see F1.1 in `.zelari/docs/kraken-best-in-class-roadmap.md`):
|
|
13
|
+
* - The script cannot read `process.env` or `fs` outside the SDK.
|
|
14
|
+
* - Every `tentacle()` call increments a counter; the run aborts past
|
|
15
|
+
* `MAX_TENTACLES` (default 200) regardless of what the script does.
|
|
16
|
+
* - The script runs under a wall-clock budget (`ZELARI_KRAKEN_PLAN_TIMEOUT_MS`,
|
|
17
|
+
* default 30 minutes) enforced by the `vm` timeout.
|
|
18
|
+
* - `merge()` is one-shot per plan: a second call throws `PlanError` and
|
|
19
|
+
* the run is settled.
|
|
20
|
+
*
|
|
21
|
+
* @since Kraken v1.30.x — workflow script runtime (F1.1)
|
|
22
|
+
*/
|
|
23
|
+
/** Structured error from the runtime; scripts should not throw raw values. */
|
|
24
|
+
export class PlanError extends Error {
|
|
25
|
+
name = 'PlanError';
|
|
26
|
+
kind;
|
|
27
|
+
cause;
|
|
28
|
+
constructor(kind, message, cause) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.kind = kind;
|
|
31
|
+
if (cause !== undefined)
|
|
32
|
+
this.cause = cause;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/kraken/runtime/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAuGH,8EAA8E;AAC9E,MAAM,OAAO,SAAU,SAAQ,KAAK;IAChB,IAAI,GAAG,WAAW,CAAC;IAC5B,IAAI,CAAgB;IACpB,KAAK,CAAW;IACzB,YAAY,IAAmB,EAAE,OAAe,EAAE,KAAe;QAC/D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAC9C,CAAC;CACF"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kraken graph engine — verify verdict parsing (pure).
|
|
3
|
+
*
|
|
4
|
+
* A `verify` node's job is to judge a writer's work, but a tentacle that runs
|
|
5
|
+
* to completion is a *successful run* regardless of what it concluded: the
|
|
6
|
+
* executor marked the node `done` and never read the text. A verify that said
|
|
7
|
+
* "this is wrong" was indistinguishable from one that said "this is correct",
|
|
8
|
+
* so the graph converged over known-bad work and the only iteration the engine
|
|
9
|
+
* could do was on execution failure, never on quality.
|
|
10
|
+
*
|
|
11
|
+
* This module turns that free text into a decision. The verify prompt asks for
|
|
12
|
+
* a `VERDICT: PASS` / `VERDICT: FAIL` trailer; the executor parses it and, on
|
|
13
|
+
* FAIL, spawns a bounded rework round.
|
|
14
|
+
*
|
|
15
|
+
* **Pillar 2 extension (v1.30.x)**: `spec` and `conformance` personas emit
|
|
16
|
+
* the same trailer PLUS a per-requirement table in a JSON code block. The
|
|
17
|
+
* parser extracts both: the trailer is the gate, the table is the
|
|
18
|
+
* structured findings the executor can surface in the digest and feed
|
|
19
|
+
* into a follow-up `fix` node.
|
|
20
|
+
*
|
|
21
|
+
* **v1.31.x (Bennett's Razor)**: each parsed verdict now also carries a
|
|
22
|
+
* `weaknessScore` in `[0, 1]`, computed from the persona's free text via
|
|
23
|
+
* `weaknessFromVerdict` (see `./weakness.js`). 1.0 = "maximally general
|
|
24
|
+
* claim" (weak), 0.0 = "maximally specific claim" (strong). The gate
|
|
25
|
+
* (PASS/FAIL) is unchanged — weakness is metadata, surfaced in the
|
|
26
|
+
* workbench digest so the user can see whether a PASS was earned by a
|
|
27
|
+
* tightly-asserted reviewer or a loosely-claimed one.
|
|
28
|
+
*
|
|
29
|
+
* No CLI dependencies (see CORREZIONE-1 in the engine plan).
|
|
30
|
+
*
|
|
31
|
+
* @since v1.28.x — verify quality gate
|
|
32
|
+
* @since v1.30.x — spec / conformance persona verdicts
|
|
33
|
+
* @since v1.31.x — weakness score on persona verdicts
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* What a verify node concluded.
|
|
37
|
+
*
|
|
38
|
+
* `unknown` means no trailer was found. It is deliberately distinct from
|
|
39
|
+
* `pass` so the caller can *report* the omission (a model that quietly stops
|
|
40
|
+
* emitting the trailer would otherwise disable the whole gate invisibly) while
|
|
41
|
+
* still treating it as non-blocking.
|
|
42
|
+
*/
|
|
43
|
+
export type VerifyVerdict = 'pass' | 'fail' | 'unknown';
|
|
44
|
+
export interface ParsedVerdict {
|
|
45
|
+
verdict: VerifyVerdict;
|
|
46
|
+
/**
|
|
47
|
+
* The verify's own reasoning — everything except the trailer line, trimmed
|
|
48
|
+
* and capped. Fed back into the rework node so it knows what to fix.
|
|
49
|
+
*/
|
|
50
|
+
findings: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Cap on retained findings text. Mirrors `MAX_UPSTREAM_CHARS_PER_DEP` in the
|
|
54
|
+
* CLI executor, since findings travel the same route (into a sub-agent prompt).
|
|
55
|
+
*/
|
|
56
|
+
export declare const MAX_FINDINGS_CHARS = 2800;
|
|
57
|
+
/**
|
|
58
|
+
* Extract a verify node's verdict from its conclusion text.
|
|
59
|
+
*
|
|
60
|
+
* The **last** trailer wins. Models routinely restate the instruction before
|
|
61
|
+
* answering ("...end with VERDICT: PASS or VERDICT: FAIL") and a first-match
|
|
62
|
+
* scan would read that echo as the answer — inverting the gate on exactly the
|
|
63
|
+
* verbose runs where the judgement matters most.
|
|
64
|
+
*/
|
|
65
|
+
export declare function parseVerifyVerdict(text: string | undefined | null): ParsedVerdict;
|
|
66
|
+
/**
|
|
67
|
+
* A verify verdict the run could not resolve, carried in the execution summary
|
|
68
|
+
* and rendered in the digest.
|
|
69
|
+
*
|
|
70
|
+
* Lives here (pure) rather than in the CLI executor so the executor and the
|
|
71
|
+
* status/digest renderer can share it without importing each other.
|
|
72
|
+
*/
|
|
73
|
+
export interface UnresolvedFinding {
|
|
74
|
+
/** The writer node whose work was judged. */
|
|
75
|
+
nodeId: string;
|
|
76
|
+
label: string;
|
|
77
|
+
/** 'fail' → rework budget exhausted; 'unknown' → no parseable verdict. */
|
|
78
|
+
reason: 'fail' | 'unknown';
|
|
79
|
+
findings: string;
|
|
80
|
+
}
|
|
81
|
+
/** One row of the spec / conformance per-requirement table. */
|
|
82
|
+
export interface RequirementVerdict {
|
|
83
|
+
requirement: string;
|
|
84
|
+
/** pass = satisfied, fail = violated, unknown = not assessed. */
|
|
85
|
+
met: 'pass' | 'fail' | 'unknown';
|
|
86
|
+
/** Path / line / output the reviewer cites as evidence. */
|
|
87
|
+
evidence?: string;
|
|
88
|
+
}
|
|
89
|
+
/** Structured verdict for `spec` and `conformance` personas.
|
|
90
|
+
*
|
|
91
|
+
* The trailer (`VERDICT: PASS|FAIL`) is the gate; the `requirements` table
|
|
92
|
+
* is the per-row reasoning the executor can surface and feed forward.
|
|
93
|
+
* `weaknessScore` (Bennett 2023) is metadata: 1.0 = the reviewer asserted
|
|
94
|
+
* very little (maximally weak / general), 0.0 = the reviewer pinned
|
|
95
|
+
* specific paths, versions, or invariants. It does NOT change the gate,
|
|
96
|
+
* but is surfaced in the workbench digest so a user can tell a tightly
|
|
97
|
+
* earned PASS from a loosely claimed one. */
|
|
98
|
+
export interface PersonaVerdict {
|
|
99
|
+
verdict: VerifyVerdict;
|
|
100
|
+
findings: string;
|
|
101
|
+
requirements: RequirementVerdict[];
|
|
102
|
+
/**
|
|
103
|
+
* Bennett-style weakness score in `[0, 1]`, computed from the persona's
|
|
104
|
+
* free text by `weaknessFromVerdict`. Higher = weaker = more general.
|
|
105
|
+
* `1` for the no-trailer case (a missing verdict asserts nothing) and
|
|
106
|
+
* for the all-empty case.
|
|
107
|
+
*
|
|
108
|
+
* @since v1.31.x
|
|
109
|
+
*/
|
|
110
|
+
weaknessScore: number;
|
|
111
|
+
}
|
|
112
|
+
/** Extract the per-requirement JSON block (if any) from a `spec` or
|
|
113
|
+
* `conformance` reply. The block is a ```json ... ``` fenced object
|
|
114
|
+
* with a `requirements: [...]` array. */
|
|
115
|
+
export declare function extractRequirementsBlock(text: string | undefined | null): RequirementVerdict[];
|
|
116
|
+
/** Full parser for a `spec` or `conformance` reply: trailer + table. */
|
|
117
|
+
export declare function parsePersonaVerdict(text: string | undefined | null): PersonaVerdict;
|
|
118
|
+
//# sourceMappingURL=verdict.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../../src/kraken/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAUvC;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,aAAa,CAsBjF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACjC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;8CAQ8C;AAC9C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC;;;;;;;OAOG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;0CAE0C;AAC1C,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,kBAAkB,EAAE,CAgC9F;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,cAAc,CAcnF"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kraken graph engine — verify verdict parsing (pure).
|
|
3
|
+
*
|
|
4
|
+
* A `verify` node's job is to judge a writer's work, but a tentacle that runs
|
|
5
|
+
* to completion is a *successful run* regardless of what it concluded: the
|
|
6
|
+
* executor marked the node `done` and never read the text. A verify that said
|
|
7
|
+
* "this is wrong" was indistinguishable from one that said "this is correct",
|
|
8
|
+
* so the graph converged over known-bad work and the only iteration the engine
|
|
9
|
+
* could do was on execution failure, never on quality.
|
|
10
|
+
*
|
|
11
|
+
* This module turns that free text into a decision. The verify prompt asks for
|
|
12
|
+
* a `VERDICT: PASS` / `VERDICT: FAIL` trailer; the executor parses it and, on
|
|
13
|
+
* FAIL, spawns a bounded rework round.
|
|
14
|
+
*
|
|
15
|
+
* **Pillar 2 extension (v1.30.x)**: `spec` and `conformance` personas emit
|
|
16
|
+
* the same trailer PLUS a per-requirement table in a JSON code block. The
|
|
17
|
+
* parser extracts both: the trailer is the gate, the table is the
|
|
18
|
+
* structured findings the executor can surface in the digest and feed
|
|
19
|
+
* into a follow-up `fix` node.
|
|
20
|
+
*
|
|
21
|
+
* **v1.31.x (Bennett's Razor)**: each parsed verdict now also carries a
|
|
22
|
+
* `weaknessScore` in `[0, 1]`, computed from the persona's free text via
|
|
23
|
+
* `weaknessFromVerdict` (see `./weakness.js`). 1.0 = "maximally general
|
|
24
|
+
* claim" (weak), 0.0 = "maximally specific claim" (strong). The gate
|
|
25
|
+
* (PASS/FAIL) is unchanged — weakness is metadata, surfaced in the
|
|
26
|
+
* workbench digest so the user can see whether a PASS was earned by a
|
|
27
|
+
* tightly-asserted reviewer or a loosely-claimed one.
|
|
28
|
+
*
|
|
29
|
+
* No CLI dependencies (see CORREZIONE-1 in the engine plan).
|
|
30
|
+
*
|
|
31
|
+
* @since v1.28.x — verify quality gate
|
|
32
|
+
* @since v1.30.x — spec / conformance persona verdicts
|
|
33
|
+
* @since v1.31.x — weakness score on persona verdicts
|
|
34
|
+
*/
|
|
35
|
+
import { weaknessScoreFromText } from './weakness.js';
|
|
36
|
+
/**
|
|
37
|
+
* Cap on retained findings text. Mirrors `MAX_UPSTREAM_CHARS_PER_DEP` in the
|
|
38
|
+
* CLI executor, since findings travel the same route (into a sub-agent prompt).
|
|
39
|
+
*/
|
|
40
|
+
export const MAX_FINDINGS_CHARS = 2800;
|
|
41
|
+
/**
|
|
42
|
+
* Matches a verdict trailer at the start of a line: `VERDICT: PASS`.
|
|
43
|
+
* Tolerates leading whitespace, markdown emphasis/bullets the model may wrap
|
|
44
|
+
* it in (`**VERDICT: FAIL**`, `- VERDICT: PASS`), and any trailing text on the
|
|
45
|
+
* line (a model that appends "— 3 gaps found" should still be understood).
|
|
46
|
+
*/
|
|
47
|
+
const VERDICT_LINE = /^[\s>*_-]*VERDICT[\s*_]*:[\s*_]*(PASS|FAIL)\b/gim;
|
|
48
|
+
/**
|
|
49
|
+
* Extract a verify node's verdict from its conclusion text.
|
|
50
|
+
*
|
|
51
|
+
* The **last** trailer wins. Models routinely restate the instruction before
|
|
52
|
+
* answering ("...end with VERDICT: PASS or VERDICT: FAIL") and a first-match
|
|
53
|
+
* scan would read that echo as the answer — inverting the gate on exactly the
|
|
54
|
+
* verbose runs where the judgement matters most.
|
|
55
|
+
*/
|
|
56
|
+
export function parseVerifyVerdict(text) {
|
|
57
|
+
const source = typeof text === 'string' ? text : '';
|
|
58
|
+
if (source.trim() === '')
|
|
59
|
+
return { verdict: 'unknown', findings: '' };
|
|
60
|
+
VERDICT_LINE.lastIndex = 0;
|
|
61
|
+
let match;
|
|
62
|
+
let last = null;
|
|
63
|
+
while ((match = VERDICT_LINE.exec(source)) !== null) {
|
|
64
|
+
last = match;
|
|
65
|
+
// A zero-length match would spin forever; the pattern cannot produce one
|
|
66
|
+
// (it requires the literal "VERDICT"), but guard anyway.
|
|
67
|
+
if (match.index === VERDICT_LINE.lastIndex)
|
|
68
|
+
VERDICT_LINE.lastIndex += 1;
|
|
69
|
+
}
|
|
70
|
+
if (!last) {
|
|
71
|
+
return { verdict: 'unknown', findings: capFindings(source) };
|
|
72
|
+
}
|
|
73
|
+
const verdict = last[1].toUpperCase() === 'FAIL' ? 'fail' : 'pass';
|
|
74
|
+
// Everything before the trailer is the reasoning that produced it.
|
|
75
|
+
const findings = capFindings(source.slice(0, last.index));
|
|
76
|
+
return { verdict, findings };
|
|
77
|
+
}
|
|
78
|
+
/** Extract the per-requirement JSON block (if any) from a `spec` or
|
|
79
|
+
* `conformance` reply. The block is a ```json ... ``` fenced object
|
|
80
|
+
* with a `requirements: [...]` array. */
|
|
81
|
+
export function extractRequirementsBlock(text) {
|
|
82
|
+
if (typeof text !== 'string' || text.trim() === '')
|
|
83
|
+
return [];
|
|
84
|
+
// Find the LAST ```json ... ``` block (LLMs sometimes emit the spec first,
|
|
85
|
+
// then the actual answer; we want the final one).
|
|
86
|
+
const re = /```json\s*([\s\S]*?)```/gi;
|
|
87
|
+
let m;
|
|
88
|
+
let last = null;
|
|
89
|
+
while ((m = re.exec(text)) !== null) {
|
|
90
|
+
last = m[1];
|
|
91
|
+
if (m.index === re.lastIndex)
|
|
92
|
+
re.lastIndex += 1;
|
|
93
|
+
}
|
|
94
|
+
if (!last)
|
|
95
|
+
return [];
|
|
96
|
+
try {
|
|
97
|
+
const obj = JSON.parse(last);
|
|
98
|
+
if (!obj || !Array.isArray(obj.requirements))
|
|
99
|
+
return [];
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const r of obj.requirements) {
|
|
102
|
+
if (!r || typeof r !== 'object')
|
|
103
|
+
continue;
|
|
104
|
+
const row = r;
|
|
105
|
+
const req = typeof row.requirement === 'string' ? row.requirement : '';
|
|
106
|
+
const metRaw = typeof row.met === 'string' ? row.met.toLowerCase() : '';
|
|
107
|
+
const met = metRaw === 'pass' || metRaw === 'true' ? 'pass'
|
|
108
|
+
: metRaw === 'fail' || metRaw === 'false' ? 'fail'
|
|
109
|
+
: 'unknown';
|
|
110
|
+
const evidence = typeof row.evidence === 'string' ? row.evidence : undefined;
|
|
111
|
+
if (req)
|
|
112
|
+
out.push({ requirement: req, met, ...(evidence ? { evidence } : {}) });
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Full parser for a `spec` or `conformance` reply: trailer + table. */
|
|
121
|
+
export function parsePersonaVerdict(text) {
|
|
122
|
+
const base = parseVerifyVerdict(text);
|
|
123
|
+
return {
|
|
124
|
+
verdict: base.verdict,
|
|
125
|
+
findings: base.findings,
|
|
126
|
+
requirements: extractRequirementsBlock(text),
|
|
127
|
+
// Bennett's weakness = "how little the reviewer's free text asserts"
|
|
128
|
+
// (arXiv:2301.12987). `weaknessScoreFromText` is the weakness form
|
|
129
|
+
// (1.0 = maximally general / no claims; 0.0 = maximally specific).
|
|
130
|
+
// The verdict gate is the trailer; weakness is metadata surfaced in
|
|
131
|
+
// the workbench so a user can see whether a PASS was earned by a
|
|
132
|
+
// tightly-asserted or loosely-claimed reviewer.
|
|
133
|
+
weaknessScore: weaknessScoreFromText(text),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** Trim and cap findings text, marking the cut so a reader knows it happened. */
|
|
137
|
+
function capFindings(raw) {
|
|
138
|
+
const trimmed = raw.trim();
|
|
139
|
+
if (trimmed.length <= MAX_FINDINGS_CHARS)
|
|
140
|
+
return trimmed;
|
|
141
|
+
return `${trimmed.slice(0, MAX_FINDINGS_CHARS)}\n… [truncated]`;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=verdict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict.js","sourceRoot":"","sources":["../../src/kraken/verdict.ts"],"names":[],"mappings":"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"}
|