zelari-code 1.48.0 → 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 +1369 -111
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/provider/openai-compatible.js +40 -6
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/provider/openai-compatible.timeout.test.js +72 -0
- package/dist/cli/provider/openai-compatible.timeout.test.js.map +1 -0
- 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 +2 -2
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,
|
|
@@ -29131,6 +29201,7 @@ __export(openai_compatible_exports, {
|
|
|
29131
29201
|
parseCachedPromptTokens: () => parseCachedPromptTokens,
|
|
29132
29202
|
providerConfigFor: () => providerConfigFor,
|
|
29133
29203
|
providerFromEnv: () => providerFromEnv,
|
|
29204
|
+
readChunkWithTimeout: () => readChunkWithTimeout,
|
|
29134
29205
|
resolveActiveProvider: () => resolveActiveProvider,
|
|
29135
29206
|
resolveBaseUrl: () => resolveBaseUrl
|
|
29136
29207
|
});
|
|
@@ -29156,13 +29227,20 @@ async function readChunkWithTimeout(reader, opts) {
|
|
|
29156
29227
|
if (opts.signal?.aborted) {
|
|
29157
29228
|
throw new Error("aborted");
|
|
29158
29229
|
}
|
|
29159
|
-
const
|
|
29230
|
+
const now = Date.now();
|
|
29231
|
+
const remaining = opts.deadlineMs - now;
|
|
29160
29232
|
if (remaining <= 0) {
|
|
29161
29233
|
throw new Error(
|
|
29162
29234
|
`Provider stream exceeded max duration (${Math.round(PROVIDER_STREAM_MAX_MS / 1e3)}s). Raise ZELARI_PROVIDER_STREAM_MAX_MS if needed.`
|
|
29163
29235
|
);
|
|
29164
29236
|
}
|
|
29165
|
-
const
|
|
29237
|
+
const idleElapsed = now - opts.lastUsefulAt();
|
|
29238
|
+
if (idleElapsed >= opts.idleMs) {
|
|
29239
|
+
throw new Error(
|
|
29240
|
+
`Provider stream idle for ${Math.round(idleElapsed / 1e3)}s (no content tokens \u2014 keep-alive frames don't count). The model/gateway stalled \u2014 try again or switch model. Override with ZELARI_PROVIDER_STREAM_IDLE_MS.`
|
|
29241
|
+
);
|
|
29242
|
+
}
|
|
29243
|
+
const waitMs = Math.min(opts.idleMs - idleElapsed, remaining);
|
|
29166
29244
|
let idleTimer;
|
|
29167
29245
|
let onAbort;
|
|
29168
29246
|
try {
|
|
@@ -29449,6 +29527,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
29449
29527
|
if (args === null) continue;
|
|
29450
29528
|
toolCallAccumulator.delete(idx);
|
|
29451
29529
|
emittedToolCall = true;
|
|
29530
|
+
markUseful();
|
|
29452
29531
|
yield {
|
|
29453
29532
|
kind: "tool_call",
|
|
29454
29533
|
toolCallId: existing.id || `tc-${idx}`,
|
|
@@ -29459,6 +29538,10 @@ function openaiCompatibleProvider(config2) {
|
|
|
29459
29538
|
toolCallAccumulator.clear();
|
|
29460
29539
|
};
|
|
29461
29540
|
const streamDeadline = Date.now() + PROVIDER_STREAM_MAX_MS;
|
|
29541
|
+
let lastUsefulAt = Date.now();
|
|
29542
|
+
const markUseful = () => {
|
|
29543
|
+
lastUsefulAt = Date.now();
|
|
29544
|
+
};
|
|
29462
29545
|
try {
|
|
29463
29546
|
while (true) {
|
|
29464
29547
|
let chunk;
|
|
@@ -29466,7 +29549,8 @@ function openaiCompatibleProvider(config2) {
|
|
|
29466
29549
|
chunk = await readChunkWithTimeout(reader, {
|
|
29467
29550
|
idleMs: PROVIDER_STREAM_IDLE_MS,
|
|
29468
29551
|
deadlineMs: streamDeadline,
|
|
29469
|
-
signal: params.signal
|
|
29552
|
+
signal: params.signal,
|
|
29553
|
+
lastUsefulAt: () => lastUsefulAt
|
|
29470
29554
|
});
|
|
29471
29555
|
} catch (err) {
|
|
29472
29556
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -29507,6 +29591,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
29507
29591
|
const completionTokens = typeof parsed.usage.completion_tokens === "number" ? parsed.usage.completion_tokens : 0;
|
|
29508
29592
|
const totalTokens = typeof parsed.usage.total_tokens === "number" ? parsed.usage.total_tokens : promptTokens + completionTokens;
|
|
29509
29593
|
const cachedPromptTokens = parseCachedPromptTokens(parsed.usage);
|
|
29594
|
+
markUseful();
|
|
29510
29595
|
yield {
|
|
29511
29596
|
kind: "usage",
|
|
29512
29597
|
usage: {
|
|
@@ -29518,10 +29603,12 @@ function openaiCompatibleProvider(config2) {
|
|
|
29518
29603
|
};
|
|
29519
29604
|
}
|
|
29520
29605
|
if (typeof delta?.content === "string" && delta.content.length > 0) {
|
|
29606
|
+
markUseful();
|
|
29521
29607
|
yield { kind: "text", delta: delta.content };
|
|
29522
29608
|
}
|
|
29523
29609
|
const reasoning = delta?.reasoning_content ?? delta?.reasoning;
|
|
29524
29610
|
if (typeof reasoning === "string" && reasoning.length > 0) {
|
|
29611
|
+
markUseful();
|
|
29525
29612
|
yield { kind: "thinking", delta: reasoning };
|
|
29526
29613
|
}
|
|
29527
29614
|
const details = delta?.reasoning_details;
|
|
@@ -29533,9 +29620,13 @@ function openaiCompatibleProvider(config2) {
|
|
|
29533
29620
|
if (t.startsWith(reasoningDetailsBuf)) {
|
|
29534
29621
|
const piece = t.slice(reasoningDetailsBuf.length);
|
|
29535
29622
|
reasoningDetailsBuf = t;
|
|
29536
|
-
if (piece.length > 0)
|
|
29623
|
+
if (piece.length > 0) {
|
|
29624
|
+
markUseful();
|
|
29625
|
+
yield { kind: "thinking", delta: piece };
|
|
29626
|
+
}
|
|
29537
29627
|
} else {
|
|
29538
29628
|
reasoningDetailsBuf += t;
|
|
29629
|
+
markUseful();
|
|
29539
29630
|
yield { kind: "thinking", delta: t };
|
|
29540
29631
|
}
|
|
29541
29632
|
}
|
|
@@ -29556,6 +29647,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
29556
29647
|
}
|
|
29557
29648
|
if (choice?.finish_reason) {
|
|
29558
29649
|
yield* flushToolAccumulator();
|
|
29650
|
+
markUseful();
|
|
29559
29651
|
const reason = choice.finish_reason === "stop" && emittedToolCall ? "tool_calls" : choice.finish_reason;
|
|
29560
29652
|
yield { kind: "finish", reason };
|
|
29561
29653
|
}
|
|
@@ -31133,6 +31225,362 @@ var init_krakenWorktree = __esm({
|
|
|
31133
31225
|
}
|
|
31134
31226
|
});
|
|
31135
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
|
+
|
|
31136
31584
|
// src/cli/tools/taskTool.ts
|
|
31137
31585
|
function resetTaskSpawnCount() {
|
|
31138
31586
|
const g = globalThis;
|
|
@@ -31162,6 +31610,21 @@ function buildTaskUserPrompt(args) {
|
|
|
31162
31610
|
}
|
|
31163
31611
|
return parts.join("\n");
|
|
31164
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
|
+
}
|
|
31165
31628
|
function systemPromptForAgent(agent) {
|
|
31166
31629
|
if (agent === "general") return GENERAL_PROMPT;
|
|
31167
31630
|
if (agent === "verify") return VERIFY_PROMPT;
|
|
@@ -31187,6 +31650,7 @@ async function runSubAgent(harness, opts = {}) {
|
|
|
31187
31650
|
let current = "";
|
|
31188
31651
|
let lastCompleted = "";
|
|
31189
31652
|
let error51;
|
|
31653
|
+
let usage;
|
|
31190
31654
|
if (signal?.aborted) return { result: "", aborted: true };
|
|
31191
31655
|
for await (const ev of harness.run()) {
|
|
31192
31656
|
if (signal?.aborted) {
|
|
@@ -31205,6 +31669,16 @@ async function runSubAgent(harness, opts = {}) {
|
|
|
31205
31669
|
break;
|
|
31206
31670
|
case "message_end":
|
|
31207
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
|
+
}
|
|
31208
31682
|
current = "";
|
|
31209
31683
|
break;
|
|
31210
31684
|
case "error":
|
|
@@ -31215,7 +31689,7 @@ async function runSubAgent(harness, opts = {}) {
|
|
|
31215
31689
|
}
|
|
31216
31690
|
}
|
|
31217
31691
|
const result = (lastCompleted || current).trim();
|
|
31218
|
-
return { result, ...error51 ? { error: error51 } : {} };
|
|
31692
|
+
return { result, ...error51 ? { error: error51 } : {}, ...usage ? { usage } : {} };
|
|
31219
31693
|
}
|
|
31220
31694
|
async function runTentacle(opts) {
|
|
31221
31695
|
const { deps, args, agent, thoroughness, parentCwd, sessionId: sessionId2 } = opts;
|
|
@@ -31290,7 +31764,7 @@ async function runTentacle(opts) {
|
|
|
31290
31764
|
const userContent = buildTaskUserPrompt({
|
|
31291
31765
|
prompt: args.prompt,
|
|
31292
31766
|
scope: args.scope,
|
|
31293
|
-
acceptance: args.acceptance
|
|
31767
|
+
acceptance: withKrakenRequiredChecks(agent, args.acceptance)
|
|
31294
31768
|
});
|
|
31295
31769
|
const maxToolCalls = maxToolCallsForThoroughness(thoroughness, agent);
|
|
31296
31770
|
const runCwd = sub.cwd || effectiveCwd;
|
|
@@ -31325,7 +31799,7 @@ async function runTentacle(opts) {
|
|
|
31325
31799
|
error: `task: failed to start sub-agent \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
31326
31800
|
};
|
|
31327
31801
|
}
|
|
31328
|
-
const { result, error: error51, aborted: aborted2 } = await runSubAgent(harness, {
|
|
31802
|
+
const { result, error: error51, aborted: aborted2, usage } = await runSubAgent(harness, {
|
|
31329
31803
|
...opts.signal ? { signal: opts.signal } : {}
|
|
31330
31804
|
});
|
|
31331
31805
|
const durationMs = Date.now() - started;
|
|
@@ -31426,19 +31900,50 @@ ${verifyHintForGeneral(args.acceptance)}`;
|
|
|
31426
31900
|
model: sub.model,
|
|
31427
31901
|
result,
|
|
31428
31902
|
footer,
|
|
31903
|
+
...usage ? { usage } : {},
|
|
31429
31904
|
worktreePath: worktree?.path ?? null,
|
|
31430
31905
|
worktreeHandle: worktree
|
|
31431
31906
|
};
|
|
31432
31907
|
}
|
|
31433
|
-
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;
|
|
31434
31916
|
return {
|
|
31435
31917
|
name: "task",
|
|
31436
|
-
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.` : ""),
|
|
31437
31920
|
permissions: ["read", "network", "write", "execute"],
|
|
31438
31921
|
timeoutMs: 3e5,
|
|
31439
|
-
inputSchema:
|
|
31922
|
+
inputSchema: inputSchema2,
|
|
31440
31923
|
execute: async (args, ctx) => {
|
|
31441
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
|
+
}
|
|
31442
31947
|
const thoroughness = args.thoroughness ?? "medium";
|
|
31443
31948
|
const sessionId2 = ctx.sessionId || "default";
|
|
31444
31949
|
const parentCwd = ctx.cwd || process.cwd();
|
|
@@ -31461,8 +31966,51 @@ function createTaskTool(deps) {
|
|
|
31461
31966
|
agent,
|
|
31462
31967
|
thoroughness,
|
|
31463
31968
|
parentCwd,
|
|
31464
|
-
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
|
+
} : {}
|
|
31465
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
|
+
}
|
|
31466
32014
|
if (!res.ok) return typedErr(res.error);
|
|
31467
32015
|
return typedOk({
|
|
31468
32016
|
result: `[sub-agent:${res.agent}/${res.thoroughness} model=${res.model}]
|
|
@@ -31472,7 +32020,7 @@ ${res.result}${res.footer}`,
|
|
|
31472
32020
|
}
|
|
31473
32021
|
};
|
|
31474
32022
|
}
|
|
31475
|
-
var EXPLORE_PROMPT, GENERAL_PROMPT, VERIFY_PROMPT, TaskArgsSchema;
|
|
32023
|
+
var EXPLORE_PROMPT, GENERAL_PROMPT, VERIFY_PROMPT, TaskArgsSchema, TaskPurposeSchema, TaskArgsWithPurposeSchema;
|
|
31476
32024
|
var init_taskTool = __esm({
|
|
31477
32025
|
"src/cli/tools/taskTool.ts"() {
|
|
31478
32026
|
"use strict";
|
|
@@ -31481,6 +32029,9 @@ var init_taskTool = __esm({
|
|
|
31481
32029
|
init_krakenRadio();
|
|
31482
32030
|
init_krakenWorktree();
|
|
31483
32031
|
init_krakenLive();
|
|
32032
|
+
init_candidateRegistry();
|
|
32033
|
+
init_verifyReport();
|
|
32034
|
+
init_metrics2();
|
|
31484
32035
|
EXPLORE_PROMPT = [
|
|
31485
32036
|
"You are a focused EXPLORE tentacle of Kraken (parent super-agent).",
|
|
31486
32037
|
"READ-ONLY tools only (read, list, grep, fetch). No edits, no shell.",
|
|
@@ -31506,7 +32057,16 @@ var init_taskTool = __esm({
|
|
|
31506
32057
|
"You may read files and run test/build commands via bash. Prefer",
|
|
31507
32058
|
"targeted checks over full suite when possible.",
|
|
31508
32059
|
"Report: pass/fail, commands run, key output, and gaps vs Acceptance criteria.",
|
|
31509
|
-
"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."
|
|
31510
32070
|
].join("\n");
|
|
31511
32071
|
TaskArgsSchema = external_exports.object({
|
|
31512
32072
|
description: external_exports.string().min(1).describe("A 3-6 word label for the sub-task (for logs/UI)."),
|
|
@@ -31524,6 +32084,336 @@ var init_taskTool = __esm({
|
|
|
31524
32084
|
"Optional acceptance checklist (contract). Appended to the prompt as Acceptance criteria."
|
|
31525
32085
|
)
|
|
31526
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
|
+
});
|
|
31527
32417
|
}
|
|
31528
32418
|
});
|
|
31529
32419
|
|
|
@@ -32248,13 +33138,13 @@ async function withPlanStore(projectRoot, fn) {
|
|
|
32248
33138
|
return out;
|
|
32249
33139
|
});
|
|
32250
33140
|
}
|
|
32251
|
-
function nextPlanTaskId(
|
|
32252
|
-
const maxExisting =
|
|
33141
|
+
function nextPlanTaskId(store6) {
|
|
33142
|
+
const maxExisting = store6.tasks.reduce((max, t) => {
|
|
32253
33143
|
const m = /^t(\d+)$/.exec(t.id);
|
|
32254
33144
|
return m ? Math.max(max, parseInt(m[1], 10)) : max;
|
|
32255
33145
|
}, 0);
|
|
32256
|
-
|
|
32257
|
-
return `t${
|
|
33146
|
+
store6.counter = Math.max(store6.counter, maxExisting) + 1;
|
|
33147
|
+
return `t${store6.counter}`;
|
|
32258
33148
|
}
|
|
32259
33149
|
function writePlanTaskArtifact(rootDir, task) {
|
|
32260
33150
|
const path56 = join15(rootDir, "plan-tasks", `${task.id}.md`);
|
|
@@ -32434,14 +33324,14 @@ function createPlanTaskTools(opts) {
|
|
|
32434
33324
|
inputSchema: CreateSchema,
|
|
32435
33325
|
execute: async (input) => {
|
|
32436
33326
|
try {
|
|
32437
|
-
const res = await withPlanStore(projectRoot, (
|
|
32438
|
-
if (
|
|
33327
|
+
const res = await withPlanStore(projectRoot, (store6) => {
|
|
33328
|
+
if (store6.tasks.length >= PLAN_MAX_TASKS) {
|
|
32439
33329
|
return typedErr(
|
|
32440
|
-
`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}).`
|
|
32441
33331
|
);
|
|
32442
33332
|
}
|
|
32443
33333
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32444
|
-
const id = nextPlanTaskId(
|
|
33334
|
+
const id = nextPlanTaskId(store6);
|
|
32445
33335
|
const task = {
|
|
32446
33336
|
id,
|
|
32447
33337
|
title: input.title.trim().slice(0, PLAN_TITLE_MAX),
|
|
@@ -32455,8 +33345,8 @@ function createPlanTaskTools(opts) {
|
|
|
32455
33345
|
createdAt: now,
|
|
32456
33346
|
updatedAt: now
|
|
32457
33347
|
};
|
|
32458
|
-
|
|
32459
|
-
writePlanTaskArtifact(
|
|
33348
|
+
store6.tasks.push(task);
|
|
33349
|
+
writePlanTaskArtifact(store6.rootDir, task);
|
|
32460
33350
|
return typedOk({ id, task });
|
|
32461
33351
|
});
|
|
32462
33352
|
if (res.ok) {
|
|
@@ -32480,8 +33370,8 @@ function createPlanTaskTools(opts) {
|
|
|
32480
33370
|
inputSchema: UpdateSchema,
|
|
32481
33371
|
execute: async (input) => {
|
|
32482
33372
|
try {
|
|
32483
|
-
const res = await withPlanStore(projectRoot, (
|
|
32484
|
-
const task =
|
|
33373
|
+
const res = await withPlanStore(projectRoot, (store6) => {
|
|
33374
|
+
const task = store6.tasks.find((t) => t.id === input.id);
|
|
32485
33375
|
if (!task) {
|
|
32486
33376
|
return typedErr(
|
|
32487
33377
|
`PLAN_TASK_NOT_FOUND: no task with id "${input.id}" in .zelari/plan.json (call task_list for current ids).`
|
|
@@ -32504,7 +33394,7 @@ ${input.appendNote}` : input.appendNote;
|
|
|
32504
33394
|
task.notes = merged.slice(-PLAN_NOTES_MAX);
|
|
32505
33395
|
}
|
|
32506
33396
|
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
32507
|
-
writePlanTaskArtifact(
|
|
33397
|
+
writePlanTaskArtifact(store6.rootDir, task);
|
|
32508
33398
|
return typedOk({ task });
|
|
32509
33399
|
});
|
|
32510
33400
|
if (res.ok) {
|
|
@@ -32529,21 +33419,21 @@ ${input.appendNote}` : input.appendNote;
|
|
|
32529
33419
|
execute: async (input) => {
|
|
32530
33420
|
try {
|
|
32531
33421
|
let allPayloads = [];
|
|
32532
|
-
const res = await withPlanStore(projectRoot, (
|
|
32533
|
-
allPayloads =
|
|
32534
|
-
const filtered =
|
|
33422
|
+
const res = await withPlanStore(projectRoot, (store6) => {
|
|
33423
|
+
allPayloads = store6.tasks.map(toTaskPayload);
|
|
33424
|
+
const filtered = store6.tasks.filter(
|
|
32535
33425
|
(t) => (input.status === void 0 || t.status === input.status) && (input.phaseId === void 0 || t.phaseId === input.phaseId)
|
|
32536
33426
|
);
|
|
32537
|
-
const done =
|
|
33427
|
+
const done = store6.tasks.filter(
|
|
32538
33428
|
(t) => t.status === "completed" || t.status === "cancelled"
|
|
32539
33429
|
).length;
|
|
32540
33430
|
const formatted = filtered.length === 0 ? "(no matching workspace tasks)" : filtered.map(taskSummaryLine).join("\n");
|
|
32541
33431
|
return typedOk({
|
|
32542
33432
|
tasks: filtered,
|
|
32543
|
-
total:
|
|
33433
|
+
total: store6.tasks.length,
|
|
32544
33434
|
done,
|
|
32545
33435
|
formatted: `${formatted}
|
|
32546
|
-
(done/total: ${done}/${
|
|
33436
|
+
(done/total: ${done}/${store6.tasks.length})`
|
|
32547
33437
|
});
|
|
32548
33438
|
});
|
|
32549
33439
|
if (res.ok) {
|
|
@@ -35673,7 +36563,7 @@ import path31 from "node:path";
|
|
|
35673
36563
|
function trustStorePath() {
|
|
35674
36564
|
return _overrideStorePath ?? path31.join(homedir8(), ".zelari-code", "trust.json");
|
|
35675
36565
|
}
|
|
35676
|
-
function
|
|
36566
|
+
function normalize3(p3) {
|
|
35677
36567
|
const resolved = path31.resolve(p3);
|
|
35678
36568
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
35679
36569
|
}
|
|
@@ -35687,11 +36577,11 @@ function readStore3() {
|
|
|
35687
36577
|
return DEFAULT_STORE;
|
|
35688
36578
|
}
|
|
35689
36579
|
}
|
|
35690
|
-
function writeStore3(
|
|
36580
|
+
function writeStore3(store6) {
|
|
35691
36581
|
const p3 = trustStorePath();
|
|
35692
36582
|
try {
|
|
35693
36583
|
mkdirSync14(path31.dirname(p3), { recursive: true });
|
|
35694
|
-
writeFileSync16(p3, JSON.stringify(
|
|
36584
|
+
writeFileSync16(p3, JSON.stringify(store6, null, 2), "utf8");
|
|
35695
36585
|
} catch (err) {
|
|
35696
36586
|
throw new Error(
|
|
35697
36587
|
`failed to persist trust store ${p3}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -35710,26 +36600,26 @@ function isFolderTrusted(folderPath) {
|
|
|
35710
36600
|
const env = envTrustedFolder();
|
|
35711
36601
|
if (env === "all") return true;
|
|
35712
36602
|
if (env === "none") return false;
|
|
35713
|
-
if (env) return
|
|
35714
|
-
const target =
|
|
35715
|
-
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);
|
|
35716
36606
|
}
|
|
35717
36607
|
function trustFolder(folderPath) {
|
|
35718
|
-
const
|
|
36608
|
+
const store6 = readStore3();
|
|
35719
36609
|
const normalized = path31.resolve(folderPath);
|
|
35720
|
-
if (!
|
|
35721
|
-
|
|
35722
|
-
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);
|
|
35723
36613
|
}
|
|
35724
36614
|
return { ok: true, path: normalized };
|
|
35725
36615
|
}
|
|
35726
36616
|
function untrustFolder(folderPath) {
|
|
35727
|
-
const
|
|
35728
|
-
const target =
|
|
35729
|
-
const before =
|
|
35730
|
-
|
|
35731
|
-
if (
|
|
35732
|
-
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);
|
|
35733
36623
|
return { ok: true, removed: true };
|
|
35734
36624
|
}
|
|
35735
36625
|
function listTrustedFolders() {
|
|
@@ -35884,19 +36774,19 @@ function applyTruncation(result, toolName) {
|
|
|
35884
36774
|
function evictOldest() {
|
|
35885
36775
|
let oldestKey = null;
|
|
35886
36776
|
let oldestTs = Infinity;
|
|
35887
|
-
for (const [key, entry] of
|
|
36777
|
+
for (const [key, entry] of store4) {
|
|
35888
36778
|
if (entry.ts < oldestTs) {
|
|
35889
36779
|
oldestTs = entry.ts;
|
|
35890
36780
|
oldestKey = key;
|
|
35891
36781
|
}
|
|
35892
36782
|
}
|
|
35893
|
-
if (oldestKey)
|
|
36783
|
+
if (oldestKey) store4.delete(oldestKey);
|
|
35894
36784
|
}
|
|
35895
36785
|
function cacheGet(key, now) {
|
|
35896
|
-
const entry =
|
|
36786
|
+
const entry = store4.get(key);
|
|
35897
36787
|
if (!entry) return null;
|
|
35898
36788
|
if (entry.expiresAt !== void 0 && entry.expiresAt <= now) {
|
|
35899
|
-
|
|
36789
|
+
store4.delete(key);
|
|
35900
36790
|
return null;
|
|
35901
36791
|
}
|
|
35902
36792
|
return cloneResult(entry.result) ?? entry.result;
|
|
@@ -35906,8 +36796,8 @@ function cachePut(key, result, now, ttlMs) {
|
|
|
35906
36796
|
if (resultBytes(result) > TOOL_CACHE_MAX_BYTES) return;
|
|
35907
36797
|
const cloned = cloneResult(result);
|
|
35908
36798
|
if (!cloned) return;
|
|
35909
|
-
if (
|
|
35910
|
-
|
|
36799
|
+
if (store4.size >= TOOL_CACHE_MAX_ENTRIES && !store4.has(key)) evictOldest();
|
|
36800
|
+
store4.set(key, {
|
|
35911
36801
|
result: cloned,
|
|
35912
36802
|
ts: now,
|
|
35913
36803
|
...ttlMs !== void 0 ? { expiresAt: now + ttlMs } : {}
|
|
@@ -35951,7 +36841,7 @@ function withResultCache(tool, options = {}) {
|
|
|
35951
36841
|
}
|
|
35952
36842
|
};
|
|
35953
36843
|
}
|
|
35954
|
-
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;
|
|
35955
36845
|
var init_toolResultCache = __esm({
|
|
35956
36846
|
"src/cli/toolResultCache.ts"() {
|
|
35957
36847
|
"use strict";
|
|
@@ -35959,7 +36849,7 @@ var init_toolResultCache = __esm({
|
|
|
35959
36849
|
TOOL_CACHE_MAX_ENTRIES = 200;
|
|
35960
36850
|
TOOL_CACHE_MAX_BYTES = 256 * 1024;
|
|
35961
36851
|
TOOL_CACHE_DEFAULT_TTL_MS = 5 * 60 * 1e3;
|
|
35962
|
-
|
|
36852
|
+
store4 = /* @__PURE__ */ new Map();
|
|
35963
36853
|
}
|
|
35964
36854
|
});
|
|
35965
36855
|
|
|
@@ -36230,11 +37120,20 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
36230
37120
|
});
|
|
36231
37121
|
}
|
|
36232
37122
|
}
|
|
36233
|
-
const enableTask = options.enableTask !== false &&
|
|
37123
|
+
const enableTask = options.enableTask !== false && options.readOnly !== true && !verifyMode && profile === "full" && (!options.planMode || options.planExploreTask !== false);
|
|
36234
37124
|
if (enableTask) {
|
|
36235
|
-
const taskTool = createTaskTool(
|
|
36236
|
-
|
|
36237
|
-
|
|
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
|
+
);
|
|
36238
37137
|
registry4.register(withPerm(taskTool));
|
|
36239
37138
|
tools.push({
|
|
36240
37139
|
name: taskTool.name,
|
|
@@ -36242,6 +37141,31 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
36242
37141
|
permissions: taskTool.permissions ?? []
|
|
36243
37142
|
});
|
|
36244
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
|
+
}
|
|
36245
37169
|
if (process.env.ZELARI_LSP !== "0" && options.lspProvider !== null) {
|
|
36246
37170
|
const lspTools = options.lspProvider ? createLspTools(options.lspProvider, root) : createLspTools(getSharedLspManager(root), root);
|
|
36247
37171
|
for (const t of lspTools) {
|
|
@@ -36524,6 +37448,7 @@ var init_toolRegistry = __esm({
|
|
|
36524
37448
|
init_auditLogger();
|
|
36525
37449
|
init_engine();
|
|
36526
37450
|
init_taskTool();
|
|
37451
|
+
init_krakenSelectTool();
|
|
36527
37452
|
init_askUser();
|
|
36528
37453
|
init_skillTool();
|
|
36529
37454
|
init_todoTools();
|
|
@@ -36540,6 +37465,7 @@ var init_toolRegistry = __esm({
|
|
|
36540
37465
|
init_worldModel();
|
|
36541
37466
|
init_openai_compatible();
|
|
36542
37467
|
init_resolveStream();
|
|
37468
|
+
init_providerConfig();
|
|
36543
37469
|
init_toolPermissions();
|
|
36544
37470
|
init_lifecycleHooks();
|
|
36545
37471
|
init_toolResultCache();
|
|
@@ -36604,10 +37530,10 @@ function isStateEnabled(env = process.env) {
|
|
|
36604
37530
|
}
|
|
36605
37531
|
async function getStateStore(projectRoot, env = process.env) {
|
|
36606
37532
|
if (!isStateEnabled(env)) return new NoopDurableStateStore();
|
|
36607
|
-
const
|
|
37533
|
+
const store6 = new FileDurableStateStore();
|
|
36608
37534
|
try {
|
|
36609
|
-
await
|
|
36610
|
-
return
|
|
37535
|
+
await store6.init(projectRoot);
|
|
37536
|
+
return store6;
|
|
36611
37537
|
} catch {
|
|
36612
37538
|
return new NoopDurableStateStore();
|
|
36613
37539
|
}
|
|
@@ -37256,24 +38182,24 @@ var init_historyCompaction = __esm({
|
|
|
37256
38182
|
|
|
37257
38183
|
// src/cli/budget/requestSnapshotStore.ts
|
|
37258
38184
|
function recordRequestSnapshot(sessionId2, snapshot) {
|
|
37259
|
-
|
|
38185
|
+
store5.set(sessionId2, { snapshot });
|
|
37260
38186
|
}
|
|
37261
38187
|
function recordRequestUsage(sessionId2, usage) {
|
|
37262
|
-
const entry =
|
|
38188
|
+
const entry = store5.get(sessionId2);
|
|
37263
38189
|
if (!entry) return;
|
|
37264
38190
|
entry.usage = usage;
|
|
37265
38191
|
}
|
|
37266
38192
|
function getRequestSnapshotWithUsage(sessionId2) {
|
|
37267
|
-
return
|
|
38193
|
+
return store5.get(sessionId2) ?? null;
|
|
37268
38194
|
}
|
|
37269
38195
|
function clearAllRequestSnapshots() {
|
|
37270
|
-
|
|
38196
|
+
store5.clear();
|
|
37271
38197
|
}
|
|
37272
|
-
var
|
|
38198
|
+
var store5;
|
|
37273
38199
|
var init_requestSnapshotStore = __esm({
|
|
37274
38200
|
"src/cli/budget/requestSnapshotStore.ts"() {
|
|
37275
38201
|
"use strict";
|
|
37276
|
-
|
|
38202
|
+
store5 = /* @__PURE__ */ new Map();
|
|
37277
38203
|
}
|
|
37278
38204
|
});
|
|
37279
38205
|
|
|
@@ -38420,8 +39346,8 @@ async function loadDurableContext(projectRoot, opts) {
|
|
|
38420
39346
|
return cache2.text;
|
|
38421
39347
|
}
|
|
38422
39348
|
try {
|
|
38423
|
-
const
|
|
38424
|
-
const text = await
|
|
39349
|
+
const store6 = await getStateStore(projectRoot, env);
|
|
39350
|
+
const text = await store6.materializeContext(void 0, opts?.maxChars);
|
|
38425
39351
|
cache2 = { text: text || "", at: now, projectRoot };
|
|
38426
39352
|
return cache2.text;
|
|
38427
39353
|
} catch {
|
|
@@ -41229,7 +42155,7 @@ __export(commitHelpers_exports, {
|
|
|
41229
42155
|
});
|
|
41230
42156
|
async function tryStateCommit(args) {
|
|
41231
42157
|
try {
|
|
41232
|
-
const
|
|
42158
|
+
const store6 = args.store ?? await getStateStore(args.projectRoot, args.env);
|
|
41233
42159
|
let workspaceCheckpointId = args.workspaceCheckpointId;
|
|
41234
42160
|
if (!workspaceCheckpointId && args.withCheckpoint && (args.env ?? process.env).ZELARI_CHECKPOINT !== "0") {
|
|
41235
42161
|
const cp = await createCheckpoint(
|
|
@@ -41238,7 +42164,7 @@ async function tryStateCommit(args) {
|
|
|
41238
42164
|
);
|
|
41239
42165
|
if (cp.ok) workspaceCheckpointId = cp.value.id;
|
|
41240
42166
|
}
|
|
41241
|
-
const meta3 = await
|
|
42167
|
+
const meta3 = await store6.commit({
|
|
41242
42168
|
mode: args.mode,
|
|
41243
42169
|
label: args.label,
|
|
41244
42170
|
layer: args.layer,
|
|
@@ -45988,6 +46914,9 @@ function parseSetConfigFlags(argv) {
|
|
|
45988
46914
|
let endpoint;
|
|
45989
46915
|
let thinking;
|
|
45990
46916
|
let endpointClear = false;
|
|
46917
|
+
let verifierProvider;
|
|
46918
|
+
let verifierModel;
|
|
46919
|
+
let verifierClear = false;
|
|
45991
46920
|
for (let i = 0; i < argv.length; i++) {
|
|
45992
46921
|
const arg = argv[i];
|
|
45993
46922
|
if (arg === "--provider") {
|
|
@@ -46004,12 +46933,20 @@ function parseSetConfigFlags(argv) {
|
|
|
46004
46933
|
i++;
|
|
46005
46934
|
} else if (arg === "--endpoint-clear") {
|
|
46006
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;
|
|
46007
46944
|
}
|
|
46008
46945
|
}
|
|
46009
|
-
if (!provider && !model && !endpoint && !endpointClear && !thinking) {
|
|
46946
|
+
if (!provider && !model && !endpoint && !endpointClear && !thinking && !verifierProvider && !verifierModel && !verifierClear) {
|
|
46010
46947
|
return {
|
|
46011
46948
|
request: null,
|
|
46012
|
-
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"
|
|
46013
46950
|
};
|
|
46014
46951
|
}
|
|
46015
46952
|
if (provider !== void 0 && provider.trim().length === 0) {
|
|
@@ -46021,6 +46958,18 @@ function parseSetConfigFlags(argv) {
|
|
|
46021
46958
|
if (endpoint !== void 0 && endpoint.trim().length === 0) {
|
|
46022
46959
|
return { request: null, error: "--endpoint cannot be empty" };
|
|
46023
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
|
+
}
|
|
46024
46973
|
if (endpoint && endpointClear) {
|
|
46025
46974
|
return { request: null, error: "--endpoint and --endpoint-clear conflict" };
|
|
46026
46975
|
}
|
|
@@ -46033,7 +46982,10 @@ function parseSetConfigFlags(argv) {
|
|
|
46033
46982
|
model: model?.trim(),
|
|
46034
46983
|
endpoint: endpoint?.trim(),
|
|
46035
46984
|
endpointClear: endpointClear || void 0,
|
|
46036
|
-
thinking: thinking?.trim().toLowerCase()
|
|
46985
|
+
thinking: thinking?.trim().toLowerCase(),
|
|
46986
|
+
verifierProvider: verifierProvider?.trim(),
|
|
46987
|
+
verifierModel: verifierModel?.trim(),
|
|
46988
|
+
verifierClear: verifierClear || void 0
|
|
46037
46989
|
}
|
|
46038
46990
|
};
|
|
46039
46991
|
}
|
|
@@ -46122,6 +47074,7 @@ function buildDesktopConfigSnapshot() {
|
|
|
46122
47074
|
activeProviderId: config2.activeProviderId,
|
|
46123
47075
|
modelByProvider: { ...config2.modelByProvider },
|
|
46124
47076
|
providers,
|
|
47077
|
+
krakenVerifier: config2.krakenVerifier ?? null,
|
|
46125
47078
|
cliVersion: getCurrentVersion(),
|
|
46126
47079
|
configPaths: {
|
|
46127
47080
|
provider: getProviderConfigPath(),
|
|
@@ -46160,11 +47113,25 @@ function applySetConfig(req) {
|
|
|
46160
47113
|
if (req.thinking) {
|
|
46161
47114
|
setThinkingForProvider(targetProvider, parseThinkingSpec(req.thinking));
|
|
46162
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
|
+
}
|
|
46163
47129
|
const after = getProviderConfig();
|
|
47130
|
+
const vf = after.krakenVerifier;
|
|
46164
47131
|
const ep = getCustomEndpoint(after.activeProviderId);
|
|
46165
47132
|
return {
|
|
46166
47133
|
ok: true,
|
|
46167
|
-
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")
|
|
46168
47135
|
};
|
|
46169
47136
|
} catch (err) {
|
|
46170
47137
|
return {
|
|
@@ -51268,7 +52235,180 @@ init_shellResolver();
|
|
|
51268
52235
|
init_keyStore();
|
|
51269
52236
|
init_providerConfig();
|
|
51270
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
|
|
51271
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();
|
|
51272
52412
|
|
|
51273
52413
|
// src/cli/hooks/permissionPicker.ts
|
|
51274
52414
|
init_toolPermissions();
|
|
@@ -51623,6 +52763,8 @@ function useChatTurn(params) {
|
|
|
51623
52763
|
const dispatchPrompt = useCallback2(
|
|
51624
52764
|
async (userText, opts) => {
|
|
51625
52765
|
resetTaskSpawnCount();
|
|
52766
|
+
resetKrakenCandidates();
|
|
52767
|
+
resetKrakenTurnMetrics();
|
|
51626
52768
|
let envConfig;
|
|
51627
52769
|
let harness;
|
|
51628
52770
|
let historySeedLen = 0;
|
|
@@ -51706,10 +52848,30 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
51706
52848
|
}) : void 0;
|
|
51707
52849
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
51708
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
|
+
} : {},
|
|
51709
52860
|
onAskUser,
|
|
51710
52861
|
onPermissionAsk,
|
|
51711
52862
|
permissionPolicy: defaultPermissionPolicy()
|
|
51712
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();
|
|
51713
52875
|
const baseProviderStream = localCliProvider ?? buildProviderStream(envConfig);
|
|
51714
52876
|
let providerStream;
|
|
51715
52877
|
if (localCliProvider) {
|
|
@@ -51900,6 +53062,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
51900
53062
|
customPromptModules: [
|
|
51901
53063
|
KRAKEN_IDENTITY_MODULE,
|
|
51902
53064
|
KRAKEN_LEAD_PLAYBOOK_MODULE,
|
|
53065
|
+
...krakenSelectionPlaybook(true),
|
|
51903
53066
|
languageModule
|
|
51904
53067
|
],
|
|
51905
53068
|
agentSkillConfigs: []
|
|
@@ -51968,6 +53131,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
51968
53131
|
harnessRef.current = harness2;
|
|
51969
53132
|
setQueueCount(harness2.queueLength);
|
|
51970
53133
|
let assistantContent = "";
|
|
53134
|
+
let krakenRepairEnqueued = false;
|
|
51971
53135
|
let streamContent = "";
|
|
51972
53136
|
const streamScrub = createStreamScrubber(16);
|
|
51973
53137
|
const toolNameById = /* @__PURE__ */ new Map();
|
|
@@ -51976,6 +53140,29 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
51976
53140
|
let realUsage = null;
|
|
51977
53141
|
try {
|
|
51978
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
|
+
}
|
|
51979
53166
|
if (event.type === "message_end") {
|
|
51980
53167
|
if (event.usage) realUsage = event.usage;
|
|
51981
53168
|
if (event.usage) {
|
|
@@ -52148,6 +53335,14 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
52148
53335
|
}
|
|
52149
53336
|
}
|
|
52150
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
|
+
}
|
|
52151
53346
|
turnSucceeded = true;
|
|
52152
53347
|
} finally {
|
|
52153
53348
|
flushStreaming();
|
|
@@ -54145,12 +55340,12 @@ init_fileStateStore();
|
|
|
54145
55340
|
async function restoreDurableState(opts) {
|
|
54146
55341
|
const restoreTree = opts.restoreTree !== false;
|
|
54147
55342
|
try {
|
|
54148
|
-
const
|
|
55343
|
+
const store6 = opts.store ?? await getStateStore(opts.projectRoot);
|
|
54149
55344
|
let meta3;
|
|
54150
55345
|
if (opts.commitId) {
|
|
54151
|
-
meta3 = await
|
|
55346
|
+
meta3 = await store6.setHead(opts.commitId);
|
|
54152
55347
|
} else {
|
|
54153
|
-
meta3 = await
|
|
55348
|
+
meta3 = await store6.head();
|
|
54154
55349
|
if (!meta3) {
|
|
54155
55350
|
return {
|
|
54156
55351
|
ok: false,
|
|
@@ -54203,8 +55398,8 @@ function ago2(ms) {
|
|
|
54203
55398
|
return `${Math.round(s / 3600)}h ago`;
|
|
54204
55399
|
}
|
|
54205
55400
|
async function handleStateStatus(ctx) {
|
|
54206
|
-
const
|
|
54207
|
-
const head = await
|
|
55401
|
+
const store6 = await getStateStore(ctx.cwd);
|
|
55402
|
+
const head = await store6.head();
|
|
54208
55403
|
if (!head) {
|
|
54209
55404
|
appendSystem(
|
|
54210
55405
|
ctx.setMessages,
|
|
@@ -54212,9 +55407,9 @@ async function handleStateStatus(ctx) {
|
|
|
54212
55407
|
);
|
|
54213
55408
|
return;
|
|
54214
55409
|
}
|
|
54215
|
-
const discoveries = await
|
|
55410
|
+
const discoveries = await store6.loadDiscoveries(head.id);
|
|
54216
55411
|
const reusable = discoveries.filter((d) => d.reusable).length;
|
|
54217
|
-
const recent = await
|
|
55412
|
+
const recent = await store6.list(8);
|
|
54218
55413
|
const lines = recent.map((c, i) => {
|
|
54219
55414
|
const ver2 = c.verification.ran ? c.verification.ok ? "ok" : "fail" : "n/a";
|
|
54220
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)}` : "");
|
|
@@ -54233,9 +55428,9 @@ async function handleStateStatus(ctx) {
|
|
|
54233
55428
|
);
|
|
54234
55429
|
}
|
|
54235
55430
|
async function handleStateCommit(ctx, label) {
|
|
54236
|
-
const
|
|
55431
|
+
const store6 = await getStateStore(ctx.cwd);
|
|
54237
55432
|
try {
|
|
54238
|
-
const meta3 = await
|
|
55433
|
+
const meta3 = await store6.commit({
|
|
54239
55434
|
mode: "agent",
|
|
54240
55435
|
label: label?.trim() || "manual state commit",
|
|
54241
55436
|
layer: "manual",
|
|
@@ -54262,8 +55457,8 @@ async function handleStateCommit(ctx, label) {
|
|
|
54262
55457
|
}
|
|
54263
55458
|
}
|
|
54264
55459
|
async function handleStateShow(ctx, id) {
|
|
54265
|
-
const
|
|
54266
|
-
const meta3 = id ? await
|
|
55460
|
+
const store6 = await getStateStore(ctx.cwd);
|
|
55461
|
+
const meta3 = id ? await store6.get(id) : await store6.head();
|
|
54267
55462
|
if (!meta3) {
|
|
54268
55463
|
appendSystem(
|
|
54269
55464
|
ctx.setMessages,
|
|
@@ -54271,7 +55466,7 @@ async function handleStateShow(ctx, id) {
|
|
|
54271
55466
|
);
|
|
54272
55467
|
return;
|
|
54273
55468
|
}
|
|
54274
|
-
const text = await
|
|
55469
|
+
const text = await store6.materializeContext(meta3.id, 6e3);
|
|
54275
55470
|
appendSystem(ctx.setMessages, `[state] show ${meta3.id}
|
|
54276
55471
|
${text}`);
|
|
54277
55472
|
}
|
|
@@ -55781,14 +56976,14 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
55781
56976
|
}
|
|
55782
56977
|
function handleCouncilFeedback(ctx, memberId, score, note) {
|
|
55783
56978
|
try {
|
|
55784
|
-
const
|
|
55785
|
-
const entry =
|
|
56979
|
+
const store6 = new FeedbackStore();
|
|
56980
|
+
const entry = store6.record({
|
|
55786
56981
|
memberId,
|
|
55787
56982
|
score,
|
|
55788
56983
|
...note ? { note } : {},
|
|
55789
56984
|
...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
|
|
55790
56985
|
});
|
|
55791
|
-
const stats =
|
|
56986
|
+
const stats = store6.getStats(memberId);
|
|
55792
56987
|
appendSystem(
|
|
55793
56988
|
ctx.setMessages,
|
|
55794
56989
|
`[council-feedback] ${memberId} rated ${entry.score}/5 \u2014 running avg ${stats.avg.toFixed(2)} over ${stats.count} rating(s).`
|
|
@@ -57480,9 +58675,13 @@ function emitEvent(event) {
|
|
|
57480
58675
|
// src/cli/runHeadless.ts
|
|
57481
58676
|
init_harness();
|
|
57482
58677
|
init_dist();
|
|
58678
|
+
init_events2();
|
|
57483
58679
|
init_conversationContext();
|
|
57484
58680
|
init_council();
|
|
57485
58681
|
init_toolRegistry();
|
|
58682
|
+
init_candidateRegistry();
|
|
58683
|
+
init_metrics2();
|
|
58684
|
+
init_completionGate();
|
|
57486
58685
|
init_claudeProvider();
|
|
57487
58686
|
init_skills2();
|
|
57488
58687
|
init_envNumber();
|
|
@@ -57839,8 +59038,18 @@ async function registerHeadlessMcp(toolRegistry, opts) {
|
|
|
57839
59038
|
}
|
|
57840
59039
|
async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
57841
59040
|
const sessionId2 = crypto.randomUUID();
|
|
59041
|
+
resetKrakenCandidates();
|
|
59042
|
+
resetKrakenTurnMetrics();
|
|
57842
59043
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
57843
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(),
|
|
57844
59053
|
// ADR-0018 3b: upgrade plan-task domain events to first-class NDJSON
|
|
57845
59054
|
// BrainEvents. Rust envelopes every stdout line with runId/conversationId,
|
|
57846
59055
|
// so task events ride the same multiplexed channel as the rest.
|
|
@@ -57949,6 +59158,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
57949
59158
|
customPromptModules: [
|
|
57950
59159
|
KRAKEN_IDENTITY_MODULE,
|
|
57951
59160
|
KRAKEN_LEAD_PLAYBOOK_MODULE,
|
|
59161
|
+
...krakenSelectionPlaybook(opts.mode === "kraken"),
|
|
57952
59162
|
{
|
|
57953
59163
|
type: "language-policy",
|
|
57954
59164
|
title: "Response Language",
|
|
@@ -58013,6 +59223,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
58013
59223
|
const scrub = createStreamScrubber2();
|
|
58014
59224
|
try {
|
|
58015
59225
|
for await (const event of harness.run()) {
|
|
59226
|
+
progressRuntime.observe(event);
|
|
58016
59227
|
if (event.type === "message_start") {
|
|
58017
59228
|
scrub.reset();
|
|
58018
59229
|
}
|
|
@@ -58090,6 +59301,16 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
58090
59301
|
messages: harness.getMessages()
|
|
58091
59302
|
};
|
|
58092
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();
|
|
58093
59314
|
const initialMessages = [
|
|
58094
59315
|
...systemMessages,
|
|
58095
59316
|
...historySeed,
|
|
@@ -58126,6 +59347,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
58126
59347
|
...retryMessages,
|
|
58127
59348
|
{ role: "user", content: retryPrompt }
|
|
58128
59349
|
];
|
|
59350
|
+
progressRuntime.beginPass();
|
|
58129
59351
|
const retry = await runSinglePass(withSystem, `${sessionId2}-write-retry`);
|
|
58130
59352
|
pass = {
|
|
58131
59353
|
...retry,
|
|
@@ -58134,6 +59356,42 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
58134
59356
|
emittedWrites: pass.emittedWrites + retry.emittedWrites
|
|
58135
59357
|
};
|
|
58136
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
|
+
}
|
|
58137
59395
|
if (opts.output === "plain" && pass.textBuffer.length > 0) {
|
|
58138
59396
|
process.stdout.write(pass.textBuffer.join(""));
|
|
58139
59397
|
}
|