jeopi-cli 16.2.30 → 16.4.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/CHANGELOG.md +32 -0
- package/dist/cli.js +2824 -2736
- package/dist/types/config/settings-schema.d.ts +32 -2
- package/dist/types/goals/runtime.d.ts +16 -0
- package/dist/types/goals/verifier.d.ts +9 -0
- package/dist/types/memories/index.d.ts +9 -0
- package/dist/types/memory-backend/types.d.ts +11 -0
- package/dist/types/session/agent-session.d.ts +22 -7
- package/dist/types/slash-commands/helpers/skill-verify.d.ts +5 -0
- package/dist/types/task/model-tier-classifier.d.ts +8 -0
- package/dist/types/tools/builtin-names.d.ts +1 -1
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/learn.d.ts +4 -0
- package/dist/types/tools/path-write-lock.d.ts +10 -0
- package/dist/types/tools/vision-verify.d.ts +34 -0
- package/package.json +12 -12
- package/src/autolearn/controller.ts +21 -5
- package/src/autoresearch/index.ts +60 -6
- package/src/config/settings-schema.ts +38 -2
- package/src/edit/index.ts +2 -1
- package/src/goals/runtime.ts +51 -0
- package/src/goals/verifier.ts +100 -0
- package/src/internal-urls/docs-index.generated.txt +2 -2
- package/src/memories/index.ts +10 -2
- package/src/memory-backend/local-backend.ts +34 -0
- package/src/memory-backend/types.ts +11 -0
- package/src/prompts/agents/designer.md +1 -0
- package/src/prompts/agents/goal-verifier.md +51 -0
- package/src/prompts/tools/learn.md +2 -0
- package/src/prompts/tools/vision-verify-system.md +16 -0
- package/src/prompts/tools/vision-verify.md +20 -0
- package/src/sdk.ts +2 -0
- package/src/session/agent-session.ts +51 -8
- package/src/slash-commands/builtin-registry.ts +9 -0
- package/src/slash-commands/helpers/skill-verify.ts +171 -0
- package/src/task/agents.ts +2 -0
- package/src/task/index.ts +26 -1
- package/src/task/model-tier-classifier.ts +43 -0
- package/src/tools/builtin-names.ts +1 -0
- package/src/tools/index.ts +4 -0
- package/src/tools/learn.ts +16 -1
- package/src/tools/path-write-lock.ts +44 -0
- package/src/tools/vision-verify.ts +347 -0
- package/src/tools/write.ts +12 -9
package/src/goals/runtime.ts
CHANGED
|
@@ -6,6 +6,13 @@ import { assertSubstantiveCompletionEvidence } from "./completion-evidence";
|
|
|
6
6
|
import { GoalEvidenceTracker, isVerificationSignal, MUTATION_TOOL_NAMES } from "./evidence-gate";
|
|
7
7
|
import type { Goal, GoalBudgetSteering, GoalModeState, GoalRuntimeEvent, GoalTokenUsage } from "./state";
|
|
8
8
|
|
|
9
|
+
export interface GoalCompletionVerdict {
|
|
10
|
+
verdict: "okay" | "iterate" | "reject";
|
|
11
|
+
justification: string;
|
|
12
|
+
summary?: string;
|
|
13
|
+
requiredFixes: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
export interface GoalRuntimeHost {
|
|
10
17
|
getState(): GoalModeState | undefined;
|
|
11
18
|
setState(state: GoalModeState | undefined): void;
|
|
@@ -17,6 +24,16 @@ export interface GoalRuntimeHost {
|
|
|
17
24
|
content: string;
|
|
18
25
|
deliverAs?: "steer" | "followUp" | "nextTurn";
|
|
19
26
|
}): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Spawn an independent grader sub-agent to verdict a completion claim against
|
|
29
|
+
* the objective and the actual repo diff (article gap: "the agent that wrote
|
|
30
|
+
* the code is not the agent that grades it"). The sub-agent sees only the
|
|
31
|
+
* objective + evidence text passed here plus its own repo inspection — never
|
|
32
|
+
* this session's transcript/reasoning. Optional: hosts that don't wire this
|
|
33
|
+
* (e.g. test harnesses, `goal.verifyCompletion` disabled) skip independent
|
|
34
|
+
* grading and fall back to the deterministic evidence gate alone.
|
|
35
|
+
*/
|
|
36
|
+
verifyCompletion?(objective: string, evidence: string): Promise<GoalCompletionVerdict>;
|
|
20
37
|
now?(): number;
|
|
21
38
|
}
|
|
22
39
|
|
|
@@ -122,6 +139,8 @@ export class GoalRuntime {
|
|
|
122
139
|
#turnSnapshot: GoalTurnSnapshot | undefined;
|
|
123
140
|
#wallClock: GoalWallClockSnapshot;
|
|
124
141
|
#budgetReportedFor: string | undefined;
|
|
142
|
+
/** Set when an independent-verifier non-okay verdict was bounced once; cleared by any new mutation/verification or a fresh goal. Mirrors GoalEvidenceTracker's bypass latch, kept separate because it gates a different (probabilistic) signal. */
|
|
143
|
+
#verifierBypassArmed = false;
|
|
125
144
|
#accountingTail: Promise<void> = Promise.resolve();
|
|
126
145
|
|
|
127
146
|
constructor(host: GoalRuntimeHost) {
|
|
@@ -219,10 +238,12 @@ export class GoalRuntime {
|
|
|
219
238
|
if (!this.#hasAccountingState()) return;
|
|
220
239
|
if (MUTATION_TOOL_NAMES[toolName]) {
|
|
221
240
|
this.#evidence.recordMutation();
|
|
241
|
+
this.#verifierBypassArmed = false;
|
|
222
242
|
return;
|
|
223
243
|
}
|
|
224
244
|
if (detail && isVerificationSignal(detail.command ?? "", detail.outputHead ?? "")) {
|
|
225
245
|
this.#evidence.recordVerification();
|
|
246
|
+
this.#verifierBypassArmed = false;
|
|
226
247
|
}
|
|
227
248
|
}
|
|
228
249
|
|
|
@@ -418,6 +439,7 @@ export class GoalRuntime {
|
|
|
418
439
|
const state = this.#createGoalState(objective, input.tokenBudget);
|
|
419
440
|
this.#budgetReportedFor = undefined;
|
|
420
441
|
this.#evidence.reset();
|
|
442
|
+
this.#verifierBypassArmed = false;
|
|
421
443
|
this.#markActiveAccounting(state.goal);
|
|
422
444
|
await this.#commitState(state, { persist: "goal" });
|
|
423
445
|
return state;
|
|
@@ -437,6 +459,7 @@ export class GoalRuntime {
|
|
|
437
459
|
const state = this.#createGoalState(objective, input.tokenBudget);
|
|
438
460
|
this.#budgetReportedFor = undefined;
|
|
439
461
|
this.#evidence.reset();
|
|
462
|
+
this.#verifierBypassArmed = false;
|
|
440
463
|
this.#markActiveAccounting(state.goal);
|
|
441
464
|
await this.#commitState(state, { persist: "goal" });
|
|
442
465
|
return state;
|
|
@@ -522,6 +545,34 @@ export class GoalRuntime {
|
|
|
522
545
|
if (gate.block) {
|
|
523
546
|
throw new Error(gate.message);
|
|
524
547
|
}
|
|
548
|
+
// Independent grader (goal-verifier sub-agent, see GoalRuntimeHost.verifyCompletion):
|
|
549
|
+
// maker != grader, per the compounding-loop primitive — the same agent that did
|
|
550
|
+
// the work does not get to be the sole judge of whether it's done. Same bounce-once
|
|
551
|
+
// shape as the deterministic gate above, kept as a separate latch since this signal
|
|
552
|
+
// is probabilistic (an LLM verdict), not mechanical.
|
|
553
|
+
if (this.#host.verifyCompletion) {
|
|
554
|
+
const verdict = await this.#host.verifyCompletion(state.goal.objective, verifiedEvidence);
|
|
555
|
+
if (verdict.verdict !== "okay") {
|
|
556
|
+
if (this.#verifierBypassArmed) {
|
|
557
|
+
// Second attempt with no new mutation/verification since the bounce:
|
|
558
|
+
// escape hatch for a verifier false-positive-reject/iterate.
|
|
559
|
+
this.#verifierBypassArmed = false;
|
|
560
|
+
} else {
|
|
561
|
+
this.#verifierBypassArmed = true;
|
|
562
|
+
const fixes =
|
|
563
|
+
verdict.requiredFixes.length > 0
|
|
564
|
+
? `\nRequired fixes:\n${verdict.requiredFixes.map(fix => `- ${fix}`).join("\n")}`
|
|
565
|
+
: "";
|
|
566
|
+
throw new Error(
|
|
567
|
+
`Completion blocked: independent verifier verdict was "${verdict.verdict}", not "okay". ` +
|
|
568
|
+
`${verdict.justification}${fixes}\nAddress the gaps above, then call complete again. ` +
|
|
569
|
+
"If the verifier is wrong, call complete again unchanged to override.",
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
} else {
|
|
573
|
+
this.#verifierBypassArmed = false;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
525
576
|
state.enabled = false;
|
|
526
577
|
state.goal.status = "complete";
|
|
527
578
|
state.goal.completionEvidence = verifiedEvidence;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Independent-verifier bridge for goal-mode completion (see GoalRuntimeHost.verifyCompletion
|
|
3
|
+
* in runtime.ts). Spawns the bundled `goal-verifier` sub-agent — a fresh session with its own
|
|
4
|
+
* kernel, its own read-only tool set, and no exposure to the maker's transcript/reasoning —
|
|
5
|
+
* to grade a completion claim against the objective and the actual repo diff. Reuses the eval
|
|
6
|
+
* tool's `agent()` bridge as a plain async call (no eval kernel required outside eval cells;
|
|
7
|
+
* `withBridgeTimeoutPause` in agent-bridge.ts is a no-op when no `emitStatus` sink is wired).
|
|
8
|
+
*/
|
|
9
|
+
import { runEvalAgent } from "../eval/agent-bridge";
|
|
10
|
+
import { getBundledAgent } from "../task/agents";
|
|
11
|
+
import type { ToolSession } from "../tools";
|
|
12
|
+
import type { GoalCompletionVerdict } from "./runtime";
|
|
13
|
+
|
|
14
|
+
const VERIFIER_AGENT_NAME = "goal-verifier";
|
|
15
|
+
|
|
16
|
+
/** Cap the evidence claim embedded in the verifier prompt; the verifier re-derives ground truth from the repo, not from a long claim. */
|
|
17
|
+
const MAX_EVIDENCE_CHARS = 4000;
|
|
18
|
+
|
|
19
|
+
function parseVerdict(output: string): GoalCompletionVerdict {
|
|
20
|
+
let data: unknown;
|
|
21
|
+
try {
|
|
22
|
+
data = JSON.parse(output);
|
|
23
|
+
} catch {
|
|
24
|
+
// Verifier didn't return a structured payload (refused, crashed, plain-text
|
|
25
|
+
// reply). Fail open to "iterate" rather than silently trusting completion —
|
|
26
|
+
// a broken verifier bounces once instead of being bypassed unnoticed.
|
|
27
|
+
return {
|
|
28
|
+
verdict: "iterate",
|
|
29
|
+
justification: `goal-verifier did not return a structured verdict. Raw output: ${output.slice(0, 500)}`,
|
|
30
|
+
requiredFixes: [],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (data === null || typeof data !== "object") {
|
|
34
|
+
return {
|
|
35
|
+
verdict: "iterate",
|
|
36
|
+
justification: "goal-verifier returned a non-object payload.",
|
|
37
|
+
requiredFixes: [],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const record = data as Record<string, unknown>;
|
|
41
|
+
const verdict = record.verdict;
|
|
42
|
+
if (verdict !== "okay" && verdict !== "iterate" && verdict !== "reject") {
|
|
43
|
+
return {
|
|
44
|
+
verdict: "iterate",
|
|
45
|
+
justification: "goal-verifier returned an unrecognized verdict value.",
|
|
46
|
+
requiredFixes: [],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const rawFixes = record.required_fixes;
|
|
50
|
+
const requiredFixes = Array.isArray(rawFixes)
|
|
51
|
+
? rawFixes.filter((fix): fix is string => typeof fix === "string")
|
|
52
|
+
: [];
|
|
53
|
+
const justification = typeof record.justification === "string" ? record.justification : "";
|
|
54
|
+
const summary = typeof record.summary === "string" ? record.summary : undefined;
|
|
55
|
+
return { verdict, justification, summary, requiredFixes };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Grade a goal-completion claim with the independent `goal-verifier` sub-agent.
|
|
60
|
+
* Never throws: a spawn failure (depth limit, disabled agent, no model) fails
|
|
61
|
+
* open to `iterate` so it costs one corrective bounce, the same as a genuine
|
|
62
|
+
* gap — never a silent skip of independent grading, never a permanent block.
|
|
63
|
+
*/
|
|
64
|
+
export async function verifyGoalCompletion(
|
|
65
|
+
session: ToolSession,
|
|
66
|
+
objective: string,
|
|
67
|
+
evidence: string,
|
|
68
|
+
): Promise<GoalCompletionVerdict> {
|
|
69
|
+
const truncatedEvidence =
|
|
70
|
+
evidence.length > MAX_EVIDENCE_CHARS ? `${evidence.slice(0, MAX_EVIDENCE_CHARS)}\n[truncated]` : evidence;
|
|
71
|
+
const verifierPrompt =
|
|
72
|
+
`<objective>\n${objective}\n</objective>\n\n` +
|
|
73
|
+
`<evidence>\n${truncatedEvidence}\n</evidence>\n\n` +
|
|
74
|
+
"Verdict whether the objective above is genuinely satisfied by the repo's current state. " +
|
|
75
|
+
"The evidence is the maker's claim, not a fact — verify it independently.";
|
|
76
|
+
try {
|
|
77
|
+
// Explicit schema, not the agent's own frontmatter default: runEvalAgent (the
|
|
78
|
+
// eval `agent()` bridge) only applies `outputSchema` when the caller supplies
|
|
79
|
+
// one — unlike the `task` tool, it never falls back to `effectiveAgent.output`
|
|
80
|
+
// (task/index.ts). Read the bundled definition's own `output` so this can't
|
|
81
|
+
// drift from goal-verifier.md's declared shape. Omit the key entirely when
|
|
82
|
+
// undefined: `Object.hasOwn` (agent-bridge.ts's `structured` check) is true
|
|
83
|
+
// for an explicit `schema: undefined`, which would wrongly disable the
|
|
84
|
+
// agent's frontmatter-schema fallback in the bridge.
|
|
85
|
+
const schema = getBundledAgent(VERIFIER_AGENT_NAME)?.output;
|
|
86
|
+
const args =
|
|
87
|
+
schema === undefined
|
|
88
|
+
? { prompt: verifierPrompt, agent: VERIFIER_AGENT_NAME }
|
|
89
|
+
: { prompt: verifierPrompt, agent: VERIFIER_AGENT_NAME, schema };
|
|
90
|
+
const result = await runEvalAgent(args, { session });
|
|
91
|
+
return parseVerdict(result.text);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
94
|
+
return {
|
|
95
|
+
verdict: "iterate",
|
|
96
|
+
justification: `Independent verifier could not run: ${message}`,
|
|
97
|
+
requiredFixes: [],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|