zelari-code 1.48.1 → 1.49.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/desktopConfig.js +51 -4
- package/dist/cli/desktopConfig.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +80 -0
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/kraken/candidateRegistry.js +195 -0
- package/dist/cli/kraken/candidateRegistry.js.map +1 -0
- package/dist/cli/kraken/candidateRegistry.test.js +211 -0
- package/dist/cli/kraken/candidateRegistry.test.js.map +1 -0
- package/dist/cli/kraken/completionGate.js +116 -0
- package/dist/cli/kraken/completionGate.js.map +1 -0
- package/dist/cli/kraken/completionGate.test.js +172 -0
- package/dist/cli/kraken/completionGate.test.js.map +1 -0
- package/dist/cli/kraken/metrics.js +96 -0
- package/dist/cli/kraken/metrics.js.map +1 -0
- package/dist/cli/kraken/metrics.test.js +145 -0
- package/dist/cli/kraken/metrics.test.js.map +1 -0
- package/dist/cli/kraken/selectionPlaybook.js +23 -0
- package/dist/cli/kraken/selectionPlaybook.js.map +1 -0
- package/dist/cli/kraken/selectionPlaybook.test.js +55 -0
- package/dist/cli/kraken/selectionPlaybook.test.js.map +1 -0
- package/dist/cli/kraken/turnRuntime.js +207 -0
- package/dist/cli/kraken/turnRuntime.js.map +1 -0
- package/dist/cli/kraken/turnRuntime.test.js +377 -0
- package/dist/cli/kraken/turnRuntime.test.js.map +1 -0
- package/dist/cli/kraken/verifier.js +233 -0
- package/dist/cli/kraken/verifier.js.map +1 -0
- package/dist/cli/kraken/verifier.test.js +232 -0
- package/dist/cli/kraken/verifier.test.js.map +1 -0
- package/dist/cli/kraken/verifierSettings.test.js +237 -0
- package/dist/cli/kraken/verifierSettings.test.js.map +1 -0
- package/dist/cli/kraken/verifyReport.js +115 -0
- package/dist/cli/kraken/verifyReport.js.map +1 -0
- package/dist/cli/kraken/verifyReport.test.js +111 -0
- package/dist/cli/kraken/verifyReport.test.js.map +1 -0
- package/dist/cli/main.bundled.js +1343 -107
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/providerConfig.js +46 -0
- package/dist/cli/providerConfig.js.map +1 -1
- package/dist/cli/runHeadless.js +85 -0
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/toolRegistry.js +50 -6
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/krakenSelectTool.js +165 -0
- package/dist/cli/tools/krakenSelectTool.js.map +1 -0
- package/dist/cli/tools/krakenSelectTool.test.js +246 -0
- package/dist/cli/tools/krakenSelectTool.test.js.map +1 -0
- package/dist/cli/tools/taskTool.dynamicChecks.test.js +156 -0
- package/dist/cli/tools/taskTool.dynamicChecks.test.js.map +1 -0
- package/dist/cli/tools/taskTool.js +174 -6
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/dist/cli/tools/taskTool.planSafety.test.js +280 -0
- package/dist/cli/tools/taskTool.planSafety.test.js.map +1 -0
- package/dist/cli/tools/taskTool.verifyReport.test.js +140 -0
- package/dist/cli/tools/taskTool.verifyReport.test.js.map +1 -0
- package/package.json +1 -1
package/dist/cli/main.bundled.js
CHANGED
|
@@ -1473,27 +1473,27 @@ function readStore() {
|
|
|
1473
1473
|
}
|
|
1474
1474
|
return { providers: {} };
|
|
1475
1475
|
}
|
|
1476
|
-
function writeStore(
|
|
1476
|
+
function writeStore(store6) {
|
|
1477
1477
|
const file2 = getKeyStorePath();
|
|
1478
1478
|
mkdirSync2(path3.dirname(file2), { recursive: true });
|
|
1479
|
-
writeFileSync2(file2, JSON.stringify(
|
|
1479
|
+
writeFileSync2(file2, JSON.stringify(store6, null, 2), { encoding: "utf-8", mode: 384 });
|
|
1480
1480
|
}
|
|
1481
1481
|
function setApiKey(providerId, key) {
|
|
1482
|
-
const
|
|
1483
|
-
|
|
1484
|
-
writeStore(
|
|
1482
|
+
const store6 = readStore();
|
|
1483
|
+
store6.providers[providerId] = { apiKey: key };
|
|
1484
|
+
writeStore(store6);
|
|
1485
1485
|
}
|
|
1486
1486
|
function clearApiKey(providerId) {
|
|
1487
|
-
const
|
|
1488
|
-
delete
|
|
1489
|
-
writeStore(
|
|
1487
|
+
const store6 = readStore();
|
|
1488
|
+
delete store6.providers[providerId];
|
|
1489
|
+
writeStore(store6);
|
|
1490
1490
|
}
|
|
1491
1491
|
function getStoredApiKey(providerId) {
|
|
1492
|
-
const
|
|
1493
|
-
return
|
|
1492
|
+
const store6 = readStore();
|
|
1493
|
+
return store6.providers[providerId]?.apiKey ?? null;
|
|
1494
1494
|
}
|
|
1495
1495
|
function setOAuthToken(providerId, token) {
|
|
1496
|
-
const
|
|
1496
|
+
const store6 = readStore();
|
|
1497
1497
|
const entry = { apiKey: token.apiKey };
|
|
1498
1498
|
if (typeof token.expiresAt === "number" && Number.isFinite(token.expiresAt)) {
|
|
1499
1499
|
entry.expiresAt = token.expiresAt;
|
|
@@ -1507,12 +1507,12 @@ function setOAuthToken(providerId, token) {
|
|
|
1507
1507
|
if (typeof token.idToken === "string" && token.idToken.length > 0) {
|
|
1508
1508
|
entry.idToken = token.idToken;
|
|
1509
1509
|
}
|
|
1510
|
-
|
|
1511
|
-
writeStore(
|
|
1510
|
+
store6.providers[providerId] = entry;
|
|
1511
|
+
writeStore(store6);
|
|
1512
1512
|
}
|
|
1513
1513
|
function getOAuthToken(providerId) {
|
|
1514
|
-
const
|
|
1515
|
-
return
|
|
1514
|
+
const store6 = readStore();
|
|
1515
|
+
return store6.providers[providerId] ?? null;
|
|
1516
1516
|
}
|
|
1517
1517
|
function resolveApiKey(providerId) {
|
|
1518
1518
|
const spec = getProviderSpec(providerId);
|
|
@@ -1571,8 +1571,8 @@ async function forceRefreshOAuth(providerId, options = {}) {
|
|
|
1571
1571
|
function readStoreDirect() {
|
|
1572
1572
|
return readStore();
|
|
1573
1573
|
}
|
|
1574
|
-
function writeStoreDirect(
|
|
1575
|
-
writeStore(
|
|
1574
|
+
function writeStoreDirect(store6) {
|
|
1575
|
+
writeStore(store6);
|
|
1576
1576
|
}
|
|
1577
1577
|
function maskKey(key) {
|
|
1578
1578
|
if (key.length <= 12) return "****";
|
|
@@ -1917,9 +1917,11 @@ var init_thinking = __esm({
|
|
|
1917
1917
|
var providerConfig_exports = {};
|
|
1918
1918
|
__export(providerConfig_exports, {
|
|
1919
1919
|
clearCustomEndpoint: () => clearCustomEndpoint,
|
|
1920
|
+
clearKrakenVerifier: () => clearKrakenVerifier,
|
|
1920
1921
|
getActiveModel: () => getActiveModel,
|
|
1921
1922
|
getActiveProvider: () => getActiveProvider,
|
|
1922
1923
|
getCustomEndpoint: () => getCustomEndpoint,
|
|
1924
|
+
getKrakenVerifierOverride: () => getKrakenVerifierOverride,
|
|
1923
1925
|
getModelForProvider: () => getModelForProvider,
|
|
1924
1926
|
getProviderConfig: () => getProviderConfig,
|
|
1925
1927
|
getProviderConfigPath: () => getProviderConfigPath,
|
|
@@ -1927,6 +1929,7 @@ __export(providerConfig_exports, {
|
|
|
1927
1929
|
loadProviderConfig: () => loadProviderConfig,
|
|
1928
1930
|
setActiveProviderId: () => setActiveProviderId,
|
|
1929
1931
|
setCustomEndpoint: () => setCustomEndpoint,
|
|
1932
|
+
setKrakenVerifier: () => setKrakenVerifier,
|
|
1930
1933
|
setModelForProvider: () => setModelForProvider,
|
|
1931
1934
|
setThinkingForProvider: () => setThinkingForProvider
|
|
1932
1935
|
});
|
|
@@ -1950,7 +1953,8 @@ function getProviderConfig() {
|
|
|
1950
1953
|
activeProviderId: parsed.activeProviderId,
|
|
1951
1954
|
modelByProvider: { ...DEFAULTS.modelByProvider, ...parsed.modelByProvider },
|
|
1952
1955
|
thinkingByProvider: { ...DEFAULTS.thinkingByProvider, ...parsed.thinkingByProvider },
|
|
1953
|
-
customEndpoints: mergeCustomEndpoints(parsed.customEndpoints)
|
|
1956
|
+
customEndpoints: mergeCustomEndpoints(parsed.customEndpoints),
|
|
1957
|
+
krakenVerifier: mergeKrakenVerifier(parsed.krakenVerifier)
|
|
1954
1958
|
};
|
|
1955
1959
|
}
|
|
1956
1960
|
} catch {
|
|
@@ -2019,6 +2023,35 @@ function clearCustomEndpoint(id) {
|
|
|
2019
2023
|
delete config2.customEndpoints[id];
|
|
2020
2024
|
writeProviderConfig(config2);
|
|
2021
2025
|
}
|
|
2026
|
+
function mergeKrakenVerifier(raw) {
|
|
2027
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
2028
|
+
const provider = typeof raw.provider === "string" ? raw.provider.trim() : "";
|
|
2029
|
+
const model = typeof raw.model === "string" ? raw.model.trim() : "";
|
|
2030
|
+
if (!provider || !model) return void 0;
|
|
2031
|
+
if (!PROVIDERS.some((p3) => p3.id === provider)) return void 0;
|
|
2032
|
+
return { provider, model };
|
|
2033
|
+
}
|
|
2034
|
+
function getKrakenVerifierOverride() {
|
|
2035
|
+
return getProviderConfig().krakenVerifier;
|
|
2036
|
+
}
|
|
2037
|
+
function setKrakenVerifier(provider, model) {
|
|
2038
|
+
const spec = PROVIDERS.find((p3) => p3.id === provider);
|
|
2039
|
+
if (!spec) {
|
|
2040
|
+
throw new Error(`Unknown provider id: "${provider}". Available: ${PROVIDERS.map((p3) => p3.id).join(", ")}`);
|
|
2041
|
+
}
|
|
2042
|
+
if (!model || model.trim().length === 0) {
|
|
2043
|
+
throw new Error("Verifier model cannot be empty. Use clearKrakenVerifier() to inherit.");
|
|
2044
|
+
}
|
|
2045
|
+
const config2 = getProviderConfig();
|
|
2046
|
+
config2.krakenVerifier = { provider, model: model.trim() };
|
|
2047
|
+
writeProviderConfig(config2);
|
|
2048
|
+
}
|
|
2049
|
+
function clearKrakenVerifier() {
|
|
2050
|
+
const config2 = getProviderConfig();
|
|
2051
|
+
if (!config2.krakenVerifier) return;
|
|
2052
|
+
delete config2.krakenVerifier;
|
|
2053
|
+
writeProviderConfig(config2);
|
|
2054
|
+
}
|
|
2022
2055
|
function setActiveProviderId(id) {
|
|
2023
2056
|
const spec = PROVIDERS.find((p3) => p3.id === id);
|
|
2024
2057
|
if (!spec) {
|
|
@@ -20258,7 +20291,7 @@ function getBasePromptModules(mode = "council") {
|
|
|
20258
20291
|
function getPromptModule(type) {
|
|
20259
20292
|
return getBasePromptModules("council").find((m) => m.type === type);
|
|
20260
20293
|
}
|
|
20261
|
-
var CODING_CAPABLE_IDENTITY, COUNCIL_IDENTITY, BEHAVIOR_AGENT, BEHAVIOR_COUNCIL, SAFETY, CONTEXT_SHARING_COUNCIL, OUTPUT_FORMATTING, NATIVE_TOOL_PROTOCOL_MODULE, CODING_PRACTICES_MODULE, TURN_COMPLETION_MODULE, CLARIFICATION_PROTOCOL_MODULE, PROMPT_MODULES, KRAKEN_IDENTITY_MODULE, KRAKEN_LEAD_PLAYBOOK_MODULE, SINGLE_AGENT_IDENTITY_MODULE;
|
|
20294
|
+
var CODING_CAPABLE_IDENTITY, COUNCIL_IDENTITY, BEHAVIOR_AGENT, BEHAVIOR_COUNCIL, SAFETY, CONTEXT_SHARING_COUNCIL, OUTPUT_FORMATTING, NATIVE_TOOL_PROTOCOL_MODULE, CODING_PRACTICES_MODULE, TURN_COMPLETION_MODULE, CLARIFICATION_PROTOCOL_MODULE, PROMPT_MODULES, KRAKEN_IDENTITY_MODULE, KRAKEN_LEAD_PLAYBOOK_MODULE, KRAKEN_SELECTION_PLAYBOOK_MODULE, SINGLE_AGENT_IDENTITY_MODULE;
|
|
20262
20295
|
var init_promptModules = __esm({
|
|
20263
20296
|
"packages/core/dist/agents/promptModules.js"() {
|
|
20264
20297
|
"use strict";
|
|
@@ -20456,6 +20489,32 @@ Rules:
|
|
|
20456
20489
|
priority: 25,
|
|
20457
20490
|
content: "# Kraken Lead Playbook (super-agent)\n\nYou are the **parent brain**. Sub-agents spawned with task are tentacles: they cannot see this chat and cannot nest further task calls.\n\n## Default workflow (non-trivial work)\n1. **Orient** - list/read key files; optionally task explore (parallel OK for disjoint questions).\n2. **Decompose** - todo_write with concrete slices and acceptance criteria.\n3. **Implement** - one slice at a time via tools or task agent=general for a bounded unit.\n4. **Verify** - after meaningful writes, run checks yourself (bash / typecheck / tests) or task agent=verify. Do not claim done without on-disk evidence.\n5. **Integrate** - summarize files touched + how to verify; if more remains, checkpoint and ask.\n\n## When to spawn task\n- **explore**: unfamiliar area, multi-file search, map call sites (prefer parallel explores).\n- **general**: isolated implement slice with clear path scope (serial writers unless worktree isolation is on).\n- **verify**: post-implement gate (tests/typecheck/smoke).\n\n## Task contracts (required quality)\nEvery task prompt must be self-contained and include:\n- **Goal** (one sentence)\n- **Scope** (paths / symbols allowed; what is out of scope)\n- **Acceptance** (how the parent will know it succeeded)\n- **Constraints** (no drive-by refactors; match existing style)\n\nOptional tool fields: scope (path allowlist hint), acceptance (checklist). Prefer them when available.\n\n## Caps and discipline\n- Prefer at most 4 explore and 2 general spawns per user turn unless the user asks for more.\n- Do not expand scope beyond the user request.\n- Parallel: many explore OK; general writers stay serial unless ZELARI_KRAKEN_WORKTREE=1.\n- Nested task from children is disabled - you are the only orchestrator.\n- Cheap thoroughness defaults: explore=quick|medium; deep only when stuck.\n- Model routing: explore/verify may use ZELARI_KRAKEN_SUB_MODEL (cheaper); general stays on parent model unless ZELARI_KRAKEN_GENERAL_MODEL is set.\n- After every successful task general, spawn task verify (or run tests yourself) before claiming done - the tool appends a verify-hint footer.\n- Opt-in isolation: ZELARI_KRAKEN_WORKTREE=1 runs general tentacles in a git worktree under .zelari/worktrees/ (KEEP=1 to retain branch for manual merge).\n- Progress bus: tentacle spawns log to .zelari/radio/<session>.jsonl - slash command /kraken shows status.\n\n## Done means verified\nNever end with status theater. Either tools ran and files changed, or you stop with a short report and ask whether to continue."
|
|
20458
20491
|
};
|
|
20492
|
+
KRAKEN_SELECTION_PLAYBOOK_MODULE = {
|
|
20493
|
+
type: "behavior-rules",
|
|
20494
|
+
title: "Kraken Verified Selection (alpha)",
|
|
20495
|
+
// priority 26 = right after the lead playbook (25); +1000 via custom modules.
|
|
20496
|
+
priority: 26,
|
|
20497
|
+
content: [
|
|
20498
|
+
"# Kraken Verified Selection (alpha)",
|
|
20499
|
+
"",
|
|
20500
|
+
"You can explore competing hypotheses before committing to one implementation path.",
|
|
20501
|
+
"The runtime registers candidates, preserves their evidence verbatim, and judges them via the kraken_select tool.",
|
|
20502
|
+
"",
|
|
20503
|
+
"## When to explore candidates",
|
|
20504
|
+
"- **Simple task** (rename, typo fix, small requested edit, single obvious change): go DIRECT. No candidates, no kraken_select.",
|
|
20505
|
+
"- **Ambiguous task** (two or more plausible root causes or designs): spawn 2 candidates.",
|
|
20506
|
+
"- **High uncertainty** (intermittent bug, race condition, architecture decision with trade-offs): spawn up to 3 candidates.",
|
|
20507
|
+
"",
|
|
20508
|
+
"## Rules",
|
|
20509
|
+
'- Spawn candidates with the task tool using purpose="candidate" - explore-only tentacles; they never write.',
|
|
20510
|
+
"- Each candidate must test a DIFFERENT normalized hypothesis. If two candidates would test the same theory, keep one.",
|
|
20511
|
+
"- Wait for all candidate reports, then call kraken_select exactly once.",
|
|
20512
|
+
"- If the verdict is needs_more_evidence: run at most one more targeted explore, then either re-select or proceed with the best-grounded candidate.",
|
|
20513
|
+
"- Implement ONLY the selected path. Never blend multiple candidates.",
|
|
20514
|
+
"- If the verdict includes required checks: in PLAN fold them into the final plan verification section; in BUILD pass them as the Acceptance criteria of your verify tentacle.",
|
|
20515
|
+
"- A degraded, timed-out, or inconclusive observation is never proof of absence."
|
|
20516
|
+
].join("\n")
|
|
20517
|
+
};
|
|
20459
20518
|
SINGLE_AGENT_IDENTITY_MODULE = KRAKEN_IDENTITY_MODULE;
|
|
20460
20519
|
}
|
|
20461
20520
|
});
|
|
@@ -21014,6 +21073,7 @@ __export(skills_exports, {
|
|
|
21014
21073
|
CODING_SKILL_CATALOG: () => CODING_SKILL_CATALOG,
|
|
21015
21074
|
KRAKEN_IDENTITY_MODULE: () => KRAKEN_IDENTITY_MODULE,
|
|
21016
21075
|
KRAKEN_LEAD_PLAYBOOK_MODULE: () => KRAKEN_LEAD_PLAYBOOK_MODULE,
|
|
21076
|
+
KRAKEN_SELECTION_PLAYBOOK_MODULE: () => KRAKEN_SELECTION_PLAYBOOK_MODULE,
|
|
21017
21077
|
LANGUAGE_POLICY_MODULE_TYPE: () => LANGUAGE_POLICY_MODULE_TYPE,
|
|
21018
21078
|
NATIVE_TOOL_PROTOCOL_MODULE: () => NATIVE_TOOL_PROTOCOL_MODULE,
|
|
21019
21079
|
SINGLE_AGENT_IDENTITY_MODULE: () => SINGLE_AGENT_IDENTITY_MODULE,
|
|
@@ -21260,6 +21320,12 @@ function isBrainTaskSnapshotEvent(e) {
|
|
|
21260
21320
|
function isBrainContextMetricsEvent(e) {
|
|
21261
21321
|
return e.type === "context_metrics";
|
|
21262
21322
|
}
|
|
21323
|
+
function isBrainKrakenProgressEvent(e) {
|
|
21324
|
+
return e.type === "kraken_progress";
|
|
21325
|
+
}
|
|
21326
|
+
function isBrainKrakenMetricsEvent(e) {
|
|
21327
|
+
return e.type === "kraken_metrics";
|
|
21328
|
+
}
|
|
21263
21329
|
function createBrainEvent(type, sessionId2, data) {
|
|
21264
21330
|
return {
|
|
21265
21331
|
type,
|
|
@@ -26972,6 +27038,7 @@ __export(council_exports, {
|
|
|
26972
27038
|
IMPLEMENTATION_WRITE_REQUIREMENTS: () => IMPLEMENTATION_WRITE_REQUIREMENTS,
|
|
26973
27039
|
KRAKEN_IDENTITY_MODULE: () => KRAKEN_IDENTITY_MODULE,
|
|
26974
27040
|
KRAKEN_LEAD_PLAYBOOK_MODULE: () => KRAKEN_LEAD_PLAYBOOK_MODULE,
|
|
27041
|
+
KRAKEN_SELECTION_PLAYBOOK_MODULE: () => KRAKEN_SELECTION_PLAYBOOK_MODULE,
|
|
26975
27042
|
LAYOUT_MOTION_PROPS: () => LAYOUT_MOTION_PROPS,
|
|
26976
27043
|
LESSONS_FILE: () => LESSONS_FILE,
|
|
26977
27044
|
MAX_DELIVERY_ATTEMPTS: () => MAX_DELIVERY_ATTEMPTS,
|
|
@@ -28211,6 +28278,7 @@ __export(dist_exports, {
|
|
|
28211
28278
|
IMPLEMENTATION_WRITE_REQUIREMENTS: () => IMPLEMENTATION_WRITE_REQUIREMENTS,
|
|
28212
28279
|
KRAKEN_IDENTITY_MODULE: () => KRAKEN_IDENTITY_MODULE,
|
|
28213
28280
|
KRAKEN_LEAD_PLAYBOOK_MODULE: () => KRAKEN_LEAD_PLAYBOOK_MODULE,
|
|
28281
|
+
KRAKEN_SELECTION_PLAYBOOK_MODULE: () => KRAKEN_SELECTION_PLAYBOOK_MODULE,
|
|
28214
28282
|
LANGUAGE_POLICY_MODULE_TYPE: () => LANGUAGE_POLICY_MODULE_TYPE,
|
|
28215
28283
|
LAYOUT_MOTION_PROPS: () => LAYOUT_MOTION_PROPS,
|
|
28216
28284
|
LESSONS_FILE: () => LESSONS_FILE,
|
|
@@ -28335,6 +28403,8 @@ __export(dist_exports, {
|
|
|
28335
28403
|
isBrainContextMetricsEvent: () => isBrainContextMetricsEvent,
|
|
28336
28404
|
isBrainCouncilModeEvent: () => isBrainCouncilModeEvent,
|
|
28337
28405
|
isBrainErrorEvent: () => isBrainErrorEvent,
|
|
28406
|
+
isBrainKrakenMetricsEvent: () => isBrainKrakenMetricsEvent,
|
|
28407
|
+
isBrainKrakenProgressEvent: () => isBrainKrakenProgressEvent,
|
|
28338
28408
|
isBrainMemberCostEvent: () => isBrainMemberCostEvent,
|
|
28339
28409
|
isBrainMessageDeltaEvent: () => isBrainMessageDeltaEvent,
|
|
28340
28410
|
isBrainMessageEndEvent: () => isBrainMessageEndEvent,
|
|
@@ -31155,6 +31225,362 @@ var init_krakenWorktree = __esm({
|
|
|
31155
31225
|
}
|
|
31156
31226
|
});
|
|
31157
31227
|
|
|
31228
|
+
// src/cli/kraken/candidateRegistry.ts
|
|
31229
|
+
function isKrakenSelectionEnabled() {
|
|
31230
|
+
return process.env.ZELARI_KRAKEN_SELECTION === "1";
|
|
31231
|
+
}
|
|
31232
|
+
function store2() {
|
|
31233
|
+
const g = globalThis;
|
|
31234
|
+
if (!g.__zelariKrakenCandidates) g.__zelariKrakenCandidates = [];
|
|
31235
|
+
return g.__zelariKrakenCandidates;
|
|
31236
|
+
}
|
|
31237
|
+
function resetKrakenCandidates() {
|
|
31238
|
+
const g = globalThis;
|
|
31239
|
+
g.__zelariKrakenCandidates = [];
|
|
31240
|
+
g.__zelariKrakenSelection = null;
|
|
31241
|
+
g.__zelariKrakenCheckResults = null;
|
|
31242
|
+
}
|
|
31243
|
+
function setKrakenSelection(verdict) {
|
|
31244
|
+
const g = globalThis;
|
|
31245
|
+
g.__zelariKrakenSelection = verdict;
|
|
31246
|
+
}
|
|
31247
|
+
function getKrakenSelection() {
|
|
31248
|
+
const g = globalThis;
|
|
31249
|
+
return g.__zelariKrakenSelection ?? null;
|
|
31250
|
+
}
|
|
31251
|
+
function krakenRequiredChecks() {
|
|
31252
|
+
const verdict = getKrakenSelection();
|
|
31253
|
+
if (!verdict || verdict.status !== "selected") return [];
|
|
31254
|
+
return verdict.requiredChecks;
|
|
31255
|
+
}
|
|
31256
|
+
function setKrakenCheckResults(results) {
|
|
31257
|
+
const g = globalThis;
|
|
31258
|
+
g.__zelariKrakenCheckResults = results;
|
|
31259
|
+
}
|
|
31260
|
+
function getKrakenCheckResults() {
|
|
31261
|
+
const g = globalThis;
|
|
31262
|
+
const results = g.__zelariKrakenCheckResults;
|
|
31263
|
+
return results ? [...results] : null;
|
|
31264
|
+
}
|
|
31265
|
+
function krakenChecksPassed() {
|
|
31266
|
+
const results = getKrakenCheckResults();
|
|
31267
|
+
if (!results) return void 0;
|
|
31268
|
+
return results.filter((r) => r.status === "pass").length;
|
|
31269
|
+
}
|
|
31270
|
+
function krakenCandidates() {
|
|
31271
|
+
return [...store2()];
|
|
31272
|
+
}
|
|
31273
|
+
function reserveCandidateSlot() {
|
|
31274
|
+
const n = store2().length;
|
|
31275
|
+
if (n >= KRAKEN_CANDIDATE_CAP) {
|
|
31276
|
+
return {
|
|
31277
|
+
error: `task: candidate cap reached (${KRAKEN_CANDIDATE_CAP}). Compare the existing candidates instead of spawning more.`
|
|
31278
|
+
};
|
|
31279
|
+
}
|
|
31280
|
+
return { index: n + 1 };
|
|
31281
|
+
}
|
|
31282
|
+
function parseCandidateReport(raw) {
|
|
31283
|
+
const open = raw.lastIndexOf(REPORT_OPEN);
|
|
31284
|
+
const close = raw.lastIndexOf(REPORT_CLOSE);
|
|
31285
|
+
if (open === -1 || close === -1 || close < open) {
|
|
31286
|
+
return { ok: false, error: "missing report block" };
|
|
31287
|
+
}
|
|
31288
|
+
const body = raw.slice(open + REPORT_OPEN.length, close).trim();
|
|
31289
|
+
let parsed;
|
|
31290
|
+
try {
|
|
31291
|
+
parsed = JSON.parse(body);
|
|
31292
|
+
} catch (err) {
|
|
31293
|
+
return {
|
|
31294
|
+
ok: false,
|
|
31295
|
+
error: `invalid JSON (${err instanceof Error ? err.message : String(err)})`
|
|
31296
|
+
};
|
|
31297
|
+
}
|
|
31298
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
31299
|
+
return { ok: false, error: "report is not a JSON object" };
|
|
31300
|
+
}
|
|
31301
|
+
const obj = parsed;
|
|
31302
|
+
const evidence = Array.isArray(obj.evidence) ? obj.evidence.filter((e) => typeof e === "object" && e !== null).map((e) => ({
|
|
31303
|
+
claim: typeof e.claim === "string" ? e.claim : "",
|
|
31304
|
+
basis: typeof e.basis === "string" ? e.basis : "",
|
|
31305
|
+
degraded: e.degraded === true
|
|
31306
|
+
})).filter((e) => e.claim.trim().length > 0) : [];
|
|
31307
|
+
const risks = Array.isArray(obj.risks) ? obj.risks.filter((r) => typeof r === "string" && r.trim().length > 0) : [];
|
|
31308
|
+
return {
|
|
31309
|
+
ok: true,
|
|
31310
|
+
report: {
|
|
31311
|
+
hypothesis: typeof obj.hypothesis === "string" ? obj.hypothesis.trim() : "",
|
|
31312
|
+
evidence,
|
|
31313
|
+
risks,
|
|
31314
|
+
hasDegradedEvidence: evidence.some((e) => e.degraded)
|
|
31315
|
+
}
|
|
31316
|
+
};
|
|
31317
|
+
}
|
|
31318
|
+
function registerCandidate(entry) {
|
|
31319
|
+
const withIndex = { ...entry, index: entry.index ?? store2().length + 1 };
|
|
31320
|
+
store2().push(withIndex);
|
|
31321
|
+
return withIndex;
|
|
31322
|
+
}
|
|
31323
|
+
function candidateInstructions(index, cap3 = KRAKEN_CANDIDATE_CAP) {
|
|
31324
|
+
return CANDIDATE_INSTRUCTIONS.replaceAll("{index}", String(index)).replaceAll("{cap}", String(cap3));
|
|
31325
|
+
}
|
|
31326
|
+
var KRAKEN_CANDIDATE_CAP, REPORT_OPEN, REPORT_CLOSE, CANDIDATE_INSTRUCTIONS;
|
|
31327
|
+
var init_candidateRegistry = __esm({
|
|
31328
|
+
"src/cli/kraken/candidateRegistry.ts"() {
|
|
31329
|
+
"use strict";
|
|
31330
|
+
KRAKEN_CANDIDATE_CAP = 3;
|
|
31331
|
+
REPORT_OPEN = "<candidate-report>";
|
|
31332
|
+
REPORT_CLOSE = "</candidate-report>";
|
|
31333
|
+
CANDIDATE_INSTRUCTIONS = [
|
|
31334
|
+
"You are CANDIDATE #{index} of at most {cap}: one independent hypothesis",
|
|
31335
|
+
"about the task, researched in parallel with other candidates.",
|
|
31336
|
+
"DIVERSITY: focus on ONE hypothesis and pursue it honestly \u2014 do not try to",
|
|
31337
|
+
"cover every angle. A narrower, well-evidenced hypothesis beats a broad guess.",
|
|
31338
|
+
"OBSERVATION INTEGRITY: mark degraded or inconclusive observations explicitly",
|
|
31339
|
+
"(timeout, zero files walked, unavailable backend) \u2014 degraded is NOT proof",
|
|
31340
|
+
"of absence.",
|
|
31341
|
+
"END your final message with EXACTLY this block (valid JSON, no prose after):",
|
|
31342
|
+
"<candidate-report>",
|
|
31343
|
+
"{",
|
|
31344
|
+
' "hypothesis": "one-sentence hypothesis you investigated",',
|
|
31345
|
+
' "evidence": [',
|
|
31346
|
+
' { "claim": "what you found", "basis": "tool + file:line or command", "degraded": false }',
|
|
31347
|
+
" ],",
|
|
31348
|
+
' "risks": ["open questions / weaknesses"]',
|
|
31349
|
+
"}",
|
|
31350
|
+
"</candidate-report>"
|
|
31351
|
+
].join("\n");
|
|
31352
|
+
}
|
|
31353
|
+
});
|
|
31354
|
+
|
|
31355
|
+
// src/cli/kraken/verifyReport.ts
|
|
31356
|
+
function normalize(text) {
|
|
31357
|
+
return text.toLowerCase().replace(/\s+/g, " ").trim();
|
|
31358
|
+
}
|
|
31359
|
+
function extractVerifyReportBlocks(raw) {
|
|
31360
|
+
const blocks = [];
|
|
31361
|
+
let cursor = 0;
|
|
31362
|
+
for (; ; ) {
|
|
31363
|
+
const open = raw.indexOf(OPEN, cursor);
|
|
31364
|
+
if (open === -1) break;
|
|
31365
|
+
const close = raw.indexOf(CLOSE, open + OPEN.length);
|
|
31366
|
+
if (close === -1) break;
|
|
31367
|
+
const block = { check: "", status: "" };
|
|
31368
|
+
for (const line of raw.slice(open + OPEN.length, close).split(/\r?\n/)) {
|
|
31369
|
+
const m = /^(check|status|note)\s*:\s*(.*)$/i.exec(line.trim());
|
|
31370
|
+
if (!m) continue;
|
|
31371
|
+
const value = m[2].trim();
|
|
31372
|
+
const key = m[1].toLowerCase();
|
|
31373
|
+
if (key === "check" && !block.check) block.check = value;
|
|
31374
|
+
else if (key === "status" && !block.status) block.status = value;
|
|
31375
|
+
else if (key === "note" && block.note === void 0) block.note = value;
|
|
31376
|
+
}
|
|
31377
|
+
if (block.check) blocks.push(block);
|
|
31378
|
+
cursor = close + CLOSE.length;
|
|
31379
|
+
}
|
|
31380
|
+
return blocks;
|
|
31381
|
+
}
|
|
31382
|
+
function parseVerifyReport(raw, requiredChecks) {
|
|
31383
|
+
const byCriterion = /* @__PURE__ */ new Map();
|
|
31384
|
+
for (const block of extractVerifyReportBlocks(raw)) {
|
|
31385
|
+
byCriterion.set(normalize(block.check), block);
|
|
31386
|
+
}
|
|
31387
|
+
const keys = [...byCriterion.keys()];
|
|
31388
|
+
return requiredChecks.map((check2) => {
|
|
31389
|
+
const norm = normalize(check2);
|
|
31390
|
+
let block = byCriterion.get(norm) ?? null;
|
|
31391
|
+
if (!block) {
|
|
31392
|
+
for (const key of keys) {
|
|
31393
|
+
if (key.length >= 8 && (key.includes(norm) || norm.includes(key))) {
|
|
31394
|
+
block = byCriterion.get(key) ?? null;
|
|
31395
|
+
break;
|
|
31396
|
+
}
|
|
31397
|
+
}
|
|
31398
|
+
}
|
|
31399
|
+
if (!block) {
|
|
31400
|
+
return {
|
|
31401
|
+
check: check2,
|
|
31402
|
+
status: "unknown",
|
|
31403
|
+
note: "no verify-report block for this check"
|
|
31404
|
+
};
|
|
31405
|
+
}
|
|
31406
|
+
const status = VALID_STATUSES.has(block.status) ? block.status : "unknown";
|
|
31407
|
+
return { check: check2, status, ...block.note ? { note: block.note } : {} };
|
|
31408
|
+
});
|
|
31409
|
+
}
|
|
31410
|
+
function allUnknownCheckResults(requiredChecks, reason) {
|
|
31411
|
+
return requiredChecks.map((check2) => ({
|
|
31412
|
+
check: check2,
|
|
31413
|
+
status: "unknown",
|
|
31414
|
+
note: reason
|
|
31415
|
+
}));
|
|
31416
|
+
}
|
|
31417
|
+
var OPEN, CLOSE, VALID_STATUSES;
|
|
31418
|
+
var init_verifyReport = __esm({
|
|
31419
|
+
"src/cli/kraken/verifyReport.ts"() {
|
|
31420
|
+
"use strict";
|
|
31421
|
+
OPEN = "<verify-report>";
|
|
31422
|
+
CLOSE = "</verify-report>";
|
|
31423
|
+
VALID_STATUSES = /* @__PURE__ */ new Set(["pass", "fail", "unknown"]);
|
|
31424
|
+
}
|
|
31425
|
+
});
|
|
31426
|
+
|
|
31427
|
+
// src/cli/kraken/completionGate.ts
|
|
31428
|
+
function normalize2(text) {
|
|
31429
|
+
return text.toLowerCase().replace(/\s+/g, " ").trim();
|
|
31430
|
+
}
|
|
31431
|
+
function classifyKrakenChecks(requiredChecks, results) {
|
|
31432
|
+
const byCriterion = /* @__PURE__ */ new Map();
|
|
31433
|
+
for (const result of results ?? []) {
|
|
31434
|
+
byCriterion.set(normalize2(result.check), result);
|
|
31435
|
+
}
|
|
31436
|
+
const keys = [...byCriterion.keys()];
|
|
31437
|
+
const out = { passed: [], failed: [], unknown: [] };
|
|
31438
|
+
for (const check2 of requiredChecks) {
|
|
31439
|
+
const norm = normalize2(check2);
|
|
31440
|
+
let result = byCriterion.get(norm) ?? null;
|
|
31441
|
+
if (!result) {
|
|
31442
|
+
for (const key of keys) {
|
|
31443
|
+
if (key.length >= 8 && (key.includes(norm) || norm.includes(key))) {
|
|
31444
|
+
result = byCriterion.get(key) ?? null;
|
|
31445
|
+
break;
|
|
31446
|
+
}
|
|
31447
|
+
}
|
|
31448
|
+
}
|
|
31449
|
+
if (!result || result.status === "unknown") out.unknown.push(check2);
|
|
31450
|
+
else if (result.status === "pass") out.passed.push(check2);
|
|
31451
|
+
else out.failed.push(check2);
|
|
31452
|
+
}
|
|
31453
|
+
return out;
|
|
31454
|
+
}
|
|
31455
|
+
function evaluateKrakenCompletionGate(mode) {
|
|
31456
|
+
try {
|
|
31457
|
+
if (mode !== "build") return OPEN_GATE;
|
|
31458
|
+
const checks = krakenRequiredChecks();
|
|
31459
|
+
if (checks.length === 0) return OPEN_GATE;
|
|
31460
|
+
const classification = classifyKrakenChecks(checks, getKrakenCheckResults());
|
|
31461
|
+
return {
|
|
31462
|
+
blocked: classification.failed.length > 0 || classification.unknown.length > 0,
|
|
31463
|
+
selectionUsed: true,
|
|
31464
|
+
total: checks.length,
|
|
31465
|
+
passed: classification.passed.length,
|
|
31466
|
+
failedChecks: classification.failed,
|
|
31467
|
+
unknownChecks: classification.unknown
|
|
31468
|
+
};
|
|
31469
|
+
} catch {
|
|
31470
|
+
return OPEN_GATE;
|
|
31471
|
+
}
|
|
31472
|
+
}
|
|
31473
|
+
function buildKrakenRepairPrompt(gate) {
|
|
31474
|
+
const lines = [
|
|
31475
|
+
`The BUILD turn is ending, but the required checks from kraken_select are not all satisfied (passed ${gate.passed}/${gate.total}).`,
|
|
31476
|
+
""
|
|
31477
|
+
];
|
|
31478
|
+
if (gate.failedChecks.length > 0) {
|
|
31479
|
+
lines.push("FAILED checks (evidence contradicts them):");
|
|
31480
|
+
for (const check2 of gate.failedChecks) lines.push(`- ${check2}`);
|
|
31481
|
+
lines.push("");
|
|
31482
|
+
}
|
|
31483
|
+
if (gate.unknownChecks.length > 0) {
|
|
31484
|
+
lines.push(
|
|
31485
|
+
"UNKNOWN checks (never conclusively verified \u2014 a degraded or missing observation is NOT proof):"
|
|
31486
|
+
);
|
|
31487
|
+
for (const check2 of gate.unknownChecks) lines.push(`- ${check2}`);
|
|
31488
|
+
lines.push("");
|
|
31489
|
+
}
|
|
31490
|
+
lines.push(
|
|
31491
|
+
"Recover this turn:",
|
|
31492
|
+
"1. The approach selection is settled \u2014 do NOT call kraken_select again.",
|
|
31493
|
+
"2. Fix each failing check directly with focused edits.",
|
|
31494
|
+
"3. Make each unknown check conclusively verifiable (run the real command, read the real output).",
|
|
31495
|
+
"4. Spawn a `task verify` tentacle whose Acceptance lists ALL required checks; its conclusion must contain one <verify-report> block per check with an explicit status.",
|
|
31496
|
+
"5. Only end the turn when every check reports status: pass \u2014 an unverified assumption is not a pass."
|
|
31497
|
+
);
|
|
31498
|
+
return lines.join("\n");
|
|
31499
|
+
}
|
|
31500
|
+
var OPEN_GATE;
|
|
31501
|
+
var init_completionGate = __esm({
|
|
31502
|
+
"src/cli/kraken/completionGate.ts"() {
|
|
31503
|
+
"use strict";
|
|
31504
|
+
init_candidateRegistry();
|
|
31505
|
+
OPEN_GATE = {
|
|
31506
|
+
blocked: false,
|
|
31507
|
+
selectionUsed: false,
|
|
31508
|
+
total: 0,
|
|
31509
|
+
passed: 0,
|
|
31510
|
+
failedChecks: [],
|
|
31511
|
+
unknownChecks: []
|
|
31512
|
+
};
|
|
31513
|
+
}
|
|
31514
|
+
});
|
|
31515
|
+
|
|
31516
|
+
// src/cli/kraken/metrics.ts
|
|
31517
|
+
function store3() {
|
|
31518
|
+
const g = globalThis;
|
|
31519
|
+
g.__zelariKrakenTurnMetrics ??= {
|
|
31520
|
+
candidateTokens: 0,
|
|
31521
|
+
selectionRecorded: false,
|
|
31522
|
+
selectionFallback: false,
|
|
31523
|
+
repairTriggered: false,
|
|
31524
|
+
repairSucceeded: false
|
|
31525
|
+
};
|
|
31526
|
+
return g.__zelariKrakenTurnMetrics;
|
|
31527
|
+
}
|
|
31528
|
+
function resetKrakenTurnMetrics() {
|
|
31529
|
+
const g = globalThis;
|
|
31530
|
+
g.__zelariKrakenTurnMetrics = void 0;
|
|
31531
|
+
}
|
|
31532
|
+
function recordCandidateTokens(totalTokens) {
|
|
31533
|
+
if (!Number.isFinite(totalTokens) || totalTokens <= 0) return;
|
|
31534
|
+
store3().candidateTokens += Math.round(totalTokens);
|
|
31535
|
+
}
|
|
31536
|
+
function recordSelectionOutcome(outcome) {
|
|
31537
|
+
const s = store3();
|
|
31538
|
+
s.selectionRecorded = true;
|
|
31539
|
+
s.selectionLatencyMs = Math.max(0, Math.round(outcome.latencyMs));
|
|
31540
|
+
if (typeof outcome.tokens === "number" && Number.isFinite(outcome.tokens) && outcome.tokens > 0) {
|
|
31541
|
+
s.selectionTokens = Math.round(outcome.tokens);
|
|
31542
|
+
}
|
|
31543
|
+
s.selectionFallback = outcome.degraded;
|
|
31544
|
+
s.selectionFallbackReason = outcome.fallbackReason;
|
|
31545
|
+
}
|
|
31546
|
+
function markRepairTriggered() {
|
|
31547
|
+
store3().repairTriggered = true;
|
|
31548
|
+
}
|
|
31549
|
+
function markRepairSucceeded() {
|
|
31550
|
+
if (store3().repairTriggered) store3().repairSucceeded = true;
|
|
31551
|
+
}
|
|
31552
|
+
function collectKrakenTurnMetrics() {
|
|
31553
|
+
const s = store3();
|
|
31554
|
+
const verdict = getKrakenSelection();
|
|
31555
|
+
const candidates = krakenCandidates();
|
|
31556
|
+
if (!verdict && candidates.length === 0 && !s.selectionRecorded && !s.repairTriggered) {
|
|
31557
|
+
return null;
|
|
31558
|
+
}
|
|
31559
|
+
const classification = classifyKrakenChecks(krakenRequiredChecks(), getKrakenCheckResults());
|
|
31560
|
+
return {
|
|
31561
|
+
selectionUsed: verdict !== null,
|
|
31562
|
+
candidateCount: candidates.length,
|
|
31563
|
+
candidateTokens: s.candidateTokens,
|
|
31564
|
+
...s.selectionTokens !== void 0 ? { selectionTokens: s.selectionTokens } : {},
|
|
31565
|
+
...s.selectionLatencyMs !== void 0 ? { selectionLatencyMs: s.selectionLatencyMs } : {},
|
|
31566
|
+
selectionFallback: s.selectionFallback,
|
|
31567
|
+
...s.selectionFallbackReason !== void 0 ? { selectionFallbackReason: s.selectionFallbackReason } : {},
|
|
31568
|
+
needsMoreEvidence: verdict?.status === "needs_more_evidence",
|
|
31569
|
+
verificationPass: classification.passed.length,
|
|
31570
|
+
verificationFail: classification.failed.length,
|
|
31571
|
+
verificationUnknown: classification.unknown.length,
|
|
31572
|
+
repairTriggered: s.repairTriggered,
|
|
31573
|
+
repairSucceeded: s.repairSucceeded
|
|
31574
|
+
};
|
|
31575
|
+
}
|
|
31576
|
+
var init_metrics2 = __esm({
|
|
31577
|
+
"src/cli/kraken/metrics.ts"() {
|
|
31578
|
+
"use strict";
|
|
31579
|
+
init_candidateRegistry();
|
|
31580
|
+
init_completionGate();
|
|
31581
|
+
}
|
|
31582
|
+
});
|
|
31583
|
+
|
|
31158
31584
|
// src/cli/tools/taskTool.ts
|
|
31159
31585
|
function resetTaskSpawnCount() {
|
|
31160
31586
|
const g = globalThis;
|
|
@@ -31184,6 +31610,21 @@ function buildTaskUserPrompt(args) {
|
|
|
31184
31610
|
}
|
|
31185
31611
|
return parts.join("\n");
|
|
31186
31612
|
}
|
|
31613
|
+
function withKrakenRequiredChecks(agent, acceptance) {
|
|
31614
|
+
if (agent !== "verify") return acceptance;
|
|
31615
|
+
const required2 = krakenRequiredChecks();
|
|
31616
|
+
if (required2.length === 0) return acceptance;
|
|
31617
|
+
const seen = new Set((acceptance ?? []).map((a) => a.trim().toLowerCase()));
|
|
31618
|
+
const merged = [...acceptance ?? []];
|
|
31619
|
+
for (const check2 of required2) {
|
|
31620
|
+
const key = check2.trim().toLowerCase();
|
|
31621
|
+
if (!seen.has(key)) {
|
|
31622
|
+
seen.add(key);
|
|
31623
|
+
merged.push(check2);
|
|
31624
|
+
}
|
|
31625
|
+
}
|
|
31626
|
+
return merged;
|
|
31627
|
+
}
|
|
31187
31628
|
function systemPromptForAgent(agent) {
|
|
31188
31629
|
if (agent === "general") return GENERAL_PROMPT;
|
|
31189
31630
|
if (agent === "verify") return VERIFY_PROMPT;
|
|
@@ -31209,6 +31650,7 @@ async function runSubAgent(harness, opts = {}) {
|
|
|
31209
31650
|
let current = "";
|
|
31210
31651
|
let lastCompleted = "";
|
|
31211
31652
|
let error51;
|
|
31653
|
+
let usage;
|
|
31212
31654
|
if (signal?.aborted) return { result: "", aborted: true };
|
|
31213
31655
|
for await (const ev of harness.run()) {
|
|
31214
31656
|
if (signal?.aborted) {
|
|
@@ -31227,6 +31669,16 @@ async function runSubAgent(harness, opts = {}) {
|
|
|
31227
31669
|
break;
|
|
31228
31670
|
case "message_end":
|
|
31229
31671
|
if (current.trim()) lastCompleted = current;
|
|
31672
|
+
if (ev.usage) {
|
|
31673
|
+
usage = usage ? {
|
|
31674
|
+
promptTokens: usage.promptTokens + ev.usage.promptTokens,
|
|
31675
|
+
completionTokens: usage.completionTokens + ev.usage.completionTokens,
|
|
31676
|
+
totalTokens: usage.totalTokens + ev.usage.totalTokens,
|
|
31677
|
+
...(usage.cachedPromptTokens ?? 0) + (ev.usage.cachedPromptTokens ?? 0) > 0 ? {
|
|
31678
|
+
cachedPromptTokens: (usage.cachedPromptTokens ?? 0) + (ev.usage.cachedPromptTokens ?? 0)
|
|
31679
|
+
} : {}
|
|
31680
|
+
} : ev.usage;
|
|
31681
|
+
}
|
|
31230
31682
|
current = "";
|
|
31231
31683
|
break;
|
|
31232
31684
|
case "error":
|
|
@@ -31237,7 +31689,7 @@ async function runSubAgent(harness, opts = {}) {
|
|
|
31237
31689
|
}
|
|
31238
31690
|
}
|
|
31239
31691
|
const result = (lastCompleted || current).trim();
|
|
31240
|
-
return { result, ...error51 ? { error: error51 } : {} };
|
|
31692
|
+
return { result, ...error51 ? { error: error51 } : {}, ...usage ? { usage } : {} };
|
|
31241
31693
|
}
|
|
31242
31694
|
async function runTentacle(opts) {
|
|
31243
31695
|
const { deps, args, agent, thoroughness, parentCwd, sessionId: sessionId2 } = opts;
|
|
@@ -31312,7 +31764,7 @@ async function runTentacle(opts) {
|
|
|
31312
31764
|
const userContent = buildTaskUserPrompt({
|
|
31313
31765
|
prompt: args.prompt,
|
|
31314
31766
|
scope: args.scope,
|
|
31315
|
-
acceptance: args.acceptance
|
|
31767
|
+
acceptance: withKrakenRequiredChecks(agent, args.acceptance)
|
|
31316
31768
|
});
|
|
31317
31769
|
const maxToolCalls = maxToolCallsForThoroughness(thoroughness, agent);
|
|
31318
31770
|
const runCwd = sub.cwd || effectiveCwd;
|
|
@@ -31347,7 +31799,7 @@ async function runTentacle(opts) {
|
|
|
31347
31799
|
error: `task: failed to start sub-agent \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
31348
31800
|
};
|
|
31349
31801
|
}
|
|
31350
|
-
const { result, error: error51, aborted: aborted2 } = await runSubAgent(harness, {
|
|
31802
|
+
const { result, error: error51, aborted: aborted2, usage } = await runSubAgent(harness, {
|
|
31351
31803
|
...opts.signal ? { signal: opts.signal } : {}
|
|
31352
31804
|
});
|
|
31353
31805
|
const durationMs = Date.now() - started;
|
|
@@ -31448,19 +31900,50 @@ ${verifyHintForGeneral(args.acceptance)}`;
|
|
|
31448
31900
|
model: sub.model,
|
|
31449
31901
|
result,
|
|
31450
31902
|
footer,
|
|
31903
|
+
...usage ? { usage } : {},
|
|
31451
31904
|
worktreePath: worktree?.path ?? null,
|
|
31452
31905
|
worktreeHandle: worktree
|
|
31453
31906
|
};
|
|
31454
31907
|
}
|
|
31455
|
-
function createTaskTool(deps) {
|
|
31908
|
+
function createTaskTool(deps, policy = {}) {
|
|
31909
|
+
const allowedAgents = policy.allowedAgents ?? ["explore", "general", "verify"];
|
|
31910
|
+
const restricted = policy.allowedAgents !== void 0 && !(policy.allowedAgents.includes("explore") && policy.allowedAgents.includes("general") && policy.allowedAgents.includes("verify"));
|
|
31911
|
+
const inputSchema2 = restricted ? TaskArgsSchema.extend({
|
|
31912
|
+
agent: external_exports.enum(allowedAgents).optional().describe(
|
|
31913
|
+
`Sub-agent type. In this mode ONLY ${allowedAgents.join("|")} is allowed (plan-safe read-only tentacles).`
|
|
31914
|
+
)
|
|
31915
|
+
}) : TaskArgsSchema;
|
|
31456
31916
|
return {
|
|
31457
31917
|
name: "task",
|
|
31458
|
-
description: "Delegate a focused sub-task to an isolated sub-agent with its own context; returns only a concise conclusion (keeps parent context lean).\n- agent=explore (default): read-only research/search\n- agent=general: can edit files for one bounded unit of work\n- agent=verify: read + bash to run tests/checks\nProvide a fully self-contained `prompt` (sub-agent cannot see this conversation). Optional scope[] + acceptance[] contracts. After general, follow up with verify."
|
|
31918
|
+
description: "Delegate a focused sub-task to an isolated sub-agent with its own context; returns only a concise conclusion (keeps parent context lean).\n- agent=explore (default): read-only research/search\n- agent=general: can edit files for one bounded unit of work\n- agent=verify: read + bash to run tests/checks\nProvide a fully self-contained `prompt` (sub-agent cannot see this conversation). Optional scope[] + acceptance[] contracts. After general, follow up with verify." + (restricted ? `
|
|
31919
|
+
RESTRICTED in this mode: only agent=${allowedAgents.join("|")} is allowed.` : ""),
|
|
31459
31920
|
permissions: ["read", "network", "write", "execute"],
|
|
31460
31921
|
timeoutMs: 3e5,
|
|
31461
|
-
inputSchema:
|
|
31922
|
+
inputSchema: inputSchema2,
|
|
31462
31923
|
execute: async (args, ctx) => {
|
|
31463
31924
|
const agent = args.agent ?? "explore";
|
|
31925
|
+
let candidateSlot = 0;
|
|
31926
|
+
if (!allowedAgents.includes(agent)) {
|
|
31927
|
+
return typedErr(
|
|
31928
|
+
`task: agent=${agent} is not allowed in this mode. Allowed: ${allowedAgents.join(", ")} (plan-safe read-only tentacles). Re-issue with an allowed agent kind.`
|
|
31929
|
+
);
|
|
31930
|
+
}
|
|
31931
|
+
const isCandidate = args.purpose === "candidate";
|
|
31932
|
+
if (isCandidate) {
|
|
31933
|
+
if (!isKrakenSelectionEnabled()) {
|
|
31934
|
+
return typedErr(
|
|
31935
|
+
"task: purpose=candidate requires ZELARI_KRAKEN_SELECTION=1 (alpha feature). Spawn a plain explore tentacle instead."
|
|
31936
|
+
);
|
|
31937
|
+
}
|
|
31938
|
+
if (agent !== "explore") {
|
|
31939
|
+
return typedErr(
|
|
31940
|
+
"task: purpose=candidate forces agent=explore (candidates are read-only in v1 \u2014 zero candidate implementations, ADR-0020)."
|
|
31941
|
+
);
|
|
31942
|
+
}
|
|
31943
|
+
const slot = reserveCandidateSlot();
|
|
31944
|
+
if ("error" in slot) return typedErr(slot.error);
|
|
31945
|
+
candidateSlot = slot.index;
|
|
31946
|
+
}
|
|
31464
31947
|
const thoroughness = args.thoroughness ?? "medium";
|
|
31465
31948
|
const sessionId2 = ctx.sessionId || "default";
|
|
31466
31949
|
const parentCwd = ctx.cwd || process.cwd();
|
|
@@ -31483,8 +31966,51 @@ function createTaskTool(deps) {
|
|
|
31483
31966
|
agent,
|
|
31484
31967
|
thoroughness,
|
|
31485
31968
|
parentCwd,
|
|
31486
|
-
sessionId: sessionId2
|
|
31969
|
+
sessionId: sessionId2,
|
|
31970
|
+
// Fase 1 (ADR-0020): propagate the parent turn's cancellation signal
|
|
31971
|
+
// so cancel/timeout unwinds the tentacle instead of letting it run on.
|
|
31972
|
+
...ctx.signal ? { signal: ctx.signal } : {},
|
|
31973
|
+
...isCandidate ? {
|
|
31974
|
+
systemPromptOverride: systemPromptForAgent("explore") + "\n\n" + candidateInstructions(candidateSlot)
|
|
31975
|
+
} : {}
|
|
31487
31976
|
});
|
|
31977
|
+
if (isCandidate) {
|
|
31978
|
+
recordCandidateTokens(res.ok ? res.usage?.totalTokens ?? 0 : 0);
|
|
31979
|
+
if (res.ok) {
|
|
31980
|
+
const parsed = parseCandidateReport(res.result);
|
|
31981
|
+
registerCandidate(
|
|
31982
|
+
parsed.ok ? {
|
|
31983
|
+
status: "ok",
|
|
31984
|
+
index: candidateSlot,
|
|
31985
|
+
description: args.description,
|
|
31986
|
+
report: parsed.report,
|
|
31987
|
+
raw: res.result
|
|
31988
|
+
} : {
|
|
31989
|
+
status: "malformed",
|
|
31990
|
+
index: candidateSlot,
|
|
31991
|
+
description: args.description,
|
|
31992
|
+
error: parsed.error,
|
|
31993
|
+
raw: res.result
|
|
31994
|
+
}
|
|
31995
|
+
);
|
|
31996
|
+
} else {
|
|
31997
|
+
registerCandidate({
|
|
31998
|
+
status: "malformed",
|
|
31999
|
+
index: candidateSlot,
|
|
32000
|
+
description: args.description,
|
|
32001
|
+
error: res.error,
|
|
32002
|
+
raw: ""
|
|
32003
|
+
});
|
|
32004
|
+
}
|
|
32005
|
+
}
|
|
32006
|
+
if (agent === "verify") {
|
|
32007
|
+
const required2 = krakenRequiredChecks();
|
|
32008
|
+
if (required2.length > 0) {
|
|
32009
|
+
setKrakenCheckResults(
|
|
32010
|
+
res.ok ? parseVerifyReport(res.result, required2) : allUnknownCheckResults(required2, `verify tentacle failed: ${res.error}`)
|
|
32011
|
+
);
|
|
32012
|
+
}
|
|
32013
|
+
}
|
|
31488
32014
|
if (!res.ok) return typedErr(res.error);
|
|
31489
32015
|
return typedOk({
|
|
31490
32016
|
result: `[sub-agent:${res.agent}/${res.thoroughness} model=${res.model}]
|
|
@@ -31494,7 +32020,7 @@ ${res.result}${res.footer}`,
|
|
|
31494
32020
|
}
|
|
31495
32021
|
};
|
|
31496
32022
|
}
|
|
31497
|
-
var EXPLORE_PROMPT, GENERAL_PROMPT, VERIFY_PROMPT, TaskArgsSchema;
|
|
32023
|
+
var EXPLORE_PROMPT, GENERAL_PROMPT, VERIFY_PROMPT, TaskArgsSchema, TaskPurposeSchema, TaskArgsWithPurposeSchema;
|
|
31498
32024
|
var init_taskTool = __esm({
|
|
31499
32025
|
"src/cli/tools/taskTool.ts"() {
|
|
31500
32026
|
"use strict";
|
|
@@ -31503,6 +32029,9 @@ var init_taskTool = __esm({
|
|
|
31503
32029
|
init_krakenRadio();
|
|
31504
32030
|
init_krakenWorktree();
|
|
31505
32031
|
init_krakenLive();
|
|
32032
|
+
init_candidateRegistry();
|
|
32033
|
+
init_verifyReport();
|
|
32034
|
+
init_metrics2();
|
|
31506
32035
|
EXPLORE_PROMPT = [
|
|
31507
32036
|
"You are a focused EXPLORE tentacle of Kraken (parent super-agent).",
|
|
31508
32037
|
"READ-ONLY tools only (read, list, grep, fetch). No edits, no shell.",
|
|
@@ -31528,7 +32057,16 @@ var init_taskTool = __esm({
|
|
|
31528
32057
|
"You may read files and run test/build commands via bash. Prefer",
|
|
31529
32058
|
"targeted checks over full suite when possible.",
|
|
31530
32059
|
"Report: pass/fail, commands run, key output, and gaps vs Acceptance criteria.",
|
|
31531
|
-
"If Acceptance criteria are listed, check each one explicitly."
|
|
32060
|
+
"If Acceptance criteria are listed, check each one explicitly.",
|
|
32061
|
+
"End your final message with ONE <verify-report> block per acceptance",
|
|
32062
|
+
"criterion (required checks included), in this exact shape:",
|
|
32063
|
+
"<verify-report>",
|
|
32064
|
+
"check: <criterion text as given>",
|
|
32065
|
+
"status: pass | fail | unknown",
|
|
32066
|
+
"note: <one line of evidence (command + outcome)>",
|
|
32067
|
+
"</verify-report>",
|
|
32068
|
+
"Use status=unknown when you could NOT determine the outcome (degraded",
|
|
32069
|
+
"tool, timeout, inconclusive evidence) \u2014 never guess pass."
|
|
31532
32070
|
].join("\n");
|
|
31533
32071
|
TaskArgsSchema = external_exports.object({
|
|
31534
32072
|
description: external_exports.string().min(1).describe("A 3-6 word label for the sub-task (for logs/UI)."),
|
|
@@ -31546,6 +32084,336 @@ var init_taskTool = __esm({
|
|
|
31546
32084
|
"Optional acceptance checklist (contract). Appended to the prompt as Acceptance criteria."
|
|
31547
32085
|
)
|
|
31548
32086
|
});
|
|
32087
|
+
TaskPurposeSchema = external_exports.enum(["candidate"]).optional().describe(
|
|
32088
|
+
"Mark this explore tentacle as one CANDIDATE hypothesis (alpha: requires ZELARI_KRAKEN_SELECTION=1). Forces agent=explore, structured report."
|
|
32089
|
+
);
|
|
32090
|
+
TaskArgsWithPurposeSchema = TaskArgsSchema.extend({
|
|
32091
|
+
purpose: TaskPurposeSchema
|
|
32092
|
+
});
|
|
32093
|
+
}
|
|
32094
|
+
});
|
|
32095
|
+
|
|
32096
|
+
// src/cli/kraken/verifier.ts
|
|
32097
|
+
function resolveKrakenVerifier(parent, env = process.env, explicit) {
|
|
32098
|
+
if (explicit?.provider?.trim() && explicit?.model?.trim()) {
|
|
32099
|
+
return { provider: explicit.provider.trim(), model: explicit.model.trim() };
|
|
32100
|
+
}
|
|
32101
|
+
const envProvider = env.ZELARI_KRAKEN_SELECT_PROVIDER?.trim();
|
|
32102
|
+
const envModel = env.ZELARI_KRAKEN_SELECT_MODEL?.trim();
|
|
32103
|
+
if (envProvider && envModel) return { provider: envProvider, model: envModel };
|
|
32104
|
+
if (envModel) return { provider: parent.provider, model: envModel };
|
|
32105
|
+
return { provider: parent.provider, model: parent.model };
|
|
32106
|
+
}
|
|
32107
|
+
function renderCandidate(entry) {
|
|
32108
|
+
if (entry.status === "malformed") {
|
|
32109
|
+
return [
|
|
32110
|
+
`## Candidate #${entry.index} \u2014 UNUSABLE (malformed report: ${entry.error})`,
|
|
32111
|
+
"This candidate cannot win; it is listed for completeness."
|
|
32112
|
+
].join("\n");
|
|
32113
|
+
}
|
|
32114
|
+
const lines = [`## Candidate #${entry.index} \u2014 ${entry.report.hypothesis || "(no hypothesis stated)"}`];
|
|
32115
|
+
if (entry.report.evidence.length === 0) {
|
|
32116
|
+
lines.push("Evidence: NONE (unsupported hypothesis)");
|
|
32117
|
+
} else {
|
|
32118
|
+
lines.push("Evidence:");
|
|
32119
|
+
for (const e of entry.report.evidence) {
|
|
32120
|
+
const tag = e.degraded ? "DEGRADED \u2014 inconclusive, NOT proof of absence" : "OK";
|
|
32121
|
+
lines.push(`- [${tag}] ${e.claim}${e.basis ? ` (basis: ${e.basis})` : ""}`);
|
|
32122
|
+
}
|
|
32123
|
+
}
|
|
32124
|
+
if (entry.report.risks.length > 0) {
|
|
32125
|
+
lines.push("Risks:");
|
|
32126
|
+
for (const r of entry.report.risks) lines.push(`- ${r}`);
|
|
32127
|
+
}
|
|
32128
|
+
return lines.join("\n");
|
|
32129
|
+
}
|
|
32130
|
+
function buildSelectionPrompt(task, candidates) {
|
|
32131
|
+
return [
|
|
32132
|
+
"TASK",
|
|
32133
|
+
task || "(the current user task \u2014 judge against the candidates below)",
|
|
32134
|
+
"",
|
|
32135
|
+
"CANDIDATES (read-only explorer reports):",
|
|
32136
|
+
...candidates.map(renderCandidate),
|
|
32137
|
+
"",
|
|
32138
|
+
"Decide which candidate is best supported by the evidence above."
|
|
32139
|
+
].join("\n");
|
|
32140
|
+
}
|
|
32141
|
+
function needsMoreEvidence(rationale, fallbackReason) {
|
|
32142
|
+
return {
|
|
32143
|
+
status: "needs_more_evidence",
|
|
32144
|
+
winnerIndex: null,
|
|
32145
|
+
rationale,
|
|
32146
|
+
requiredChecks: [],
|
|
32147
|
+
degraded: true,
|
|
32148
|
+
...fallbackReason ? { fallbackReason } : {},
|
|
32149
|
+
verifier: null,
|
|
32150
|
+
judgedBy: "deterministic"
|
|
32151
|
+
};
|
|
32152
|
+
}
|
|
32153
|
+
function parseSelectionVerdict(raw, candidates) {
|
|
32154
|
+
const open = raw.lastIndexOf(VERDICT_OPEN);
|
|
32155
|
+
const close = raw.lastIndexOf(VERDICT_CLOSE);
|
|
32156
|
+
if (open === -1 || close === -1 || close < open) {
|
|
32157
|
+
return { ok: false, error: "missing verdict block" };
|
|
32158
|
+
}
|
|
32159
|
+
const body = raw.slice(open + VERDICT_OPEN.length, close).trim();
|
|
32160
|
+
let parsed;
|
|
32161
|
+
try {
|
|
32162
|
+
parsed = JSON.parse(body);
|
|
32163
|
+
} catch (err) {
|
|
32164
|
+
return { ok: false, error: `invalid JSON (${err instanceof Error ? err.message : String(err)})` };
|
|
32165
|
+
}
|
|
32166
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
32167
|
+
return { ok: false, error: "verdict is not a JSON object" };
|
|
32168
|
+
}
|
|
32169
|
+
const obj = parsed;
|
|
32170
|
+
const status = obj.status;
|
|
32171
|
+
if (status !== "selected" && status !== "needs_more_evidence") {
|
|
32172
|
+
return { ok: false, error: `invalid status (${String(status)})` };
|
|
32173
|
+
}
|
|
32174
|
+
const rationale = typeof obj.rationale === "string" ? obj.rationale.trim() : "";
|
|
32175
|
+
const requiredChecks = (Array.isArray(obj.requiredChecks) ? obj.requiredChecks : []).filter((c) => typeof c === "string" && c.trim().length > 0).map((c) => c.trim()).slice(0, 5);
|
|
32176
|
+
if (status === "needs_more_evidence") {
|
|
32177
|
+
return {
|
|
32178
|
+
ok: true,
|
|
32179
|
+
verdict: {
|
|
32180
|
+
status,
|
|
32181
|
+
winnerIndex: null,
|
|
32182
|
+
rationale: rationale || "Candidates are not sufficiently differentiated by evidence.",
|
|
32183
|
+
requiredChecks,
|
|
32184
|
+
degraded: false,
|
|
32185
|
+
verifier: null,
|
|
32186
|
+
judgedBy: "llm"
|
|
32187
|
+
}
|
|
32188
|
+
};
|
|
32189
|
+
}
|
|
32190
|
+
const winner = obj.winnerIndex;
|
|
32191
|
+
if (typeof winner !== "number" || !Number.isInteger(winner)) {
|
|
32192
|
+
return { ok: false, error: `selected without integer winnerIndex (${String(winner)})` };
|
|
32193
|
+
}
|
|
32194
|
+
const entry = candidates.find((c) => c.index === winner);
|
|
32195
|
+
if (!entry || entry.status !== "ok") {
|
|
32196
|
+
return { ok: false, error: `winnerIndex ${winner} does not point at a usable candidate` };
|
|
32197
|
+
}
|
|
32198
|
+
return {
|
|
32199
|
+
ok: true,
|
|
32200
|
+
verdict: {
|
|
32201
|
+
status: "selected",
|
|
32202
|
+
winnerIndex: winner,
|
|
32203
|
+
rationale: rationale || "Selected by evidence comparison.",
|
|
32204
|
+
requiredChecks,
|
|
32205
|
+
degraded: false,
|
|
32206
|
+
verifier: null,
|
|
32207
|
+
judgedBy: "llm"
|
|
32208
|
+
}
|
|
32209
|
+
};
|
|
32210
|
+
}
|
|
32211
|
+
async function runKrakenSelection(opts) {
|
|
32212
|
+
const okEntries = opts.candidates.filter(
|
|
32213
|
+
(c) => c.status === "ok"
|
|
32214
|
+
);
|
|
32215
|
+
if (okEntries.length === 0) {
|
|
32216
|
+
return needsMoreEvidence(
|
|
32217
|
+
"No usable candidate reports this turn (all malformed). Proceed with your own judgment.",
|
|
32218
|
+
"no usable candidates"
|
|
32219
|
+
);
|
|
32220
|
+
}
|
|
32221
|
+
if (okEntries.length === 1) {
|
|
32222
|
+
return {
|
|
32223
|
+
status: "selected",
|
|
32224
|
+
winnerIndex: okEntries[0].index,
|
|
32225
|
+
rationale: "Single usable candidate this turn \u2014 selected without a comparison call.",
|
|
32226
|
+
requiredChecks: [],
|
|
32227
|
+
degraded: false,
|
|
32228
|
+
verifier: null,
|
|
32229
|
+
judgedBy: "deterministic"
|
|
32230
|
+
};
|
|
32231
|
+
}
|
|
32232
|
+
const system = VERIFIER_SYSTEM_PROMPT;
|
|
32233
|
+
const user = buildSelectionPrompt(opts.task, opts.candidates);
|
|
32234
|
+
let raw;
|
|
32235
|
+
try {
|
|
32236
|
+
raw = await opts.callModel({ system, user, identity: opts.identity });
|
|
32237
|
+
} catch (err) {
|
|
32238
|
+
return needsMoreEvidence(
|
|
32239
|
+
`Verifier call failed (${err instanceof Error ? err.message : String(err)}). Proceed with your own judgment.`,
|
|
32240
|
+
"verifier call failed"
|
|
32241
|
+
);
|
|
32242
|
+
}
|
|
32243
|
+
const parsed = parseSelectionVerdict(raw, opts.candidates);
|
|
32244
|
+
if (!parsed.ok) {
|
|
32245
|
+
return needsMoreEvidence(
|
|
32246
|
+
`Verifier response unusable (${parsed.error}). Proceed with your own judgment.`,
|
|
32247
|
+
`malformed verifier response: ${parsed.error}`
|
|
32248
|
+
);
|
|
32249
|
+
}
|
|
32250
|
+
return { ...parsed.verdict, verifier: opts.identity };
|
|
32251
|
+
}
|
|
32252
|
+
var VERIFIER_SYSTEM_PROMPT, VERDICT_OPEN, VERDICT_CLOSE;
|
|
32253
|
+
var init_verifier = __esm({
|
|
32254
|
+
"src/cli/kraken/verifier.ts"() {
|
|
32255
|
+
"use strict";
|
|
32256
|
+
VERIFIER_SYSTEM_PROMPT = [
|
|
32257
|
+
"You are the Kraken selection verifier. You compare independent research",
|
|
32258
|
+
"reports (candidates) about ONE task and decide which hypothesis is best",
|
|
32259
|
+
"supported by OBSERVED EVIDENCE. You do not explore anything yourself \u2014",
|
|
32260
|
+
"you judge what the candidates actually observed.",
|
|
32261
|
+
"",
|
|
32262
|
+
"RULES:",
|
|
32263
|
+
"- Evidence decides. A specific observation (file:line, command output,",
|
|
32264
|
+
" test result) outweighs any amount of confident wording.",
|
|
32265
|
+
"- An eloquent candidate with no observations must NOT beat a plainly",
|
|
32266
|
+
" worded candidate whose claims are grounded in evidence.",
|
|
32267
|
+
"- Degraded observations (timeouts, empty searches, unavailable backends)",
|
|
32268
|
+
" are NOT proof of absence \u2014 never treat them as confirmation that",
|
|
32269
|
+
" something does not exist or cannot happen.",
|
|
32270
|
+
"- A candidate CONTRADICTED by a concrete observation loses to one",
|
|
32271
|
+
" consistent with it. Weigh listed risks.",
|
|
32272
|
+
"- If the leading candidates are equally (un)supported, answer",
|
|
32273
|
+
" needs_more_evidence \u2014 do not guess.",
|
|
32274
|
+
"- requiredChecks: 0-5 concrete, runnable checks the implementation must",
|
|
32275
|
+
" pass to prove the winning hypothesis (test names, commands, behaviors).",
|
|
32276
|
+
"",
|
|
32277
|
+
"Answer with EXACTLY this block (valid JSON, no prose before/after):",
|
|
32278
|
+
"<selection-verdict>",
|
|
32279
|
+
"{",
|
|
32280
|
+
' "status": "selected" | "needs_more_evidence",',
|
|
32281
|
+
' "winnerIndex": <1-based index of the winning candidate, or null>,',
|
|
32282
|
+
' "rationale": "2-4 sentences grounded in the evidence",',
|
|
32283
|
+
' "requiredChecks": ["concrete check", "..."]',
|
|
32284
|
+
"}",
|
|
32285
|
+
"</selection-verdict>"
|
|
32286
|
+
].join("\n");
|
|
32287
|
+
VERDICT_OPEN = "<selection-verdict>";
|
|
32288
|
+
VERDICT_CLOSE = "</selection-verdict>";
|
|
32289
|
+
}
|
|
32290
|
+
});
|
|
32291
|
+
|
|
32292
|
+
// src/cli/tools/krakenSelectTool.ts
|
|
32293
|
+
async function collectProviderText(stream, params) {
|
|
32294
|
+
let text = "";
|
|
32295
|
+
let usage;
|
|
32296
|
+
for await (const delta of stream(params)) {
|
|
32297
|
+
if (delta.kind === "text") text += delta.delta;
|
|
32298
|
+
else if (delta.kind === "usage") usage = delta.usage;
|
|
32299
|
+
else if (delta.kind === "error") throw new Error(delta.message);
|
|
32300
|
+
else if (delta.kind === "finish") break;
|
|
32301
|
+
}
|
|
32302
|
+
return { text, ...usage ? { usage } : {} };
|
|
32303
|
+
}
|
|
32304
|
+
function renderVerdict(verdict, candidateCount) {
|
|
32305
|
+
const header = verdict.status === "selected" && verdict.winnerIndex !== null ? `kraken_select: SELECTED candidate #${verdict.winnerIndex}` : "kraken_select: NEEDS MORE EVIDENCE \u2014 no candidate clearly wins";
|
|
32306
|
+
const lines = [header, `Rationale: ${verdict.rationale}`];
|
|
32307
|
+
if (verdict.requiredChecks.length > 0) {
|
|
32308
|
+
lines.push("Required checks (must pass before clean completion):");
|
|
32309
|
+
verdict.requiredChecks.forEach((c, i) => lines.push(` ${i + 1}. ${c}`));
|
|
32310
|
+
}
|
|
32311
|
+
if (verdict.degraded && verdict.fallbackReason) {
|
|
32312
|
+
lines.push(
|
|
32313
|
+
`NOTE (degraded): ${verdict.fallbackReason}. Proceed with your own judgment \u2014 the verifier could not decide.`
|
|
32314
|
+
);
|
|
32315
|
+
}
|
|
32316
|
+
lines.push(`Candidates compared: ${candidateCount}.`);
|
|
32317
|
+
return lines.join("\n");
|
|
32318
|
+
}
|
|
32319
|
+
function createKrakenSelectTool(deps) {
|
|
32320
|
+
const timeoutMs = deps.timeoutMs ?? 12e4;
|
|
32321
|
+
return {
|
|
32322
|
+
name: "kraken_select",
|
|
32323
|
+
description: "Compare the candidate research reports spawned this turn (task purpose=candidate) and select the hypothesis best supported by OBSERVED EVIDENCE. A dedicated verifier call (default: the current model) judges grounded vs unsupported claims; degraded observations are never treated as proof of absence. Returns the selected candidate, a rationale, and requiredChecks the implementation must pass. Call it ONCE per turn, after candidate tentacles finished and BEFORE implementing. If it reports needs_more_evidence, either spawn ONE more differentiated candidate or proceed with your own judgment.",
|
|
32324
|
+
permissions: ["read", "network"],
|
|
32325
|
+
timeoutMs: 3e5,
|
|
32326
|
+
inputSchema: KrakenSelectArgsSchema,
|
|
32327
|
+
execute: async (input, _ctx) => {
|
|
32328
|
+
try {
|
|
32329
|
+
const candidates = krakenCandidates();
|
|
32330
|
+
if (candidates.length === 0) {
|
|
32331
|
+
return typedOk({
|
|
32332
|
+
result: "kraken_select: no candidates registered this turn \u2014 nothing to compare. Proceed directly (single-path execution)."
|
|
32333
|
+
});
|
|
32334
|
+
}
|
|
32335
|
+
const parent = await deps.loadParentIdentity();
|
|
32336
|
+
if (!parent) {
|
|
32337
|
+
const verdict2 = {
|
|
32338
|
+
status: "needs_more_evidence",
|
|
32339
|
+
winnerIndex: null,
|
|
32340
|
+
rationale: "Verifier unavailable (no provider identity for this run). Proceed with your own judgment.",
|
|
32341
|
+
requiredChecks: [],
|
|
32342
|
+
degraded: true,
|
|
32343
|
+
fallbackReason: "parent identity unavailable",
|
|
32344
|
+
verifier: null,
|
|
32345
|
+
judgedBy: "deterministic"
|
|
32346
|
+
};
|
|
32347
|
+
setKrakenSelection(verdict2);
|
|
32348
|
+
return typedOk({ result: renderVerdict(verdict2, candidates.length) });
|
|
32349
|
+
}
|
|
32350
|
+
const identity = resolveKrakenVerifier(
|
|
32351
|
+
parent,
|
|
32352
|
+
deps.env ?? process.env,
|
|
32353
|
+
deps.loadVerifierOverride?.()
|
|
32354
|
+
);
|
|
32355
|
+
let judgingStartedAt = Date.now();
|
|
32356
|
+
let judgingUsage;
|
|
32357
|
+
let judgingLatencyMs = 0;
|
|
32358
|
+
const verdict = await runKrakenSelection({
|
|
32359
|
+
task: input.task ?? "",
|
|
32360
|
+
candidates,
|
|
32361
|
+
identity,
|
|
32362
|
+
callModel: async ({ system, user, identity: id }) => {
|
|
32363
|
+
const stream = await deps.loadStream(id.provider);
|
|
32364
|
+
if (!stream) {
|
|
32365
|
+
throw new Error(`no provider config for verifier "${id.provider}"`);
|
|
32366
|
+
}
|
|
32367
|
+
judgingStartedAt = Date.now();
|
|
32368
|
+
try {
|
|
32369
|
+
const { text: raw, usage } = await collectProviderText(stream, {
|
|
32370
|
+
messages: [
|
|
32371
|
+
{ role: "system", content: system },
|
|
32372
|
+
{ role: "user", content: user }
|
|
32373
|
+
],
|
|
32374
|
+
model: id.model,
|
|
32375
|
+
provider: id.provider,
|
|
32376
|
+
tools: [],
|
|
32377
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
32378
|
+
});
|
|
32379
|
+
judgingUsage = usage;
|
|
32380
|
+
if (raw.trim().length === 0) throw new Error("empty verifier response");
|
|
32381
|
+
return raw;
|
|
32382
|
+
} finally {
|
|
32383
|
+
judgingLatencyMs = Date.now() - judgingStartedAt;
|
|
32384
|
+
}
|
|
32385
|
+
}
|
|
32386
|
+
});
|
|
32387
|
+
recordSelectionOutcome({
|
|
32388
|
+
latencyMs: judgingLatencyMs,
|
|
32389
|
+
...judgingUsage ? { tokens: judgingUsage.totalTokens } : {},
|
|
32390
|
+
degraded: verdict.degraded,
|
|
32391
|
+
fallbackReason: verdict.fallbackReason
|
|
32392
|
+
});
|
|
32393
|
+
setKrakenSelection(verdict);
|
|
32394
|
+
return typedOk({ result: renderVerdict(verdict, candidates.length) });
|
|
32395
|
+
} catch (err) {
|
|
32396
|
+
return typedErr(
|
|
32397
|
+
`kraken_select: internal error \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
32398
|
+
);
|
|
32399
|
+
}
|
|
32400
|
+
}
|
|
32401
|
+
};
|
|
32402
|
+
}
|
|
32403
|
+
var KrakenSelectArgsSchema;
|
|
32404
|
+
var init_krakenSelectTool = __esm({
|
|
32405
|
+
"src/cli/tools/krakenSelectTool.ts"() {
|
|
32406
|
+
"use strict";
|
|
32407
|
+
init_zod();
|
|
32408
|
+
init_metrics2();
|
|
32409
|
+
init_toolTypes();
|
|
32410
|
+
init_verifier();
|
|
32411
|
+
init_candidateRegistry();
|
|
32412
|
+
KrakenSelectArgsSchema = external_exports.object({
|
|
32413
|
+
task: external_exports.string().optional().describe(
|
|
32414
|
+
"The user task being solved, in one or two sentences. Used to judge which candidate hypothesis best serves it. Omit to judge against the candidates alone."
|
|
32415
|
+
)
|
|
32416
|
+
});
|
|
31549
32417
|
}
|
|
31550
32418
|
});
|
|
31551
32419
|
|
|
@@ -32270,13 +33138,13 @@ async function withPlanStore(projectRoot, fn) {
|
|
|
32270
33138
|
return out;
|
|
32271
33139
|
});
|
|
32272
33140
|
}
|
|
32273
|
-
function nextPlanTaskId(
|
|
32274
|
-
const maxExisting =
|
|
33141
|
+
function nextPlanTaskId(store6) {
|
|
33142
|
+
const maxExisting = store6.tasks.reduce((max, t) => {
|
|
32275
33143
|
const m = /^t(\d+)$/.exec(t.id);
|
|
32276
33144
|
return m ? Math.max(max, parseInt(m[1], 10)) : max;
|
|
32277
33145
|
}, 0);
|
|
32278
|
-
|
|
32279
|
-
return `t${
|
|
33146
|
+
store6.counter = Math.max(store6.counter, maxExisting) + 1;
|
|
33147
|
+
return `t${store6.counter}`;
|
|
32280
33148
|
}
|
|
32281
33149
|
function writePlanTaskArtifact(rootDir, task) {
|
|
32282
33150
|
const path56 = join15(rootDir, "plan-tasks", `${task.id}.md`);
|
|
@@ -32456,14 +33324,14 @@ function createPlanTaskTools(opts) {
|
|
|
32456
33324
|
inputSchema: CreateSchema,
|
|
32457
33325
|
execute: async (input) => {
|
|
32458
33326
|
try {
|
|
32459
|
-
const res = await withPlanStore(projectRoot, (
|
|
32460
|
-
if (
|
|
33327
|
+
const res = await withPlanStore(projectRoot, (store6) => {
|
|
33328
|
+
if (store6.tasks.length >= PLAN_MAX_TASKS) {
|
|
32461
33329
|
return typedErr(
|
|
32462
|
-
`PLAN_TOO_MANY_TASKS: plan.json already holds ${
|
|
33330
|
+
`PLAN_TOO_MANY_TASKS: plan.json already holds ${store6.tasks.length} tasks (max ${PLAN_MAX_TASKS}).`
|
|
32463
33331
|
);
|
|
32464
33332
|
}
|
|
32465
33333
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32466
|
-
const id = nextPlanTaskId(
|
|
33334
|
+
const id = nextPlanTaskId(store6);
|
|
32467
33335
|
const task = {
|
|
32468
33336
|
id,
|
|
32469
33337
|
title: input.title.trim().slice(0, PLAN_TITLE_MAX),
|
|
@@ -32477,8 +33345,8 @@ function createPlanTaskTools(opts) {
|
|
|
32477
33345
|
createdAt: now,
|
|
32478
33346
|
updatedAt: now
|
|
32479
33347
|
};
|
|
32480
|
-
|
|
32481
|
-
writePlanTaskArtifact(
|
|
33348
|
+
store6.tasks.push(task);
|
|
33349
|
+
writePlanTaskArtifact(store6.rootDir, task);
|
|
32482
33350
|
return typedOk({ id, task });
|
|
32483
33351
|
});
|
|
32484
33352
|
if (res.ok) {
|
|
@@ -32502,8 +33370,8 @@ function createPlanTaskTools(opts) {
|
|
|
32502
33370
|
inputSchema: UpdateSchema,
|
|
32503
33371
|
execute: async (input) => {
|
|
32504
33372
|
try {
|
|
32505
|
-
const res = await withPlanStore(projectRoot, (
|
|
32506
|
-
const task =
|
|
33373
|
+
const res = await withPlanStore(projectRoot, (store6) => {
|
|
33374
|
+
const task = store6.tasks.find((t) => t.id === input.id);
|
|
32507
33375
|
if (!task) {
|
|
32508
33376
|
return typedErr(
|
|
32509
33377
|
`PLAN_TASK_NOT_FOUND: no task with id "${input.id}" in .zelari/plan.json (call task_list for current ids).`
|
|
@@ -32526,7 +33394,7 @@ ${input.appendNote}` : input.appendNote;
|
|
|
32526
33394
|
task.notes = merged.slice(-PLAN_NOTES_MAX);
|
|
32527
33395
|
}
|
|
32528
33396
|
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
32529
|
-
writePlanTaskArtifact(
|
|
33397
|
+
writePlanTaskArtifact(store6.rootDir, task);
|
|
32530
33398
|
return typedOk({ task });
|
|
32531
33399
|
});
|
|
32532
33400
|
if (res.ok) {
|
|
@@ -32551,21 +33419,21 @@ ${input.appendNote}` : input.appendNote;
|
|
|
32551
33419
|
execute: async (input) => {
|
|
32552
33420
|
try {
|
|
32553
33421
|
let allPayloads = [];
|
|
32554
|
-
const res = await withPlanStore(projectRoot, (
|
|
32555
|
-
allPayloads =
|
|
32556
|
-
const filtered =
|
|
33422
|
+
const res = await withPlanStore(projectRoot, (store6) => {
|
|
33423
|
+
allPayloads = store6.tasks.map(toTaskPayload);
|
|
33424
|
+
const filtered = store6.tasks.filter(
|
|
32557
33425
|
(t) => (input.status === void 0 || t.status === input.status) && (input.phaseId === void 0 || t.phaseId === input.phaseId)
|
|
32558
33426
|
);
|
|
32559
|
-
const done =
|
|
33427
|
+
const done = store6.tasks.filter(
|
|
32560
33428
|
(t) => t.status === "completed" || t.status === "cancelled"
|
|
32561
33429
|
).length;
|
|
32562
33430
|
const formatted = filtered.length === 0 ? "(no matching workspace tasks)" : filtered.map(taskSummaryLine).join("\n");
|
|
32563
33431
|
return typedOk({
|
|
32564
33432
|
tasks: filtered,
|
|
32565
|
-
total:
|
|
33433
|
+
total: store6.tasks.length,
|
|
32566
33434
|
done,
|
|
32567
33435
|
formatted: `${formatted}
|
|
32568
|
-
(done/total: ${done}/${
|
|
33436
|
+
(done/total: ${done}/${store6.tasks.length})`
|
|
32569
33437
|
});
|
|
32570
33438
|
});
|
|
32571
33439
|
if (res.ok) {
|
|
@@ -35695,7 +36563,7 @@ import path31 from "node:path";
|
|
|
35695
36563
|
function trustStorePath() {
|
|
35696
36564
|
return _overrideStorePath ?? path31.join(homedir8(), ".zelari-code", "trust.json");
|
|
35697
36565
|
}
|
|
35698
|
-
function
|
|
36566
|
+
function normalize3(p3) {
|
|
35699
36567
|
const resolved = path31.resolve(p3);
|
|
35700
36568
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
35701
36569
|
}
|
|
@@ -35709,11 +36577,11 @@ function readStore3() {
|
|
|
35709
36577
|
return DEFAULT_STORE;
|
|
35710
36578
|
}
|
|
35711
36579
|
}
|
|
35712
|
-
function writeStore3(
|
|
36580
|
+
function writeStore3(store6) {
|
|
35713
36581
|
const p3 = trustStorePath();
|
|
35714
36582
|
try {
|
|
35715
36583
|
mkdirSync14(path31.dirname(p3), { recursive: true });
|
|
35716
|
-
writeFileSync16(p3, JSON.stringify(
|
|
36584
|
+
writeFileSync16(p3, JSON.stringify(store6, null, 2), "utf8");
|
|
35717
36585
|
} catch (err) {
|
|
35718
36586
|
throw new Error(
|
|
35719
36587
|
`failed to persist trust store ${p3}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -35732,26 +36600,26 @@ function isFolderTrusted(folderPath) {
|
|
|
35732
36600
|
const env = envTrustedFolder();
|
|
35733
36601
|
if (env === "all") return true;
|
|
35734
36602
|
if (env === "none") return false;
|
|
35735
|
-
if (env) return
|
|
35736
|
-
const target =
|
|
35737
|
-
return readStore3().folders.some((f) =>
|
|
36603
|
+
if (env) return normalize3(env) === normalize3(folderPath);
|
|
36604
|
+
const target = normalize3(folderPath);
|
|
36605
|
+
return readStore3().folders.some((f) => normalize3(f.path) === target);
|
|
35738
36606
|
}
|
|
35739
36607
|
function trustFolder(folderPath) {
|
|
35740
|
-
const
|
|
36608
|
+
const store6 = readStore3();
|
|
35741
36609
|
const normalized = path31.resolve(folderPath);
|
|
35742
|
-
if (!
|
|
35743
|
-
|
|
35744
|
-
writeStore3(
|
|
36610
|
+
if (!store6.folders.some((f) => normalize3(f.path) === normalize3(normalized))) {
|
|
36611
|
+
store6.folders.push({ path: normalized, trustedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
36612
|
+
writeStore3(store6);
|
|
35745
36613
|
}
|
|
35746
36614
|
return { ok: true, path: normalized };
|
|
35747
36615
|
}
|
|
35748
36616
|
function untrustFolder(folderPath) {
|
|
35749
|
-
const
|
|
35750
|
-
const target =
|
|
35751
|
-
const before =
|
|
35752
|
-
|
|
35753
|
-
if (
|
|
35754
|
-
writeStore3(
|
|
36617
|
+
const store6 = readStore3();
|
|
36618
|
+
const target = normalize3(folderPath);
|
|
36619
|
+
const before = store6.folders.length;
|
|
36620
|
+
store6.folders = store6.folders.filter((f) => normalize3(f.path) !== target);
|
|
36621
|
+
if (store6.folders.length === before) return { ok: true, removed: false };
|
|
36622
|
+
writeStore3(store6);
|
|
35755
36623
|
return { ok: true, removed: true };
|
|
35756
36624
|
}
|
|
35757
36625
|
function listTrustedFolders() {
|
|
@@ -35906,19 +36774,19 @@ function applyTruncation(result, toolName) {
|
|
|
35906
36774
|
function evictOldest() {
|
|
35907
36775
|
let oldestKey = null;
|
|
35908
36776
|
let oldestTs = Infinity;
|
|
35909
|
-
for (const [key, entry] of
|
|
36777
|
+
for (const [key, entry] of store4) {
|
|
35910
36778
|
if (entry.ts < oldestTs) {
|
|
35911
36779
|
oldestTs = entry.ts;
|
|
35912
36780
|
oldestKey = key;
|
|
35913
36781
|
}
|
|
35914
36782
|
}
|
|
35915
|
-
if (oldestKey)
|
|
36783
|
+
if (oldestKey) store4.delete(oldestKey);
|
|
35916
36784
|
}
|
|
35917
36785
|
function cacheGet(key, now) {
|
|
35918
|
-
const entry =
|
|
36786
|
+
const entry = store4.get(key);
|
|
35919
36787
|
if (!entry) return null;
|
|
35920
36788
|
if (entry.expiresAt !== void 0 && entry.expiresAt <= now) {
|
|
35921
|
-
|
|
36789
|
+
store4.delete(key);
|
|
35922
36790
|
return null;
|
|
35923
36791
|
}
|
|
35924
36792
|
return cloneResult(entry.result) ?? entry.result;
|
|
@@ -35928,8 +36796,8 @@ function cachePut(key, result, now, ttlMs) {
|
|
|
35928
36796
|
if (resultBytes(result) > TOOL_CACHE_MAX_BYTES) return;
|
|
35929
36797
|
const cloned = cloneResult(result);
|
|
35930
36798
|
if (!cloned) return;
|
|
35931
|
-
if (
|
|
35932
|
-
|
|
36799
|
+
if (store4.size >= TOOL_CACHE_MAX_ENTRIES && !store4.has(key)) evictOldest();
|
|
36800
|
+
store4.set(key, {
|
|
35933
36801
|
result: cloned,
|
|
35934
36802
|
ts: now,
|
|
35935
36803
|
...ttlMs !== void 0 ? { expiresAt: now + ttlMs } : {}
|
|
@@ -35973,7 +36841,7 @@ function withResultCache(tool, options = {}) {
|
|
|
35973
36841
|
}
|
|
35974
36842
|
};
|
|
35975
36843
|
}
|
|
35976
|
-
var TOOL_CACHE_MAX_ENTRIES, TOOL_CACHE_MAX_BYTES, TOOL_CACHE_DEFAULT_TTL_MS,
|
|
36844
|
+
var TOOL_CACHE_MAX_ENTRIES, TOOL_CACHE_MAX_BYTES, TOOL_CACHE_DEFAULT_TTL_MS, store4;
|
|
35977
36845
|
var init_toolResultCache = __esm({
|
|
35978
36846
|
"src/cli/toolResultCache.ts"() {
|
|
35979
36847
|
"use strict";
|
|
@@ -35981,7 +36849,7 @@ var init_toolResultCache = __esm({
|
|
|
35981
36849
|
TOOL_CACHE_MAX_ENTRIES = 200;
|
|
35982
36850
|
TOOL_CACHE_MAX_BYTES = 256 * 1024;
|
|
35983
36851
|
TOOL_CACHE_DEFAULT_TTL_MS = 5 * 60 * 1e3;
|
|
35984
|
-
|
|
36852
|
+
store4 = /* @__PURE__ */ new Map();
|
|
35985
36853
|
}
|
|
35986
36854
|
});
|
|
35987
36855
|
|
|
@@ -36252,11 +37120,20 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
36252
37120
|
});
|
|
36253
37121
|
}
|
|
36254
37122
|
}
|
|
36255
|
-
const enableTask = options.enableTask !== false &&
|
|
37123
|
+
const enableTask = options.enableTask !== false && options.readOnly !== true && !verifyMode && profile === "full" && (!options.planMode || options.planExploreTask !== false);
|
|
36256
37124
|
if (enableTask) {
|
|
36257
|
-
const taskTool = createTaskTool(
|
|
36258
|
-
|
|
36259
|
-
|
|
37125
|
+
const taskTool = createTaskTool(
|
|
37126
|
+
{
|
|
37127
|
+
createSubAgentContext: createKrakenSubAgentContextFactory({
|
|
37128
|
+
root,
|
|
37129
|
+
audit,
|
|
37130
|
+
sessionId: sessionId2,
|
|
37131
|
+
...options.subAgentProvider ? { provider: options.subAgentProvider } : {},
|
|
37132
|
+
...options.subAgentModel ? { model: options.subAgentModel } : {}
|
|
37133
|
+
})
|
|
37134
|
+
},
|
|
37135
|
+
options.planMode === true ? { allowedAgents: ["explore"] } : void 0
|
|
37136
|
+
);
|
|
36260
37137
|
registry4.register(withPerm(taskTool));
|
|
36261
37138
|
tools.push({
|
|
36262
37139
|
name: taskTool.name,
|
|
@@ -36264,6 +37141,31 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
36264
37141
|
permissions: taskTool.permissions ?? []
|
|
36265
37142
|
});
|
|
36266
37143
|
}
|
|
37144
|
+
if (options.krakenSelect === true && enableTask) {
|
|
37145
|
+
const selectTool = createKrakenSelectTool({
|
|
37146
|
+
loadParentIdentity: async () => {
|
|
37147
|
+
const cfg = options.subAgentProvider ? await providerConfigFor(options.subAgentProvider) : await providerFromEnv();
|
|
37148
|
+
if (!cfg) return null;
|
|
37149
|
+
return {
|
|
37150
|
+
provider: cfg.providerId,
|
|
37151
|
+
model: options.subAgentModel || cfg.model
|
|
37152
|
+
};
|
|
37153
|
+
},
|
|
37154
|
+
loadStream: async (provider) => {
|
|
37155
|
+
const cfg = await providerConfigFor(provider);
|
|
37156
|
+
return cfg ? buildProviderStream(cfg) : null;
|
|
37157
|
+
},
|
|
37158
|
+
// Fase 9 (ADR-0020): persisted verifier override (provider.json
|
|
37159
|
+
// `krakenVerifier`). undefined = inherit the EXACT parent model.
|
|
37160
|
+
loadVerifierOverride: () => getKrakenVerifierOverride()
|
|
37161
|
+
});
|
|
37162
|
+
registry4.register(withPerm(selectTool));
|
|
37163
|
+
tools.push({
|
|
37164
|
+
name: selectTool.name,
|
|
37165
|
+
description: selectTool.description,
|
|
37166
|
+
permissions: selectTool.permissions ?? []
|
|
37167
|
+
});
|
|
37168
|
+
}
|
|
36267
37169
|
if (process.env.ZELARI_LSP !== "0" && options.lspProvider !== null) {
|
|
36268
37170
|
const lspTools = options.lspProvider ? createLspTools(options.lspProvider, root) : createLspTools(getSharedLspManager(root), root);
|
|
36269
37171
|
for (const t of lspTools) {
|
|
@@ -36546,6 +37448,7 @@ var init_toolRegistry = __esm({
|
|
|
36546
37448
|
init_auditLogger();
|
|
36547
37449
|
init_engine();
|
|
36548
37450
|
init_taskTool();
|
|
37451
|
+
init_krakenSelectTool();
|
|
36549
37452
|
init_askUser();
|
|
36550
37453
|
init_skillTool();
|
|
36551
37454
|
init_todoTools();
|
|
@@ -36562,6 +37465,7 @@ var init_toolRegistry = __esm({
|
|
|
36562
37465
|
init_worldModel();
|
|
36563
37466
|
init_openai_compatible();
|
|
36564
37467
|
init_resolveStream();
|
|
37468
|
+
init_providerConfig();
|
|
36565
37469
|
init_toolPermissions();
|
|
36566
37470
|
init_lifecycleHooks();
|
|
36567
37471
|
init_toolResultCache();
|
|
@@ -36626,10 +37530,10 @@ function isStateEnabled(env = process.env) {
|
|
|
36626
37530
|
}
|
|
36627
37531
|
async function getStateStore(projectRoot, env = process.env) {
|
|
36628
37532
|
if (!isStateEnabled(env)) return new NoopDurableStateStore();
|
|
36629
|
-
const
|
|
37533
|
+
const store6 = new FileDurableStateStore();
|
|
36630
37534
|
try {
|
|
36631
|
-
await
|
|
36632
|
-
return
|
|
37535
|
+
await store6.init(projectRoot);
|
|
37536
|
+
return store6;
|
|
36633
37537
|
} catch {
|
|
36634
37538
|
return new NoopDurableStateStore();
|
|
36635
37539
|
}
|
|
@@ -37278,24 +38182,24 @@ var init_historyCompaction = __esm({
|
|
|
37278
38182
|
|
|
37279
38183
|
// src/cli/budget/requestSnapshotStore.ts
|
|
37280
38184
|
function recordRequestSnapshot(sessionId2, snapshot) {
|
|
37281
|
-
|
|
38185
|
+
store5.set(sessionId2, { snapshot });
|
|
37282
38186
|
}
|
|
37283
38187
|
function recordRequestUsage(sessionId2, usage) {
|
|
37284
|
-
const entry =
|
|
38188
|
+
const entry = store5.get(sessionId2);
|
|
37285
38189
|
if (!entry) return;
|
|
37286
38190
|
entry.usage = usage;
|
|
37287
38191
|
}
|
|
37288
38192
|
function getRequestSnapshotWithUsage(sessionId2) {
|
|
37289
|
-
return
|
|
38193
|
+
return store5.get(sessionId2) ?? null;
|
|
37290
38194
|
}
|
|
37291
38195
|
function clearAllRequestSnapshots() {
|
|
37292
|
-
|
|
38196
|
+
store5.clear();
|
|
37293
38197
|
}
|
|
37294
|
-
var
|
|
38198
|
+
var store5;
|
|
37295
38199
|
var init_requestSnapshotStore = __esm({
|
|
37296
38200
|
"src/cli/budget/requestSnapshotStore.ts"() {
|
|
37297
38201
|
"use strict";
|
|
37298
|
-
|
|
38202
|
+
store5 = /* @__PURE__ */ new Map();
|
|
37299
38203
|
}
|
|
37300
38204
|
});
|
|
37301
38205
|
|
|
@@ -38442,8 +39346,8 @@ async function loadDurableContext(projectRoot, opts) {
|
|
|
38442
39346
|
return cache2.text;
|
|
38443
39347
|
}
|
|
38444
39348
|
try {
|
|
38445
|
-
const
|
|
38446
|
-
const text = await
|
|
39349
|
+
const store6 = await getStateStore(projectRoot, env);
|
|
39350
|
+
const text = await store6.materializeContext(void 0, opts?.maxChars);
|
|
38447
39351
|
cache2 = { text: text || "", at: now, projectRoot };
|
|
38448
39352
|
return cache2.text;
|
|
38449
39353
|
} catch {
|
|
@@ -41251,7 +42155,7 @@ __export(commitHelpers_exports, {
|
|
|
41251
42155
|
});
|
|
41252
42156
|
async function tryStateCommit(args) {
|
|
41253
42157
|
try {
|
|
41254
|
-
const
|
|
42158
|
+
const store6 = args.store ?? await getStateStore(args.projectRoot, args.env);
|
|
41255
42159
|
let workspaceCheckpointId = args.workspaceCheckpointId;
|
|
41256
42160
|
if (!workspaceCheckpointId && args.withCheckpoint && (args.env ?? process.env).ZELARI_CHECKPOINT !== "0") {
|
|
41257
42161
|
const cp = await createCheckpoint(
|
|
@@ -41260,7 +42164,7 @@ async function tryStateCommit(args) {
|
|
|
41260
42164
|
);
|
|
41261
42165
|
if (cp.ok) workspaceCheckpointId = cp.value.id;
|
|
41262
42166
|
}
|
|
41263
|
-
const meta3 = await
|
|
42167
|
+
const meta3 = await store6.commit({
|
|
41264
42168
|
mode: args.mode,
|
|
41265
42169
|
label: args.label,
|
|
41266
42170
|
layer: args.layer,
|
|
@@ -46010,6 +46914,9 @@ function parseSetConfigFlags(argv) {
|
|
|
46010
46914
|
let endpoint;
|
|
46011
46915
|
let thinking;
|
|
46012
46916
|
let endpointClear = false;
|
|
46917
|
+
let verifierProvider;
|
|
46918
|
+
let verifierModel;
|
|
46919
|
+
let verifierClear = false;
|
|
46013
46920
|
for (let i = 0; i < argv.length; i++) {
|
|
46014
46921
|
const arg = argv[i];
|
|
46015
46922
|
if (arg === "--provider") {
|
|
@@ -46026,12 +46933,20 @@ function parseSetConfigFlags(argv) {
|
|
|
46026
46933
|
i++;
|
|
46027
46934
|
} else if (arg === "--endpoint-clear") {
|
|
46028
46935
|
endpointClear = true;
|
|
46936
|
+
} else if (arg === "--verifier-provider") {
|
|
46937
|
+
verifierProvider = argv[i + 1];
|
|
46938
|
+
i++;
|
|
46939
|
+
} else if (arg === "--verifier-model") {
|
|
46940
|
+
verifierModel = argv[i + 1];
|
|
46941
|
+
i++;
|
|
46942
|
+
} else if (arg === "--verifier-clear") {
|
|
46943
|
+
verifierClear = true;
|
|
46029
46944
|
}
|
|
46030
46945
|
}
|
|
46031
|
-
if (!provider && !model && !endpoint && !endpointClear && !thinking) {
|
|
46946
|
+
if (!provider && !model && !endpoint && !endpointClear && !thinking && !verifierProvider && !verifierModel && !verifierClear) {
|
|
46032
46947
|
return {
|
|
46033
46948
|
request: null,
|
|
46034
|
-
error: "--set-config requires --provider, --model, --endpoint, --thinking, and/or --endpoint-clear"
|
|
46949
|
+
error: "--set-config requires --provider, --model, --endpoint, --thinking, --verifier-provider + --verifier-model, --verifier-clear, and/or --endpoint-clear"
|
|
46035
46950
|
};
|
|
46036
46951
|
}
|
|
46037
46952
|
if (provider !== void 0 && provider.trim().length === 0) {
|
|
@@ -46043,6 +46958,18 @@ function parseSetConfigFlags(argv) {
|
|
|
46043
46958
|
if (endpoint !== void 0 && endpoint.trim().length === 0) {
|
|
46044
46959
|
return { request: null, error: "--endpoint cannot be empty" };
|
|
46045
46960
|
}
|
|
46961
|
+
if (verifierClear && (verifierProvider || verifierModel)) {
|
|
46962
|
+
return { request: null, error: "--verifier-clear conflicts with --verifier-provider/--verifier-model" };
|
|
46963
|
+
}
|
|
46964
|
+
if (verifierProvider !== void 0 && verifierModel === void 0 || verifierProvider === void 0 && verifierModel !== void 0) {
|
|
46965
|
+
return { request: null, error: "--verifier-provider and --verifier-model must be used together" };
|
|
46966
|
+
}
|
|
46967
|
+
if (verifierProvider !== void 0 && verifierProvider.trim().length === 0) {
|
|
46968
|
+
return { request: null, error: "--verifier-provider cannot be empty" };
|
|
46969
|
+
}
|
|
46970
|
+
if (verifierModel !== void 0 && verifierModel.trim().length === 0) {
|
|
46971
|
+
return { request: null, error: "--verifier-model cannot be empty" };
|
|
46972
|
+
}
|
|
46046
46973
|
if (endpoint && endpointClear) {
|
|
46047
46974
|
return { request: null, error: "--endpoint and --endpoint-clear conflict" };
|
|
46048
46975
|
}
|
|
@@ -46055,7 +46982,10 @@ function parseSetConfigFlags(argv) {
|
|
|
46055
46982
|
model: model?.trim(),
|
|
46056
46983
|
endpoint: endpoint?.trim(),
|
|
46057
46984
|
endpointClear: endpointClear || void 0,
|
|
46058
|
-
thinking: thinking?.trim().toLowerCase()
|
|
46985
|
+
thinking: thinking?.trim().toLowerCase(),
|
|
46986
|
+
verifierProvider: verifierProvider?.trim(),
|
|
46987
|
+
verifierModel: verifierModel?.trim(),
|
|
46988
|
+
verifierClear: verifierClear || void 0
|
|
46059
46989
|
}
|
|
46060
46990
|
};
|
|
46061
46991
|
}
|
|
@@ -46144,6 +47074,7 @@ function buildDesktopConfigSnapshot() {
|
|
|
46144
47074
|
activeProviderId: config2.activeProviderId,
|
|
46145
47075
|
modelByProvider: { ...config2.modelByProvider },
|
|
46146
47076
|
providers,
|
|
47077
|
+
krakenVerifier: config2.krakenVerifier ?? null,
|
|
46147
47078
|
cliVersion: getCurrentVersion(),
|
|
46148
47079
|
configPaths: {
|
|
46149
47080
|
provider: getProviderConfigPath(),
|
|
@@ -46182,11 +47113,25 @@ function applySetConfig(req) {
|
|
|
46182
47113
|
if (req.thinking) {
|
|
46183
47114
|
setThinkingForProvider(targetProvider, parseThinkingSpec(req.thinking));
|
|
46184
47115
|
}
|
|
47116
|
+
if (req.verifierClear) {
|
|
47117
|
+
clearKrakenVerifier();
|
|
47118
|
+
}
|
|
47119
|
+
if (req.verifierProvider && req.verifierModel) {
|
|
47120
|
+
const existsV = PROVIDERS.some((p3) => p3.id === req.verifierProvider);
|
|
47121
|
+
if (!existsV) {
|
|
47122
|
+
return {
|
|
47123
|
+
ok: false,
|
|
47124
|
+
error: `unknown verifier provider '${req.verifierProvider}'. Available: ${PROVIDERS.map((p3) => p3.id).join(", ")}`
|
|
47125
|
+
};
|
|
47126
|
+
}
|
|
47127
|
+
setKrakenVerifier(req.verifierProvider, req.verifierModel);
|
|
47128
|
+
}
|
|
46185
47129
|
const after = getProviderConfig();
|
|
47130
|
+
const vf = after.krakenVerifier;
|
|
46186
47131
|
const ep = getCustomEndpoint(after.activeProviderId);
|
|
46187
47132
|
return {
|
|
46188
47133
|
ok: true,
|
|
46189
|
-
message: `activeProvider=${after.activeProviderId} model=${after.modelByProvider[after.activeProviderId]}` + (ep ? ` endpoint=${ep}` : "")
|
|
47134
|
+
message: `activeProvider=${after.activeProviderId} model=${after.modelByProvider[after.activeProviderId]}` + (ep ? ` endpoint=${ep}` : "") + (vf ? ` verifier=${vf.provider}/${vf.model}` : " verifier=inherit")
|
|
46190
47135
|
};
|
|
46191
47136
|
} catch (err) {
|
|
46192
47137
|
return {
|
|
@@ -51290,7 +52235,180 @@ init_shellResolver();
|
|
|
51290
52235
|
init_keyStore();
|
|
51291
52236
|
init_providerConfig();
|
|
51292
52237
|
init_toolRegistry();
|
|
52238
|
+
|
|
52239
|
+
// src/cli/kraken/turnRuntime.ts
|
|
52240
|
+
init_events2();
|
|
52241
|
+
var WRITE_TOOLS3 = /* @__PURE__ */ new Set([
|
|
52242
|
+
"write_file",
|
|
52243
|
+
"edit_file",
|
|
52244
|
+
"apply_diff"
|
|
52245
|
+
]);
|
|
52246
|
+
var KrakenTurnRuntime = class {
|
|
52247
|
+
mode;
|
|
52248
|
+
sessionId;
|
|
52249
|
+
onProgress;
|
|
52250
|
+
now;
|
|
52251
|
+
loadCheckTotal;
|
|
52252
|
+
loadChecksPassed;
|
|
52253
|
+
phase = "understanding";
|
|
52254
|
+
phaseEnteredAt;
|
|
52255
|
+
tentacles = 0;
|
|
52256
|
+
exploreTentacles = 0;
|
|
52257
|
+
verifyTentacles = 0;
|
|
52258
|
+
writes = 0;
|
|
52259
|
+
inFlightTentacles = 0;
|
|
52260
|
+
/** Required checks registered via kraken_select (Fase 6). */
|
|
52261
|
+
checkTotal;
|
|
52262
|
+
/** Required checks explicitly PASSED (Fase 7; undefined until first report). */
|
|
52263
|
+
checksPassed;
|
|
52264
|
+
/** toolCallId → toolName (end events omit the name). */
|
|
52265
|
+
pendingTools = /* @__PURE__ */ new Map();
|
|
52266
|
+
/** toolCallIds of VERIFY tentacles (Fase 7 refresh trigger). */
|
|
52267
|
+
pendingVerifyIds = /* @__PURE__ */ new Set();
|
|
52268
|
+
started = false;
|
|
52269
|
+
constructor(opts) {
|
|
52270
|
+
this.mode = opts.mode;
|
|
52271
|
+
this.sessionId = opts.sessionId;
|
|
52272
|
+
this.onProgress = opts.onProgress;
|
|
52273
|
+
this.now = opts.now ?? Date.now;
|
|
52274
|
+
this.loadCheckTotal = opts.loadCheckTotal;
|
|
52275
|
+
this.loadChecksPassed = opts.loadChecksPassed;
|
|
52276
|
+
this.phaseEnteredAt = this.now();
|
|
52277
|
+
}
|
|
52278
|
+
/** Start the turn: emits the initial `understanding` event. */
|
|
52279
|
+
beginTurn() {
|
|
52280
|
+
this.started = true;
|
|
52281
|
+
this.emit();
|
|
52282
|
+
}
|
|
52283
|
+
/**
|
|
52284
|
+
* Reset the phase machine for a recovery pass of the SAME turn (headless
|
|
52285
|
+
* BUILD write-retry). Counters are kept — they describe the whole turn.
|
|
52286
|
+
*
|
|
52287
|
+
* Fase 8 (ADR-0020): `beginPass(true)` marks an automatic REPAIR pass
|
|
52288
|
+
* (completion gate) and projects `repairing` instead of restarting at
|
|
52289
|
+
* `understanding`, so the UI shows the fix loop rather than a fresh turn.
|
|
52290
|
+
*/
|
|
52291
|
+
beginPass(repair = false) {
|
|
52292
|
+
this.transition(repair ? "repairing" : "understanding");
|
|
52293
|
+
}
|
|
52294
|
+
/**
|
|
52295
|
+
* Terminal transition. `completed` is only projected on a clean finish —
|
|
52296
|
+
* cancellations and errors are already carried by the `agent_end` event.
|
|
52297
|
+
*/
|
|
52298
|
+
finish(reason) {
|
|
52299
|
+
if (reason === "completed") this.transition("completed");
|
|
52300
|
+
}
|
|
52301
|
+
/** Feed one parent-turn BrainEvent. Never throws. */
|
|
52302
|
+
observe(event) {
|
|
52303
|
+
try {
|
|
52304
|
+
if (event.type === "tool_execution_start") {
|
|
52305
|
+
if (event.toolCallId && event.toolName) {
|
|
52306
|
+
this.pendingTools.set(event.toolCallId, event.toolName);
|
|
52307
|
+
}
|
|
52308
|
+
if (event.toolName === "kraken_select") {
|
|
52309
|
+
this.transition("selecting");
|
|
52310
|
+
return;
|
|
52311
|
+
}
|
|
52312
|
+
if (event.toolName === "task") {
|
|
52313
|
+
this.tentacles++;
|
|
52314
|
+
this.inFlightTentacles++;
|
|
52315
|
+
const agent = typeof event.args?.agent === "string" ? event.args.agent : "explore";
|
|
52316
|
+
if (agent === "verify") {
|
|
52317
|
+
this.verifyTentacles++;
|
|
52318
|
+
if (event.toolCallId) this.pendingVerifyIds.add(event.toolCallId);
|
|
52319
|
+
} else this.exploreTentacles++;
|
|
52320
|
+
this.transition(agent === "verify" ? "verifying" : "exploring");
|
|
52321
|
+
}
|
|
52322
|
+
return;
|
|
52323
|
+
}
|
|
52324
|
+
if (event.type === "tool_execution_end") {
|
|
52325
|
+
const name = this.pendingTools.get(event.toolCallId);
|
|
52326
|
+
this.pendingTools.delete(event.toolCallId);
|
|
52327
|
+
if (name === "task") {
|
|
52328
|
+
const wasVerify = event.toolCallId ? this.pendingVerifyIds.delete(event.toolCallId) : false;
|
|
52329
|
+
this.inFlightTentacles = Math.max(0, this.inFlightTentacles - 1);
|
|
52330
|
+
let checksChanged = false;
|
|
52331
|
+
if (wasVerify && this.loadChecksPassed) {
|
|
52332
|
+
const passed = this.loadChecksPassed();
|
|
52333
|
+
if (passed !== void 0 && passed !== this.checksPassed) {
|
|
52334
|
+
this.checksPassed = passed;
|
|
52335
|
+
checksChanged = true;
|
|
52336
|
+
}
|
|
52337
|
+
}
|
|
52338
|
+
if (this.inFlightTentacles === 0) {
|
|
52339
|
+
if (this.mode === "plan") this.transition("planning");
|
|
52340
|
+
else if (this.writes > 0) this.transition("implementing");
|
|
52341
|
+
else if (checksChanged) this.emit();
|
|
52342
|
+
} else if (checksChanged) {
|
|
52343
|
+
this.emit();
|
|
52344
|
+
}
|
|
52345
|
+
return;
|
|
52346
|
+
}
|
|
52347
|
+
if (name === "kraken_select") {
|
|
52348
|
+
const total = this.loadCheckTotal?.() ?? 0;
|
|
52349
|
+
if (total > 0 && total !== this.checkTotal) {
|
|
52350
|
+
this.checkTotal = total;
|
|
52351
|
+
this.emit();
|
|
52352
|
+
}
|
|
52353
|
+
return;
|
|
52354
|
+
}
|
|
52355
|
+
if (name && WRITE_TOOLS3.has(name) && !event.isError) {
|
|
52356
|
+
this.writes++;
|
|
52357
|
+
if (this.mode === "build") this.transition("implementing");
|
|
52358
|
+
}
|
|
52359
|
+
return;
|
|
52360
|
+
}
|
|
52361
|
+
} catch {
|
|
52362
|
+
}
|
|
52363
|
+
}
|
|
52364
|
+
/** Current progress payload (counters + phase). */
|
|
52365
|
+
snapshot() {
|
|
52366
|
+
return {
|
|
52367
|
+
phase: this.phase,
|
|
52368
|
+
mode: this.mode,
|
|
52369
|
+
tentacles: this.tentacles,
|
|
52370
|
+
exploreTentacles: this.exploreTentacles,
|
|
52371
|
+
verifyTentacles: this.verifyTentacles,
|
|
52372
|
+
writes: this.writes,
|
|
52373
|
+
phaseEnteredAt: this.phaseEnteredAt,
|
|
52374
|
+
...this.checkTotal !== void 0 ? { checkTotal: this.checkTotal } : {},
|
|
52375
|
+
...this.checksPassed !== void 0 ? { checksPassed: this.checksPassed } : {}
|
|
52376
|
+
};
|
|
52377
|
+
}
|
|
52378
|
+
transition(next) {
|
|
52379
|
+
if (this.phase === next) return;
|
|
52380
|
+
this.phase = next;
|
|
52381
|
+
this.phaseEnteredAt = this.now();
|
|
52382
|
+
this.emit();
|
|
52383
|
+
}
|
|
52384
|
+
emit() {
|
|
52385
|
+
if (!this.started) return;
|
|
52386
|
+
try {
|
|
52387
|
+
this.onProgress(
|
|
52388
|
+
createBrainEvent("kraken_progress", this.sessionId, {
|
|
52389
|
+
progress: this.snapshot()
|
|
52390
|
+
})
|
|
52391
|
+
);
|
|
52392
|
+
} catch {
|
|
52393
|
+
}
|
|
52394
|
+
}
|
|
52395
|
+
};
|
|
52396
|
+
|
|
52397
|
+
// src/cli/hooks/useChatTurn.ts
|
|
51293
52398
|
init_taskTool();
|
|
52399
|
+
init_candidateRegistry();
|
|
52400
|
+
init_metrics2();
|
|
52401
|
+
|
|
52402
|
+
// src/cli/kraken/selectionPlaybook.ts
|
|
52403
|
+
init_skills2();
|
|
52404
|
+
init_candidateRegistry();
|
|
52405
|
+
function krakenSelectionPlaybook(include) {
|
|
52406
|
+
if (!include || !isKrakenSelectionEnabled()) return [];
|
|
52407
|
+
return [KRAKEN_SELECTION_PLAYBOOK_MODULE];
|
|
52408
|
+
}
|
|
52409
|
+
|
|
52410
|
+
// src/cli/hooks/useChatTurn.ts
|
|
52411
|
+
init_completionGate();
|
|
51294
52412
|
|
|
51295
52413
|
// src/cli/hooks/permissionPicker.ts
|
|
51296
52414
|
init_toolPermissions();
|
|
@@ -51645,6 +52763,8 @@ function useChatTurn(params) {
|
|
|
51645
52763
|
const dispatchPrompt = useCallback2(
|
|
51646
52764
|
async (userText, opts) => {
|
|
51647
52765
|
resetTaskSpawnCount();
|
|
52766
|
+
resetKrakenCandidates();
|
|
52767
|
+
resetKrakenTurnMetrics();
|
|
51648
52768
|
let envConfig;
|
|
51649
52769
|
let harness;
|
|
51650
52770
|
let historySeedLen = 0;
|
|
@@ -51728,10 +52848,30 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
51728
52848
|
}) : void 0;
|
|
51729
52849
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
51730
52850
|
planMode: workPhase === "plan",
|
|
52851
|
+
// Fase 1 (ADR-0020): anchor tentacles to THIS turn's resolved
|
|
52852
|
+
// provider/model so the TUI selection governs sub-agents too.
|
|
52853
|
+
...envConfig ? {
|
|
52854
|
+
subAgentProvider: envConfig.providerId,
|
|
52855
|
+
subAgentModel: envConfig.model,
|
|
52856
|
+
// Fase 4 (ADR-0020): kraken_select rides the same alpha
|
|
52857
|
+
// flag as candidate spawning (default off = unchanged).
|
|
52858
|
+
krakenSelect: isKrakenSelectionEnabled()
|
|
52859
|
+
} : {},
|
|
51731
52860
|
onAskUser,
|
|
51732
52861
|
onPermissionAsk,
|
|
51733
52862
|
permissionPolicy: defaultPermissionPolicy()
|
|
51734
52863
|
});
|
|
52864
|
+
const progressRuntime = new KrakenTurnRuntime({
|
|
52865
|
+
mode: workPhase === "plan" ? "plan" : "build",
|
|
52866
|
+
sessionId: sessionId2,
|
|
52867
|
+
loadCheckTotal: () => krakenRequiredChecks().length,
|
|
52868
|
+
loadChecksPassed: () => krakenChecksPassed(),
|
|
52869
|
+
onProgress: (ev) => {
|
|
52870
|
+
if (writerRef.current) void writerRef.current.append(ev);
|
|
52871
|
+
if (sessionId2) ingestLiveEvent(sessionId2, ev);
|
|
52872
|
+
}
|
|
52873
|
+
});
|
|
52874
|
+
progressRuntime.beginTurn();
|
|
51735
52875
|
const baseProviderStream = localCliProvider ?? buildProviderStream(envConfig);
|
|
51736
52876
|
let providerStream;
|
|
51737
52877
|
if (localCliProvider) {
|
|
@@ -51922,6 +53062,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
51922
53062
|
customPromptModules: [
|
|
51923
53063
|
KRAKEN_IDENTITY_MODULE,
|
|
51924
53064
|
KRAKEN_LEAD_PLAYBOOK_MODULE,
|
|
53065
|
+
...krakenSelectionPlaybook(true),
|
|
51925
53066
|
languageModule
|
|
51926
53067
|
],
|
|
51927
53068
|
agentSkillConfigs: []
|
|
@@ -51990,6 +53131,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
51990
53131
|
harnessRef.current = harness2;
|
|
51991
53132
|
setQueueCount(harness2.queueLength);
|
|
51992
53133
|
let assistantContent = "";
|
|
53134
|
+
let krakenRepairEnqueued = false;
|
|
51993
53135
|
let streamContent = "";
|
|
51994
53136
|
const streamScrub = createStreamScrubber(16);
|
|
51995
53137
|
const toolNameById = /* @__PURE__ */ new Map();
|
|
@@ -51998,6 +53140,29 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
51998
53140
|
let realUsage = null;
|
|
51999
53141
|
try {
|
|
52000
53142
|
for await (const event of harness2.run()) {
|
|
53143
|
+
progressRuntime.observe(event);
|
|
53144
|
+
if (event.type === "agent_end") {
|
|
53145
|
+
let krakenSuppressFinish = false;
|
|
53146
|
+
if (event.reason === "completed" && !krakenRepairEnqueued && isKrakenSelectionEnabled() && workPhase === "build") {
|
|
53147
|
+
const krakenGate = evaluateKrakenCompletionGate("build");
|
|
53148
|
+
if (krakenGate.blocked) {
|
|
53149
|
+
krakenRepairEnqueued = true;
|
|
53150
|
+
markRepairTriggered();
|
|
53151
|
+
harness2.enqueue(buildKrakenRepairPrompt(krakenGate));
|
|
53152
|
+
appendSystem(
|
|
53153
|
+
setMessages,
|
|
53154
|
+
`[kraken] required checks unresolved (${krakenGate.passed}/${krakenGate.total} passed) \u2014 automatic repair pass`,
|
|
53155
|
+
Date.now()
|
|
53156
|
+
);
|
|
53157
|
+
progressRuntime.beginPass(true);
|
|
53158
|
+
krakenSuppressFinish = true;
|
|
53159
|
+
}
|
|
53160
|
+
}
|
|
53161
|
+
if (event.reason === "completed" && krakenRepairEnqueued && !evaluateKrakenCompletionGate("build").blocked) {
|
|
53162
|
+
markRepairSucceeded();
|
|
53163
|
+
}
|
|
53164
|
+
if (!krakenSuppressFinish) progressRuntime.finish(event.reason);
|
|
53165
|
+
}
|
|
52001
53166
|
if (event.type === "message_end") {
|
|
52002
53167
|
if (event.usage) realUsage = event.usage;
|
|
52003
53168
|
if (event.usage) {
|
|
@@ -52170,6 +53335,14 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
52170
53335
|
}
|
|
52171
53336
|
}
|
|
52172
53337
|
}
|
|
53338
|
+
const turnMetrics = collectKrakenTurnMetrics();
|
|
53339
|
+
if (turnMetrics) {
|
|
53340
|
+
const metricsEvent = createBrainEvent("kraken_metrics", sessionId2, {
|
|
53341
|
+
metrics: turnMetrics
|
|
53342
|
+
});
|
|
53343
|
+
if (writerRef.current) void writerRef.current.append(metricsEvent);
|
|
53344
|
+
if (sessionId2) ingestLiveEvent(sessionId2, metricsEvent);
|
|
53345
|
+
}
|
|
52173
53346
|
turnSucceeded = true;
|
|
52174
53347
|
} finally {
|
|
52175
53348
|
flushStreaming();
|
|
@@ -54167,12 +55340,12 @@ init_fileStateStore();
|
|
|
54167
55340
|
async function restoreDurableState(opts) {
|
|
54168
55341
|
const restoreTree = opts.restoreTree !== false;
|
|
54169
55342
|
try {
|
|
54170
|
-
const
|
|
55343
|
+
const store6 = opts.store ?? await getStateStore(opts.projectRoot);
|
|
54171
55344
|
let meta3;
|
|
54172
55345
|
if (opts.commitId) {
|
|
54173
|
-
meta3 = await
|
|
55346
|
+
meta3 = await store6.setHead(opts.commitId);
|
|
54174
55347
|
} else {
|
|
54175
|
-
meta3 = await
|
|
55348
|
+
meta3 = await store6.head();
|
|
54176
55349
|
if (!meta3) {
|
|
54177
55350
|
return {
|
|
54178
55351
|
ok: false,
|
|
@@ -54225,8 +55398,8 @@ function ago2(ms) {
|
|
|
54225
55398
|
return `${Math.round(s / 3600)}h ago`;
|
|
54226
55399
|
}
|
|
54227
55400
|
async function handleStateStatus(ctx) {
|
|
54228
|
-
const
|
|
54229
|
-
const head = await
|
|
55401
|
+
const store6 = await getStateStore(ctx.cwd);
|
|
55402
|
+
const head = await store6.head();
|
|
54230
55403
|
if (!head) {
|
|
54231
55404
|
appendSystem(
|
|
54232
55405
|
ctx.setMessages,
|
|
@@ -54234,9 +55407,9 @@ async function handleStateStatus(ctx) {
|
|
|
54234
55407
|
);
|
|
54235
55408
|
return;
|
|
54236
55409
|
}
|
|
54237
|
-
const discoveries = await
|
|
55410
|
+
const discoveries = await store6.loadDiscoveries(head.id);
|
|
54238
55411
|
const reusable = discoveries.filter((d) => d.reusable).length;
|
|
54239
|
-
const recent = await
|
|
55412
|
+
const recent = await store6.list(8);
|
|
54240
55413
|
const lines = recent.map((c, i) => {
|
|
54241
55414
|
const ver2 = c.verification.ran ? c.verification.ok ? "ok" : "fail" : "n/a";
|
|
54242
55415
|
return ` ${i === 0 ? "\u2192" : " "} ${c.id} ${ago2(c.createdAt)} ${c.label} ver=${ver2}` + (c.layer ? ` [${c.layer}]` : "") + (c.stablePromptHash ? ` hash=${c.stablePromptHash.slice(0, 8)}` : "");
|
|
@@ -54255,9 +55428,9 @@ async function handleStateStatus(ctx) {
|
|
|
54255
55428
|
);
|
|
54256
55429
|
}
|
|
54257
55430
|
async function handleStateCommit(ctx, label) {
|
|
54258
|
-
const
|
|
55431
|
+
const store6 = await getStateStore(ctx.cwd);
|
|
54259
55432
|
try {
|
|
54260
|
-
const meta3 = await
|
|
55433
|
+
const meta3 = await store6.commit({
|
|
54261
55434
|
mode: "agent",
|
|
54262
55435
|
label: label?.trim() || "manual state commit",
|
|
54263
55436
|
layer: "manual",
|
|
@@ -54284,8 +55457,8 @@ async function handleStateCommit(ctx, label) {
|
|
|
54284
55457
|
}
|
|
54285
55458
|
}
|
|
54286
55459
|
async function handleStateShow(ctx, id) {
|
|
54287
|
-
const
|
|
54288
|
-
const meta3 = id ? await
|
|
55460
|
+
const store6 = await getStateStore(ctx.cwd);
|
|
55461
|
+
const meta3 = id ? await store6.get(id) : await store6.head();
|
|
54289
55462
|
if (!meta3) {
|
|
54290
55463
|
appendSystem(
|
|
54291
55464
|
ctx.setMessages,
|
|
@@ -54293,7 +55466,7 @@ async function handleStateShow(ctx, id) {
|
|
|
54293
55466
|
);
|
|
54294
55467
|
return;
|
|
54295
55468
|
}
|
|
54296
|
-
const text = await
|
|
55469
|
+
const text = await store6.materializeContext(meta3.id, 6e3);
|
|
54297
55470
|
appendSystem(ctx.setMessages, `[state] show ${meta3.id}
|
|
54298
55471
|
${text}`);
|
|
54299
55472
|
}
|
|
@@ -55803,14 +56976,14 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
55803
56976
|
}
|
|
55804
56977
|
function handleCouncilFeedback(ctx, memberId, score, note) {
|
|
55805
56978
|
try {
|
|
55806
|
-
const
|
|
55807
|
-
const entry =
|
|
56979
|
+
const store6 = new FeedbackStore();
|
|
56980
|
+
const entry = store6.record({
|
|
55808
56981
|
memberId,
|
|
55809
56982
|
score,
|
|
55810
56983
|
...note ? { note } : {},
|
|
55811
56984
|
...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
|
|
55812
56985
|
});
|
|
55813
|
-
const stats =
|
|
56986
|
+
const stats = store6.getStats(memberId);
|
|
55814
56987
|
appendSystem(
|
|
55815
56988
|
ctx.setMessages,
|
|
55816
56989
|
`[council-feedback] ${memberId} rated ${entry.score}/5 \u2014 running avg ${stats.avg.toFixed(2)} over ${stats.count} rating(s).`
|
|
@@ -57502,9 +58675,13 @@ function emitEvent(event) {
|
|
|
57502
58675
|
// src/cli/runHeadless.ts
|
|
57503
58676
|
init_harness();
|
|
57504
58677
|
init_dist();
|
|
58678
|
+
init_events2();
|
|
57505
58679
|
init_conversationContext();
|
|
57506
58680
|
init_council();
|
|
57507
58681
|
init_toolRegistry();
|
|
58682
|
+
init_candidateRegistry();
|
|
58683
|
+
init_metrics2();
|
|
58684
|
+
init_completionGate();
|
|
57508
58685
|
init_claudeProvider();
|
|
57509
58686
|
init_skills2();
|
|
57510
58687
|
init_envNumber();
|
|
@@ -57861,8 +59038,18 @@ async function registerHeadlessMcp(toolRegistry, opts) {
|
|
|
57861
59038
|
}
|
|
57862
59039
|
async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
57863
59040
|
const sessionId2 = crypto.randomUUID();
|
|
59041
|
+
resetKrakenCandidates();
|
|
59042
|
+
resetKrakenTurnMetrics();
|
|
57864
59043
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
57865
59044
|
planMode: planModeFromOpts(opts),
|
|
59045
|
+
// Fase 1 (ADR-0020): anchor tentacles to the provider/model THIS run
|
|
59046
|
+
// resolved (--provider/--model opts or Desktop's selector), mirroring
|
|
59047
|
+
// what the kraken-graph path already does for its executor.
|
|
59048
|
+
subAgentProvider: provider,
|
|
59049
|
+
subAgentModel: model,
|
|
59050
|
+
// Fase 4 (ADR-0020): kraken_select on the parent registry for kraken
|
|
59051
|
+
// runs with the alpha selection flag on (default off = unchanged).
|
|
59052
|
+
krakenSelect: opts.mode === "kraken" && isKrakenSelectionEnabled(),
|
|
57866
59053
|
// ADR-0018 3b: upgrade plan-task domain events to first-class NDJSON
|
|
57867
59054
|
// BrainEvents. Rust envelopes every stdout line with runId/conversationId,
|
|
57868
59055
|
// so task events ride the same multiplexed channel as the rest.
|
|
@@ -57971,6 +59158,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
57971
59158
|
customPromptModules: [
|
|
57972
59159
|
KRAKEN_IDENTITY_MODULE,
|
|
57973
59160
|
KRAKEN_LEAD_PLAYBOOK_MODULE,
|
|
59161
|
+
...krakenSelectionPlaybook(opts.mode === "kraken"),
|
|
57974
59162
|
{
|
|
57975
59163
|
type: "language-policy",
|
|
57976
59164
|
title: "Response Language",
|
|
@@ -58035,6 +59223,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
58035
59223
|
const scrub = createStreamScrubber2();
|
|
58036
59224
|
try {
|
|
58037
59225
|
for await (const event of harness.run()) {
|
|
59226
|
+
progressRuntime.observe(event);
|
|
58038
59227
|
if (event.type === "message_start") {
|
|
58039
59228
|
scrub.reset();
|
|
58040
59229
|
}
|
|
@@ -58112,6 +59301,16 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
58112
59301
|
messages: harness.getMessages()
|
|
58113
59302
|
};
|
|
58114
59303
|
}
|
|
59304
|
+
const progressRuntime = new KrakenTurnRuntime({
|
|
59305
|
+
mode: planModeFromOpts(opts) ? "plan" : "build",
|
|
59306
|
+
sessionId: sessionId2,
|
|
59307
|
+
loadCheckTotal: () => krakenRequiredChecks().length,
|
|
59308
|
+
loadChecksPassed: () => krakenChecksPassed(),
|
|
59309
|
+
onProgress: (ev) => {
|
|
59310
|
+
if (opts.output === "json") emitEvent(ev);
|
|
59311
|
+
}
|
|
59312
|
+
});
|
|
59313
|
+
progressRuntime.beginTurn();
|
|
58115
59314
|
const initialMessages = [
|
|
58116
59315
|
...systemMessages,
|
|
58117
59316
|
...historySeed,
|
|
@@ -58148,6 +59347,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
58148
59347
|
...retryMessages,
|
|
58149
59348
|
{ role: "user", content: retryPrompt }
|
|
58150
59349
|
];
|
|
59350
|
+
progressRuntime.beginPass();
|
|
58151
59351
|
const retry = await runSinglePass(withSystem, `${sessionId2}-write-retry`);
|
|
58152
59352
|
pass = {
|
|
58153
59353
|
...retry,
|
|
@@ -58156,6 +59356,42 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
58156
59356
|
emittedWrites: pass.emittedWrites + retry.emittedWrites
|
|
58157
59357
|
};
|
|
58158
59358
|
}
|
|
59359
|
+
if (pass.finalReason === "completed" && pass.exitCode === 0 && opts.mode === "kraken" && isKrakenSelectionEnabled() && !planModeFromOpts(opts)) {
|
|
59360
|
+
const gate = evaluateKrakenCompletionGate("build");
|
|
59361
|
+
if (gate.blocked) {
|
|
59362
|
+
const repairPrompt = buildKrakenRepairPrompt(gate);
|
|
59363
|
+
if (opts.output === "json") {
|
|
59364
|
+
emitEvent({
|
|
59365
|
+
type: "log",
|
|
59366
|
+
message: `[headless] Kraken BUILD: ${gate.failedChecks.length} failed / ${gate.unknownChecks.length} unknown required checks \u2014 forcing repair pass`
|
|
59367
|
+
});
|
|
59368
|
+
} else {
|
|
59369
|
+
process.stderr.write(
|
|
59370
|
+
"[zelari-code --headless] Kraken BUILD: required checks unresolved \u2014 forcing repair pass\n"
|
|
59371
|
+
);
|
|
59372
|
+
}
|
|
59373
|
+
const withSystem = [
|
|
59374
|
+
...systemMessages,
|
|
59375
|
+
...pass.messages.filter((m) => m.role !== "system"),
|
|
59376
|
+
{ role: "user", content: repairPrompt }
|
|
59377
|
+
];
|
|
59378
|
+
progressRuntime.beginPass(true);
|
|
59379
|
+
markRepairTriggered();
|
|
59380
|
+
const repair = await runSinglePass(withSystem, `${sessionId2}-check-repair`);
|
|
59381
|
+
pass = {
|
|
59382
|
+
...repair,
|
|
59383
|
+
textBuffer: [...pass.textBuffer, ...repair.textBuffer],
|
|
59384
|
+
successfulWrites: pass.successfulWrites + repair.successfulWrites,
|
|
59385
|
+
emittedWrites: pass.emittedWrites + repair.emittedWrites
|
|
59386
|
+
};
|
|
59387
|
+
if (!evaluateKrakenCompletionGate("build").blocked) markRepairSucceeded();
|
|
59388
|
+
}
|
|
59389
|
+
}
|
|
59390
|
+
progressRuntime.finish(pass.finalReason);
|
|
59391
|
+
const turnMetrics = collectKrakenTurnMetrics();
|
|
59392
|
+
if (turnMetrics && opts.output === "json") {
|
|
59393
|
+
emitEvent(createBrainEvent("kraken_metrics", sessionId2, { metrics: turnMetrics }));
|
|
59394
|
+
}
|
|
58159
59395
|
if (opts.output === "plain" && pass.textBuffer.length > 0) {
|
|
58160
59396
|
process.stdout.write(pass.textBuffer.join(""));
|
|
58161
59397
|
}
|