zelari-code 1.28.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/headless.js +14 -0
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js +11 -0
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/kraken/executor.js +725 -42
- package/dist/cli/kraken/executor.js.map +1 -1
- package/dist/cli/kraken/graphMemory.js +27 -5
- package/dist/cli/kraken/graphMemory.js.map +1 -1
- package/dist/cli/kraken/graphStatus.js +59 -0
- package/dist/cli/kraken/graphStatus.js.map +1 -1
- package/dist/cli/kraken/planner.js +189 -12
- package/dist/cli/kraken/planner.js.map +1 -1
- package/dist/cli/kraken/planner.test.js +43 -0
- package/dist/cli/kraken/planner.test.js.map +1 -0
- package/dist/cli/kraken/runtime/compile.js +73 -0
- package/dist/cli/kraken/runtime/compile.js.map +1 -0
- package/dist/cli/kraken/runtime/runScriptPlan.js +195 -0
- package/dist/cli/kraken/runtime/runScriptPlan.js.map +1 -0
- package/dist/cli/kraken/scriptPlanner.js +286 -0
- package/dist/cli/kraken/scriptPlanner.js.map +1 -0
- package/dist/cli/kraken/scriptPlanner.test.js +152 -0
- package/dist/cli/kraken/scriptPlanner.test.js.map +1 -0
- package/dist/cli/kraken/skillSuggest.js +97 -0
- package/dist/cli/kraken/skillSuggest.js.map +1 -0
- package/dist/cli/kraken/skillSuggest.test.js +157 -0
- package/dist/cli/kraken/skillSuggest.test.js.map +1 -0
- package/dist/cli/kraken/weaknessMeter.js +183 -0
- package/dist/cli/kraken/weaknessMeter.js.map +1 -0
- package/dist/cli/kraken/weaknessMeter.test.js +212 -0
- package/dist/cli/kraken/weaknessMeter.test.js.map +1 -0
- package/dist/cli/kraken/workbench.js +296 -0
- package/dist/cli/kraken/workbench.js.map +1 -0
- package/dist/cli/kraken/workbench.test.js +253 -0
- package/dist/cli/kraken/workbench.test.js.map +1 -0
- package/dist/cli/kraken/workbenchView.js +155 -0
- package/dist/cli/kraken/workbenchView.js.map +1 -0
- package/dist/cli/kraken/workbenchView.test.js +130 -0
- package/dist/cli/kraken/workbenchView.test.js.map +1 -0
- package/dist/cli/main.bundled.js +2930 -353
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/runHeadless.js +83 -8
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/slashCommands.js +16 -0
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/krakenFanout.js +200 -0
- package/dist/cli/slashHandlers/krakenFanout.js.map +1 -0
- package/dist/cli/slashHandlers/krakenGraph.js +10 -3
- package/dist/cli/slashHandlers/krakenGraph.js.map +1 -1
- package/dist/cli/slashHandlers/krakenWorkbench.js +49 -0
- package/dist/cli/slashHandlers/krakenWorkbench.js.map +1 -0
- package/dist/cli/tools/krakenCsvFanout.js +260 -0
- package/dist/cli/tools/krakenCsvFanout.js.map +1 -0
- package/dist/cli/tools/krakenCsvFanout.test.js +200 -0
- package/dist/cli/tools/krakenCsvFanout.test.js.map +1 -0
- package/dist/cli/tools/krakenModel.js +32 -0
- package/dist/cli/tools/krakenModel.js.map +1 -1
- package/dist/cli/tools/krakenRadio.js.map +1 -1
- package/dist/cli/tools/taskTool.js +2 -2
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -886,8 +886,8 @@ function loadModelsRegistry(file2 = getModelsFilePath()) {
|
|
|
886
886
|
}
|
|
887
887
|
}
|
|
888
888
|
function getCachedModels(provider, file2 = getModelsFilePath()) {
|
|
889
|
-
const
|
|
890
|
-
return
|
|
889
|
+
const registry4 = loadModelsRegistry(file2);
|
|
890
|
+
return registry4[provider];
|
|
891
891
|
}
|
|
892
892
|
function isModelsCacheStale(provider, maxAgeMs = 6 * 60 * 60 * 1e3, file2 = getModelsFilePath(), now = Date.now()) {
|
|
893
893
|
const entry = getCachedModels(provider, file2);
|
|
@@ -1829,10 +1829,10 @@ function mergeDefs(...defs) {
|
|
|
1829
1829
|
function cloneDef(schema) {
|
|
1830
1830
|
return mergeDefs(schema._zod.def);
|
|
1831
1831
|
}
|
|
1832
|
-
function getElementAtPath(obj,
|
|
1833
|
-
if (!
|
|
1832
|
+
function getElementAtPath(obj, path47) {
|
|
1833
|
+
if (!path47)
|
|
1834
1834
|
return obj;
|
|
1835
|
-
return
|
|
1835
|
+
return path47.reduce((acc, key) => acc?.[key], obj);
|
|
1836
1836
|
}
|
|
1837
1837
|
function promiseAllObject(promisesObj) {
|
|
1838
1838
|
const keys = Object.keys(promisesObj);
|
|
@@ -2160,11 +2160,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
2160
2160
|
}
|
|
2161
2161
|
return false;
|
|
2162
2162
|
}
|
|
2163
|
-
function prefixIssues(
|
|
2163
|
+
function prefixIssues(path47, issues) {
|
|
2164
2164
|
return issues.map((iss) => {
|
|
2165
2165
|
var _a3;
|
|
2166
2166
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
2167
|
-
iss.path.unshift(
|
|
2167
|
+
iss.path.unshift(path47);
|
|
2168
2168
|
return iss;
|
|
2169
2169
|
});
|
|
2170
2170
|
}
|
|
@@ -2382,16 +2382,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2382
2382
|
}
|
|
2383
2383
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2384
2384
|
const fieldErrors = { _errors: [] };
|
|
2385
|
-
const processError = (error52,
|
|
2385
|
+
const processError = (error52, path47 = []) => {
|
|
2386
2386
|
for (const issue2 of error52.issues) {
|
|
2387
2387
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2388
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2388
|
+
issue2.errors.map((issues) => processError({ issues }, [...path47, ...issue2.path]));
|
|
2389
2389
|
} else if (issue2.code === "invalid_key") {
|
|
2390
|
-
processError({ issues: issue2.issues }, [...
|
|
2390
|
+
processError({ issues: issue2.issues }, [...path47, ...issue2.path]);
|
|
2391
2391
|
} else if (issue2.code === "invalid_element") {
|
|
2392
|
-
processError({ issues: issue2.issues }, [...
|
|
2392
|
+
processError({ issues: issue2.issues }, [...path47, ...issue2.path]);
|
|
2393
2393
|
} else {
|
|
2394
|
-
const fullpath = [...
|
|
2394
|
+
const fullpath = [...path47, ...issue2.path];
|
|
2395
2395
|
if (fullpath.length === 0) {
|
|
2396
2396
|
fieldErrors._errors.push(mapper(issue2));
|
|
2397
2397
|
} else {
|
|
@@ -2418,17 +2418,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2418
2418
|
}
|
|
2419
2419
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2420
2420
|
const result = { errors: [] };
|
|
2421
|
-
const processError = (error52,
|
|
2421
|
+
const processError = (error52, path47 = []) => {
|
|
2422
2422
|
var _a3, _b;
|
|
2423
2423
|
for (const issue2 of error52.issues) {
|
|
2424
2424
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2425
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2425
|
+
issue2.errors.map((issues) => processError({ issues }, [...path47, ...issue2.path]));
|
|
2426
2426
|
} else if (issue2.code === "invalid_key") {
|
|
2427
|
-
processError({ issues: issue2.issues }, [...
|
|
2427
|
+
processError({ issues: issue2.issues }, [...path47, ...issue2.path]);
|
|
2428
2428
|
} else if (issue2.code === "invalid_element") {
|
|
2429
|
-
processError({ issues: issue2.issues }, [...
|
|
2429
|
+
processError({ issues: issue2.issues }, [...path47, ...issue2.path]);
|
|
2430
2430
|
} else {
|
|
2431
|
-
const fullpath = [...
|
|
2431
|
+
const fullpath = [...path47, ...issue2.path];
|
|
2432
2432
|
if (fullpath.length === 0) {
|
|
2433
2433
|
result.errors.push(mapper(issue2));
|
|
2434
2434
|
continue;
|
|
@@ -2460,8 +2460,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2460
2460
|
}
|
|
2461
2461
|
function toDotPath(_path) {
|
|
2462
2462
|
const segs = [];
|
|
2463
|
-
const
|
|
2464
|
-
for (const seg of
|
|
2463
|
+
const path47 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2464
|
+
for (const seg of path47) {
|
|
2465
2465
|
if (typeof seg === "number")
|
|
2466
2466
|
segs.push(`[${seg}]`);
|
|
2467
2467
|
else if (typeof seg === "symbol")
|
|
@@ -13342,21 +13342,21 @@ var init_to_json_schema = __esm({
|
|
|
13342
13342
|
// node_modules/zod/v4/core/json-schema-processors.js
|
|
13343
13343
|
function toJSONSchema(input, params) {
|
|
13344
13344
|
if ("_idmap" in input) {
|
|
13345
|
-
const
|
|
13345
|
+
const registry4 = input;
|
|
13346
13346
|
const ctx2 = initializeContext({ ...params, processors: allProcessors });
|
|
13347
13347
|
const defs = {};
|
|
13348
|
-
for (const entry of
|
|
13348
|
+
for (const entry of registry4._idmap.entries()) {
|
|
13349
13349
|
const [_, schema] = entry;
|
|
13350
13350
|
process2(schema, ctx2);
|
|
13351
13351
|
}
|
|
13352
13352
|
const schemas = {};
|
|
13353
13353
|
const external = {
|
|
13354
|
-
registry:
|
|
13354
|
+
registry: registry4,
|
|
13355
13355
|
uri: params?.uri,
|
|
13356
13356
|
defs
|
|
13357
13357
|
};
|
|
13358
13358
|
ctx2.external = external;
|
|
13359
|
-
for (const entry of
|
|
13359
|
+
for (const entry of registry4._idmap.entries()) {
|
|
13360
13360
|
const [key, schema] = entry;
|
|
13361
13361
|
extractDefs(ctx2, schema);
|
|
13362
13362
|
schemas[key] = finalize(ctx2, schema);
|
|
@@ -15964,13 +15964,13 @@ function resolveRef(ref, ctx) {
|
|
|
15964
15964
|
if (!ref.startsWith("#")) {
|
|
15965
15965
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15966
15966
|
}
|
|
15967
|
-
const
|
|
15968
|
-
if (
|
|
15967
|
+
const path47 = ref.slice(1).split("/").filter(Boolean);
|
|
15968
|
+
if (path47.length === 0) {
|
|
15969
15969
|
return ctx.rootSchema;
|
|
15970
15970
|
}
|
|
15971
15971
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15972
|
-
if (
|
|
15973
|
-
const key =
|
|
15972
|
+
if (path47[0] === defsKey) {
|
|
15973
|
+
const key = path47[1];
|
|
15974
15974
|
if (!key || !ctx.defs[key]) {
|
|
15975
15975
|
throw new Error(`Reference not found: ${ref}`);
|
|
15976
15976
|
}
|
|
@@ -18134,11 +18134,11 @@ var init_tools = __esm({
|
|
|
18134
18134
|
if (!ctx.addDocument)
|
|
18135
18135
|
return "Knowledge vault tool not available.";
|
|
18136
18136
|
const title = args["title"] || "New Document";
|
|
18137
|
-
const
|
|
18137
|
+
const path47 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
18138
18138
|
const content = args["content"] || "";
|
|
18139
18139
|
const tags = args["tags"] || [];
|
|
18140
18140
|
ctx.addDocument({
|
|
18141
|
-
path:
|
|
18141
|
+
path: path47,
|
|
18142
18142
|
title,
|
|
18143
18143
|
content,
|
|
18144
18144
|
format: "markdown",
|
|
@@ -18147,7 +18147,7 @@ var init_tools = __esm({
|
|
|
18147
18147
|
workspaceId: ctx.workspaceId
|
|
18148
18148
|
});
|
|
18149
18149
|
ctx.addActivity("vault", "created document", title);
|
|
18150
|
-
return `Document "${title}" created at "${
|
|
18150
|
+
return `Document "${title}" created at "${path47}".`;
|
|
18151
18151
|
}
|
|
18152
18152
|
}
|
|
18153
18153
|
];
|
|
@@ -18922,10 +18922,10 @@ function computeAgentTools(agent, aiConfig) {
|
|
|
18922
18922
|
}
|
|
18923
18923
|
return merged;
|
|
18924
18924
|
}
|
|
18925
|
-
function getToolDescriptions(toolNames,
|
|
18925
|
+
function getToolDescriptions(toolNames, registry4) {
|
|
18926
18926
|
const lines = ["AVAILABLE TOOLS (use ONLY these exact names):"];
|
|
18927
18927
|
for (const name of toolNames) {
|
|
18928
|
-
const tool =
|
|
18928
|
+
const tool = registry4.get(name);
|
|
18929
18929
|
if (!tool)
|
|
18930
18930
|
continue;
|
|
18931
18931
|
let paramList;
|
|
@@ -18952,7 +18952,7 @@ function buildSystemPromptSplit(agent, options) {
|
|
|
18952
18952
|
includeWorkspaceInPrompt = true,
|
|
18953
18953
|
durableStateContext
|
|
18954
18954
|
} = options;
|
|
18955
|
-
const
|
|
18955
|
+
const registry4 = new Map(tools.map((t) => [t.name, t]));
|
|
18956
18956
|
const skills = computeAgentSkills(agent, aiConfig);
|
|
18957
18957
|
const baseModules = getBasePromptModules(mode).filter((m) => !m.conditional || m.conditional(skills));
|
|
18958
18958
|
const customModulesRaw = aiConfig?.customPromptModules ?? [];
|
|
@@ -18987,7 +18987,7 @@ ${projectInstructions.trim()}`);
|
|
|
18987
18987
|
` + skills.map((s) => `## ${s.name}
|
|
18988
18988
|
${s.systemPromptFragment}`).join("\n\n"));
|
|
18989
18989
|
}
|
|
18990
|
-
const toolBlock = getToolDescriptions(toolNames,
|
|
18990
|
+
const toolBlock = getToolDescriptions(toolNames, registry4);
|
|
18991
18991
|
if (toolNames.length > 0) {
|
|
18992
18992
|
stableParts.push(`# Tools
|
|
18993
18993
|
|
|
@@ -19619,6 +19619,48 @@ var init_krakenLive = __esm({
|
|
|
19619
19619
|
});
|
|
19620
19620
|
|
|
19621
19621
|
// packages/core/dist/shared/events.js
|
|
19622
|
+
function isBrainAgentStartEvent(e) {
|
|
19623
|
+
return e.type === "agent_start";
|
|
19624
|
+
}
|
|
19625
|
+
function isBrainAgentEndEvent(e) {
|
|
19626
|
+
return e.type === "agent_end";
|
|
19627
|
+
}
|
|
19628
|
+
function isBrainMessageStartEvent(e) {
|
|
19629
|
+
return e.type === "message_start";
|
|
19630
|
+
}
|
|
19631
|
+
function isBrainMessageDeltaEvent(e) {
|
|
19632
|
+
return e.type === "message_delta";
|
|
19633
|
+
}
|
|
19634
|
+
function isBrainMessageEndEvent(e) {
|
|
19635
|
+
return e.type === "message_end";
|
|
19636
|
+
}
|
|
19637
|
+
function isBrainThinkingDeltaEvent(e) {
|
|
19638
|
+
return e.type === "thinking_delta";
|
|
19639
|
+
}
|
|
19640
|
+
function isBrainToolExecutionStartEvent(e) {
|
|
19641
|
+
return e.type === "tool_execution_start";
|
|
19642
|
+
}
|
|
19643
|
+
function isBrainToolExecutionUpdateEvent(e) {
|
|
19644
|
+
return e.type === "tool_execution_update";
|
|
19645
|
+
}
|
|
19646
|
+
function isBrainToolExecutionEndEvent(e) {
|
|
19647
|
+
return e.type === "tool_execution_end";
|
|
19648
|
+
}
|
|
19649
|
+
function isBrainQueueUpdateEvent(e) {
|
|
19650
|
+
return e.type === "queue_update";
|
|
19651
|
+
}
|
|
19652
|
+
function isBrainSessionCompactedEvent(e) {
|
|
19653
|
+
return e.type === "session_compacted";
|
|
19654
|
+
}
|
|
19655
|
+
function isBrainErrorEvent(e) {
|
|
19656
|
+
return e.type === "error";
|
|
19657
|
+
}
|
|
19658
|
+
function isBrainMemberCostEvent(e) {
|
|
19659
|
+
return e.type === "member_cost";
|
|
19660
|
+
}
|
|
19661
|
+
function isBrainCouncilModeEvent(e) {
|
|
19662
|
+
return e.type === "council_mode";
|
|
19663
|
+
}
|
|
19622
19664
|
function createBrainEvent(type, sessionId, data) {
|
|
19623
19665
|
return {
|
|
19624
19666
|
type,
|
|
@@ -19635,9 +19677,90 @@ var init_events = __esm({
|
|
|
19635
19677
|
});
|
|
19636
19678
|
|
|
19637
19679
|
// packages/core/dist/shared/eventBus.js
|
|
19680
|
+
var EventBus;
|
|
19638
19681
|
var init_eventBus = __esm({
|
|
19639
19682
|
"packages/core/dist/shared/eventBus.js"() {
|
|
19640
19683
|
"use strict";
|
|
19684
|
+
EventBus = class {
|
|
19685
|
+
/** Per-type subscriber registry. */
|
|
19686
|
+
subscribers;
|
|
19687
|
+
/** Wildcard subscribers (receive every event). */
|
|
19688
|
+
wildcardSubscribers;
|
|
19689
|
+
constructor() {
|
|
19690
|
+
this.subscribers = /* @__PURE__ */ new Map();
|
|
19691
|
+
this.wildcardSubscribers = /* @__PURE__ */ new Set();
|
|
19692
|
+
}
|
|
19693
|
+
/** Subscribe to a specific event type. Returns an unsubscribe function. */
|
|
19694
|
+
subscribe(type, handler) {
|
|
19695
|
+
let set2 = this.subscribers.get(type);
|
|
19696
|
+
if (!set2) {
|
|
19697
|
+
set2 = /* @__PURE__ */ new Set();
|
|
19698
|
+
this.subscribers.set(type, set2);
|
|
19699
|
+
}
|
|
19700
|
+
const stored = handler;
|
|
19701
|
+
set2.add(stored);
|
|
19702
|
+
return () => {
|
|
19703
|
+
const current = this.subscribers.get(type);
|
|
19704
|
+
if (!current)
|
|
19705
|
+
return;
|
|
19706
|
+
current.delete(stored);
|
|
19707
|
+
if (current.size === 0)
|
|
19708
|
+
this.subscribers.delete(type);
|
|
19709
|
+
};
|
|
19710
|
+
}
|
|
19711
|
+
/** Subscribe to all events. Returns an unsubscribe function. */
|
|
19712
|
+
subscribeAll(handler) {
|
|
19713
|
+
this.wildcardSubscribers.add(handler);
|
|
19714
|
+
return () => {
|
|
19715
|
+
this.wildcardSubscribers.delete(handler);
|
|
19716
|
+
};
|
|
19717
|
+
}
|
|
19718
|
+
/**
|
|
19719
|
+
* Emit an event to all matching subscribers (typed first, then wildcard).
|
|
19720
|
+
* Errors in individual subscribers are logged but do not prevent other
|
|
19721
|
+
* subscribers from receiving the event.
|
|
19722
|
+
*/
|
|
19723
|
+
emit(event) {
|
|
19724
|
+
const set2 = this.subscribers.get(event.type);
|
|
19725
|
+
if (set2) {
|
|
19726
|
+
for (const handler of set2)
|
|
19727
|
+
this.dispatch(handler, event);
|
|
19728
|
+
}
|
|
19729
|
+
for (const handler of this.wildcardSubscribers)
|
|
19730
|
+
this.dispatch(handler, event);
|
|
19731
|
+
}
|
|
19732
|
+
/** Remove all subscribers (typed + wildcard). */
|
|
19733
|
+
clear() {
|
|
19734
|
+
this.subscribers.clear();
|
|
19735
|
+
this.wildcardSubscribers.clear();
|
|
19736
|
+
}
|
|
19737
|
+
/**
|
|
19738
|
+
* Number of subscribers. With a `type`, returns typed subscribers for that
|
|
19739
|
+
* type; without, returns the total across all types plus wildcard handlers.
|
|
19740
|
+
*/
|
|
19741
|
+
listenerCount(type) {
|
|
19742
|
+
if (type !== void 0) {
|
|
19743
|
+
return this.subscribers.get(type)?.size ?? 0;
|
|
19744
|
+
}
|
|
19745
|
+
let total = this.wildcardSubscribers.size;
|
|
19746
|
+
for (const set2 of this.subscribers.values())
|
|
19747
|
+
total += set2.size;
|
|
19748
|
+
return total;
|
|
19749
|
+
}
|
|
19750
|
+
/** Invoke a single subscriber, isolating sync throws and async rejections. */
|
|
19751
|
+
dispatch(handler, event) {
|
|
19752
|
+
try {
|
|
19753
|
+
const result = handler(event);
|
|
19754
|
+
if (result instanceof Promise) {
|
|
19755
|
+
result.catch((err) => {
|
|
19756
|
+
console.error("[eventBus] subscriber error:", err);
|
|
19757
|
+
});
|
|
19758
|
+
}
|
|
19759
|
+
} catch (err) {
|
|
19760
|
+
console.error("[eventBus] subscriber error:", err);
|
|
19761
|
+
}
|
|
19762
|
+
}
|
|
19763
|
+
};
|
|
19641
19764
|
}
|
|
19642
19765
|
});
|
|
19643
19766
|
|
|
@@ -19658,6 +19781,14 @@ var init_context = __esm({
|
|
|
19658
19781
|
});
|
|
19659
19782
|
|
|
19660
19783
|
// packages/core/dist/types/systemTypes.js
|
|
19784
|
+
function createDefaultSystemPromptConfig() {
|
|
19785
|
+
return {
|
|
19786
|
+
enabledSkills: [],
|
|
19787
|
+
enabledTools: [],
|
|
19788
|
+
customPromptModules: [],
|
|
19789
|
+
agentSkillConfigs: []
|
|
19790
|
+
};
|
|
19791
|
+
}
|
|
19661
19792
|
var init_systemTypes = __esm({
|
|
19662
19793
|
"packages/core/dist/types/systemTypes.js"() {
|
|
19663
19794
|
"use strict";
|
|
@@ -21839,11 +21970,11 @@ var init_synthesisAudit = __esm({
|
|
|
21839
21970
|
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
21840
21971
|
import { join as join2 } from "node:path";
|
|
21841
21972
|
function loadNfrSpec(zelariRoot) {
|
|
21842
|
-
const
|
|
21843
|
-
if (!existsSync6(
|
|
21973
|
+
const path47 = join2(zelariRoot, "nfr-spec.json");
|
|
21974
|
+
if (!existsSync6(path47))
|
|
21844
21975
|
return null;
|
|
21845
21976
|
try {
|
|
21846
|
-
const raw = JSON.parse(readFileSync5(
|
|
21977
|
+
const raw = JSON.parse(readFileSync5(path47, "utf8"));
|
|
21847
21978
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
21848
21979
|
return null;
|
|
21849
21980
|
return raw;
|
|
@@ -24149,9 +24280,9 @@ var init_types3 = __esm({
|
|
|
24149
24280
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
24150
24281
|
import { join as join8 } from "node:path";
|
|
24151
24282
|
function readLessonsDeduped(zelariRoot) {
|
|
24152
|
-
const
|
|
24283
|
+
const path47 = join8(zelariRoot, LESSONS_FILE);
|
|
24153
24284
|
try {
|
|
24154
|
-
const raw = readFileSync10(
|
|
24285
|
+
const raw = readFileSync10(path47, "utf8");
|
|
24155
24286
|
const byId = /* @__PURE__ */ new Map();
|
|
24156
24287
|
for (const line of raw.split(/\r?\n/)) {
|
|
24157
24288
|
if (!line.trim())
|
|
@@ -24252,8 +24383,8 @@ function keywordsFrom(check2, signature) {
|
|
|
24252
24383
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
24253
24384
|
}
|
|
24254
24385
|
function writeLesson(zelariRoot, lesson) {
|
|
24255
|
-
const
|
|
24256
|
-
appendFileSync(
|
|
24386
|
+
const path47 = join9(zelariRoot, LESSONS_FILE);
|
|
24387
|
+
appendFileSync(path47, `${JSON.stringify(lesson)}
|
|
24257
24388
|
`, "utf8");
|
|
24258
24389
|
}
|
|
24259
24390
|
function findSimilar(lessons, signature) {
|
|
@@ -24866,9 +24997,9 @@ function findCycle(nodes) {
|
|
|
24866
24997
|
if (color.get(start) !== WHITE)
|
|
24867
24998
|
continue;
|
|
24868
24999
|
const stack = [[start, 0]];
|
|
24869
|
-
const
|
|
25000
|
+
const path47 = [];
|
|
24870
25001
|
color.set(start, GRAY);
|
|
24871
|
-
|
|
25002
|
+
path47.push(start);
|
|
24872
25003
|
while (stack.length > 0) {
|
|
24873
25004
|
const top = stack[stack.length - 1];
|
|
24874
25005
|
const [id, idx] = top;
|
|
@@ -24881,17 +25012,17 @@ function findCycle(nodes) {
|
|
|
24881
25012
|
continue;
|
|
24882
25013
|
const c = color.get(dep);
|
|
24883
25014
|
if (c === GRAY) {
|
|
24884
|
-
const at =
|
|
24885
|
-
return [...
|
|
25015
|
+
const at = path47.indexOf(dep);
|
|
25016
|
+
return [...path47.slice(at), dep];
|
|
24886
25017
|
}
|
|
24887
25018
|
if (c === WHITE) {
|
|
24888
25019
|
color.set(dep, GRAY);
|
|
24889
|
-
|
|
25020
|
+
path47.push(dep);
|
|
24890
25021
|
stack.push([dep, 0]);
|
|
24891
25022
|
}
|
|
24892
25023
|
} else {
|
|
24893
25024
|
color.set(id, BLACK);
|
|
24894
|
-
|
|
25025
|
+
path47.pop();
|
|
24895
25026
|
stack.pop();
|
|
24896
25027
|
}
|
|
24897
25028
|
}
|
|
@@ -24978,10 +25109,15 @@ function countByStatus(graph) {
|
|
|
24978
25109
|
counts[node.status] += 1;
|
|
24979
25110
|
return counts;
|
|
24980
25111
|
}
|
|
24981
|
-
var DEFAULT_MAX_NODES;
|
|
25112
|
+
var TERMINAL_STATUSES, DEFAULT_MAX_NODES;
|
|
24982
25113
|
var init_graph = __esm({
|
|
24983
25114
|
"packages/core/dist/kraken/graph.js"() {
|
|
24984
25115
|
"use strict";
|
|
25116
|
+
TERMINAL_STATUSES = [
|
|
25117
|
+
"done",
|
|
25118
|
+
"error",
|
|
25119
|
+
"skipped"
|
|
25120
|
+
];
|
|
24985
25121
|
DEFAULT_MAX_NODES = 24;
|
|
24986
25122
|
}
|
|
24987
25123
|
});
|
|
@@ -25091,16 +25227,1034 @@ var init_conflict = __esm({
|
|
|
25091
25227
|
}
|
|
25092
25228
|
});
|
|
25093
25229
|
|
|
25230
|
+
// packages/core/dist/kraken/weakness.js
|
|
25231
|
+
function weaknessFromVerdict(text) {
|
|
25232
|
+
if (typeof text !== "string")
|
|
25233
|
+
return 0;
|
|
25234
|
+
const trimmed = text.trim();
|
|
25235
|
+
if (trimmed === "")
|
|
25236
|
+
return 0;
|
|
25237
|
+
let score = 0;
|
|
25238
|
+
for (const re of SPECIFICITY_MARKERS) {
|
|
25239
|
+
if (re.test(trimmed))
|
|
25240
|
+
score += SPECIFICITY_MARKER_WEIGHT;
|
|
25241
|
+
}
|
|
25242
|
+
const clauses = trimmed.split(/[.!?;]+|\n+/).map((c) => c.trim()).filter((c) => c.length > 0 && /\b\w+ing\b|\b\w+ed\b|\bwill\b|\bcan\b|\bmust\b|\bshould\b|\bmay\b/i.test(c));
|
|
25243
|
+
const clausePenalty = Math.min(clauses.length, SPECIFICITY_CLAUSE_MAX) * SPECIFICITY_CLAUSE_WEIGHT;
|
|
25244
|
+
score += clausePenalty;
|
|
25245
|
+
return clamp01(score);
|
|
25246
|
+
}
|
|
25247
|
+
function weaknessScoreFromText(text) {
|
|
25248
|
+
return 1 - weaknessFromVerdict(text);
|
|
25249
|
+
}
|
|
25250
|
+
function weaknessFromMeter(meter) {
|
|
25251
|
+
return clamp01(1 - clamp01(meter.specificity));
|
|
25252
|
+
}
|
|
25253
|
+
function specificityFromAssumptions(assumptions) {
|
|
25254
|
+
if (!Array.isArray(assumptions) || assumptions.length === 0)
|
|
25255
|
+
return 0;
|
|
25256
|
+
const raw = Math.min(assumptions.length, 6) / 6;
|
|
25257
|
+
return clamp01(raw);
|
|
25258
|
+
}
|
|
25259
|
+
function rankByWeakness(candidates) {
|
|
25260
|
+
if (candidates.length === 0)
|
|
25261
|
+
return [];
|
|
25262
|
+
const raws = candidates.map((c) => computeRaw(c));
|
|
25263
|
+
const extScores = raws.filter((r) => r.source === "extensionSize").map((r) => r.raw);
|
|
25264
|
+
const extMax = extScores.length > 0 ? Math.max(...extScores) : 1;
|
|
25265
|
+
const extMin = extScores.length > 0 ? Math.min(...extScores) : 0;
|
|
25266
|
+
const extRange = extMax - extMin;
|
|
25267
|
+
const scored = raws.map((r) => {
|
|
25268
|
+
let score;
|
|
25269
|
+
switch (r.source) {
|
|
25270
|
+
case "extensionSize":
|
|
25271
|
+
score = extRange > 0 ? (r.raw - extMin) / extRange : 1;
|
|
25272
|
+
break;
|
|
25273
|
+
case "meter":
|
|
25274
|
+
score = clamp01(1 - clamp01(r.raw));
|
|
25275
|
+
break;
|
|
25276
|
+
case "heuristic":
|
|
25277
|
+
score = clamp01(r.raw);
|
|
25278
|
+
break;
|
|
25279
|
+
}
|
|
25280
|
+
return { candidate: r.candidate, score, source: r.source };
|
|
25281
|
+
});
|
|
25282
|
+
const indexed = scored.map((s, i) => ({ ...s, originalIndex: i }));
|
|
25283
|
+
indexed.sort((a, b) => {
|
|
25284
|
+
if (b.score !== a.score)
|
|
25285
|
+
return b.score - a.score;
|
|
25286
|
+
return a.originalIndex - b.originalIndex;
|
|
25287
|
+
});
|
|
25288
|
+
let lastScore;
|
|
25289
|
+
let lastRank = 0;
|
|
25290
|
+
let seen = 0;
|
|
25291
|
+
return indexed.map((s) => {
|
|
25292
|
+
seen += 1;
|
|
25293
|
+
if (lastScore === void 0 || s.score !== lastScore) {
|
|
25294
|
+
lastRank = seen;
|
|
25295
|
+
lastScore = s.score;
|
|
25296
|
+
}
|
|
25297
|
+
return {
|
|
25298
|
+
candidate: s.candidate,
|
|
25299
|
+
weaknessScore: s.score,
|
|
25300
|
+
rank: lastRank,
|
|
25301
|
+
source: s.source
|
|
25302
|
+
};
|
|
25303
|
+
});
|
|
25304
|
+
}
|
|
25305
|
+
function computeRaw(c) {
|
|
25306
|
+
if (typeof c.extensionSize === "number" && Number.isFinite(c.extensionSize) && c.extensionSize >= 0) {
|
|
25307
|
+
return { candidate: c, raw: c.extensionSize, source: "extensionSize" };
|
|
25308
|
+
}
|
|
25309
|
+
if (c.meter && typeof c.meter.specificity === "number" && Number.isFinite(c.meter.specificity)) {
|
|
25310
|
+
return { candidate: c, raw: clamp01(c.meter.specificity), source: "meter" };
|
|
25311
|
+
}
|
|
25312
|
+
return { candidate: c, raw: weaknessScoreFromText(c.text), source: "heuristic" };
|
|
25313
|
+
}
|
|
25314
|
+
function clamp01(n) {
|
|
25315
|
+
if (!Number.isFinite(n))
|
|
25316
|
+
return 0;
|
|
25317
|
+
if (n < 0)
|
|
25318
|
+
return 0;
|
|
25319
|
+
if (n > 1)
|
|
25320
|
+
return 1;
|
|
25321
|
+
return n;
|
|
25322
|
+
}
|
|
25323
|
+
function pickWeakest(candidates) {
|
|
25324
|
+
const ranked = rankByWeakness(candidates);
|
|
25325
|
+
return ranked.length > 0 ? ranked[0].candidate : void 0;
|
|
25326
|
+
}
|
|
25327
|
+
function filterByWeakness(candidates, threshold) {
|
|
25328
|
+
const ranked = rankByWeakness(candidates);
|
|
25329
|
+
return ranked.filter((r) => r.weaknessScore >= threshold).map((r) => r.candidate);
|
|
25330
|
+
}
|
|
25331
|
+
var BENNETTS_RAZOR, BENNETTS_RAZOR_SHORT, SPECIFICITY_MARKERS, SPECIFICITY_MARKER_WEIGHT, SPECIFICITY_CLAUSE_WEIGHT, SPECIFICITY_CLAUSE_MAX, WEAKNESS_METER_PROMPT, WeaknessMeterResponseSchema;
|
|
25332
|
+
var init_weakness = __esm({
|
|
25333
|
+
"packages/core/dist/kraken/weakness.js"() {
|
|
25334
|
+
"use strict";
|
|
25335
|
+
init_zod();
|
|
25336
|
+
BENNETTS_RAZOR = [
|
|
25337
|
+
"Bennett's Razor (arXiv:2301.12987): explanations should be no more specific than necessary.",
|
|
25338
|
+
"When two solutions both satisfy the task, prefer the one that assumes the least.",
|
|
25339
|
+
'A claim is "more specific" when it pins down an exact value, path, version, or invariant that a more general plan would not have to commit to.',
|
|
25340
|
+
"Specificity is a tie-breaker, not a goal: a plan that is too vague to act on is still useless.",
|
|
25341
|
+
"Weakness ranking only applies among solutions that already meet the bar; do not weaken a passing plan below the bar in the name of weakness."
|
|
25342
|
+
].join(" ");
|
|
25343
|
+
BENNETTS_RAZOR_SHORT = "Prefer the solution that is no more specific than necessary.";
|
|
25344
|
+
SPECIFICITY_MARKERS = [
|
|
25345
|
+
/\bguarantee[ds]?\b/i,
|
|
25346
|
+
/\bexact(?:ly)?\b/i,
|
|
25347
|
+
/\bmust\b/i,
|
|
25348
|
+
/\bshall\b/i,
|
|
25349
|
+
/\balways\b/i,
|
|
25350
|
+
/\bnever\b/i,
|
|
25351
|
+
/\brequire[ds]?\b/i,
|
|
25352
|
+
/\bmandatory\b/i,
|
|
25353
|
+
/\bline\s+\d+/i,
|
|
25354
|
+
// "line 42"
|
|
25355
|
+
/\bversion\s+[\d.]+/i,
|
|
25356
|
+
// "version 1.2.3"
|
|
25357
|
+
/\bv?\d+\.\d+\.\d+\b/,
|
|
25358
|
+
// bare semver
|
|
25359
|
+
/\b[0-9a-f]{7,40}\b/i,
|
|
25360
|
+
// git short SHA / commit hash
|
|
25361
|
+
/\bthe\s+(?:file|path)\s+(?:is|at)\b/i,
|
|
25362
|
+
/\bprecise(?:ly)?\b/i,
|
|
25363
|
+
/\bassert(?:s|ed|ion)?\b/i,
|
|
25364
|
+
/\bconfirm(?:s|ed)?\b/i,
|
|
25365
|
+
/\bwill\s+(?:definitely|certainly|always)\b/i
|
|
25366
|
+
];
|
|
25367
|
+
SPECIFICITY_MARKER_WEIGHT = 0.25;
|
|
25368
|
+
SPECIFICITY_CLAUSE_WEIGHT = 0.05;
|
|
25369
|
+
SPECIFICITY_CLAUSE_MAX = 6;
|
|
25370
|
+
WEAKNESS_METER_PROMPT = `You are measuring the SPECIFICITY of a candidate solution to a software task.
|
|
25371
|
+
|
|
25372
|
+
Specificity means: how many specific commitments does this solution make that a more general plan would not have to make? Examples of specific commitments: exact file paths, exact line numbers, exact semver versions, exact function signatures, guarantees about runtime behaviour, assertions about what other agents/users will do.
|
|
25373
|
+
|
|
25374
|
+
A maximally general solution is one that asserts nothing beyond the task itself ("just do the task"). A maximally specific solution is one that pins every possible value, path, and invariant.
|
|
25375
|
+
|
|
25376
|
+
Output ONLY a JSON object of the form:
|
|
25377
|
+
{"specificity": <float in [0,1]>, "assumptions": [<short string>, ...]}
|
|
25378
|
+
|
|
25379
|
+
where
|
|
25380
|
+
- specificity = 0.0 \u2192 solution asserts nothing beyond the task
|
|
25381
|
+
- specificity = 1.0 \u2192 solution pins every value, path, version, and invariant
|
|
25382
|
+
- assumptions = the list of specific commitments you identified, each \u2264 12 words, deduped, sorted by strength (most specific first). Cap the list at 12.
|
|
25383
|
+
|
|
25384
|
+
Do not add prose, do not add a code fence, do not explain your reasoning. JSON only.`;
|
|
25385
|
+
WeaknessMeterResponseSchema = external_exports.object({
|
|
25386
|
+
specificity: external_exports.number().min(0).max(1),
|
|
25387
|
+
assumptions: external_exports.array(external_exports.string().min(1).max(200)).max(12)
|
|
25388
|
+
});
|
|
25389
|
+
}
|
|
25390
|
+
});
|
|
25391
|
+
|
|
25392
|
+
// packages/core/dist/kraken/verdict.js
|
|
25393
|
+
function parseVerifyVerdict(text) {
|
|
25394
|
+
const source = typeof text === "string" ? text : "";
|
|
25395
|
+
if (source.trim() === "")
|
|
25396
|
+
return { verdict: "unknown", findings: "" };
|
|
25397
|
+
VERDICT_LINE.lastIndex = 0;
|
|
25398
|
+
let match;
|
|
25399
|
+
let last = null;
|
|
25400
|
+
while ((match = VERDICT_LINE.exec(source)) !== null) {
|
|
25401
|
+
last = match;
|
|
25402
|
+
if (match.index === VERDICT_LINE.lastIndex)
|
|
25403
|
+
VERDICT_LINE.lastIndex += 1;
|
|
25404
|
+
}
|
|
25405
|
+
if (!last) {
|
|
25406
|
+
return { verdict: "unknown", findings: capFindings(source) };
|
|
25407
|
+
}
|
|
25408
|
+
const verdict = last[1].toUpperCase() === "FAIL" ? "fail" : "pass";
|
|
25409
|
+
const findings = capFindings(source.slice(0, last.index));
|
|
25410
|
+
return { verdict, findings };
|
|
25411
|
+
}
|
|
25412
|
+
function extractRequirementsBlock(text) {
|
|
25413
|
+
if (typeof text !== "string" || text.trim() === "")
|
|
25414
|
+
return [];
|
|
25415
|
+
const re = /```json\s*([\s\S]*?)```/gi;
|
|
25416
|
+
let m;
|
|
25417
|
+
let last = null;
|
|
25418
|
+
while ((m = re.exec(text)) !== null) {
|
|
25419
|
+
last = m[1];
|
|
25420
|
+
if (m.index === re.lastIndex)
|
|
25421
|
+
re.lastIndex += 1;
|
|
25422
|
+
}
|
|
25423
|
+
if (!last)
|
|
25424
|
+
return [];
|
|
25425
|
+
try {
|
|
25426
|
+
const obj = JSON.parse(last);
|
|
25427
|
+
if (!obj || !Array.isArray(obj.requirements))
|
|
25428
|
+
return [];
|
|
25429
|
+
const out = [];
|
|
25430
|
+
for (const r of obj.requirements) {
|
|
25431
|
+
if (!r || typeof r !== "object")
|
|
25432
|
+
continue;
|
|
25433
|
+
const row = r;
|
|
25434
|
+
const req = typeof row.requirement === "string" ? row.requirement : "";
|
|
25435
|
+
const metRaw = typeof row.met === "string" ? row.met.toLowerCase() : "";
|
|
25436
|
+
const met = metRaw === "pass" || metRaw === "true" ? "pass" : metRaw === "fail" || metRaw === "false" ? "fail" : "unknown";
|
|
25437
|
+
const evidence = typeof row.evidence === "string" ? row.evidence : void 0;
|
|
25438
|
+
if (req)
|
|
25439
|
+
out.push({ requirement: req, met, ...evidence ? { evidence } : {} });
|
|
25440
|
+
}
|
|
25441
|
+
return out;
|
|
25442
|
+
} catch {
|
|
25443
|
+
return [];
|
|
25444
|
+
}
|
|
25445
|
+
}
|
|
25446
|
+
function parsePersonaVerdict(text) {
|
|
25447
|
+
const base = parseVerifyVerdict(text);
|
|
25448
|
+
return {
|
|
25449
|
+
verdict: base.verdict,
|
|
25450
|
+
findings: base.findings,
|
|
25451
|
+
requirements: extractRequirementsBlock(text),
|
|
25452
|
+
// Bennett's weakness = "how little the reviewer's free text asserts"
|
|
25453
|
+
// (arXiv:2301.12987). `weaknessScoreFromText` is the weakness form
|
|
25454
|
+
// (1.0 = maximally general / no claims; 0.0 = maximally specific).
|
|
25455
|
+
// The verdict gate is the trailer; weakness is metadata surfaced in
|
|
25456
|
+
// the workbench so a user can see whether a PASS was earned by a
|
|
25457
|
+
// tightly-asserted or loosely-claimed reviewer.
|
|
25458
|
+
weaknessScore: weaknessScoreFromText(text)
|
|
25459
|
+
};
|
|
25460
|
+
}
|
|
25461
|
+
function capFindings(raw) {
|
|
25462
|
+
const trimmed = raw.trim();
|
|
25463
|
+
if (trimmed.length <= MAX_FINDINGS_CHARS)
|
|
25464
|
+
return trimmed;
|
|
25465
|
+
return `${trimmed.slice(0, MAX_FINDINGS_CHARS)}
|
|
25466
|
+
\u2026 [truncated]`;
|
|
25467
|
+
}
|
|
25468
|
+
var MAX_FINDINGS_CHARS, VERDICT_LINE;
|
|
25469
|
+
var init_verdict = __esm({
|
|
25470
|
+
"packages/core/dist/kraken/verdict.js"() {
|
|
25471
|
+
"use strict";
|
|
25472
|
+
init_weakness();
|
|
25473
|
+
MAX_FINDINGS_CHARS = 2800;
|
|
25474
|
+
VERDICT_LINE = /^[\s>*_-]*VERDICT[\s*_]*:[\s*_]*(PASS|FAIL)\b/gim;
|
|
25475
|
+
}
|
|
25476
|
+
});
|
|
25477
|
+
|
|
25478
|
+
// packages/core/dist/kraken/personas/registry.js
|
|
25479
|
+
function registerPersona(p3) {
|
|
25480
|
+
registry3.set(p3.kind, p3);
|
|
25481
|
+
}
|
|
25482
|
+
function getPersona(kind) {
|
|
25483
|
+
return registry3.get(kind);
|
|
25484
|
+
}
|
|
25485
|
+
function listPersonas() {
|
|
25486
|
+
return [...registry3.values()];
|
|
25487
|
+
}
|
|
25488
|
+
function isReviewerKind(kind) {
|
|
25489
|
+
return kind === "verify" || kind === "spec" || kind === "conformance";
|
|
25490
|
+
}
|
|
25491
|
+
function defaultPersonaParse(text) {
|
|
25492
|
+
return parsePersonaVerdict(text);
|
|
25493
|
+
}
|
|
25494
|
+
var registry3;
|
|
25495
|
+
var init_registry = __esm({
|
|
25496
|
+
"packages/core/dist/kraken/personas/registry.js"() {
|
|
25497
|
+
"use strict";
|
|
25498
|
+
init_verdict();
|
|
25499
|
+
registry3 = /* @__PURE__ */ new Map();
|
|
25500
|
+
}
|
|
25501
|
+
});
|
|
25502
|
+
|
|
25503
|
+
// packages/core/dist/kraken/personas/specReviewer.js
|
|
25504
|
+
var SYSTEM_PROMPT, specPersona;
|
|
25505
|
+
var init_specReviewer = __esm({
|
|
25506
|
+
"packages/core/dist/kraken/personas/specReviewer.js"() {
|
|
25507
|
+
"use strict";
|
|
25508
|
+
init_registry();
|
|
25509
|
+
SYSTEM_PROMPT = [
|
|
25510
|
+
"You are the spec-reviewer for Kraken. You compare a writer's delivered",
|
|
25511
|
+
"work against a written spec or plan and judge each requirement.",
|
|
25512
|
+
"",
|
|
25513
|
+
'Your bias is CONSERVATIVE. When the spec said "must do X" and the writer',
|
|
25514
|
+
"did a reasonable but slightly different thing, that is a FAIL. Specs",
|
|
25515
|
+
"are meant to catch exactly this kind of drift.",
|
|
25516
|
+
"",
|
|
25517
|
+
"## Output format",
|
|
25518
|
+
"",
|
|
25519
|
+
"You MUST end with a verdict trailer on its own line:",
|
|
25520
|
+
"",
|
|
25521
|
+
" VERDICT: PASS",
|
|
25522
|
+
"or",
|
|
25523
|
+
" VERDICT: FAIL",
|
|
25524
|
+
"",
|
|
25525
|
+
"When the verdict is FAIL, you MUST also emit a per-requirement table in",
|
|
25526
|
+
"a JSON code block, BEFORE the trailer:",
|
|
25527
|
+
"",
|
|
25528
|
+
"```json",
|
|
25529
|
+
"{",
|
|
25530
|
+
' "requirements": [',
|
|
25531
|
+
' { "requirement": "<verbatim from the spec>", "met": "pass|fail|unknown", "evidence": "<path, line, command output>" },',
|
|
25532
|
+
" ...",
|
|
25533
|
+
" ]",
|
|
25534
|
+
"}",
|
|
25535
|
+
"```",
|
|
25536
|
+
"",
|
|
25537
|
+
"Rules:",
|
|
25538
|
+
" - Cite concrete evidence (file path, function name, command output).",
|
|
25539
|
+
' "Looks fine" is not evidence.',
|
|
25540
|
+
" - When a requirement is partially met, mark it `fail` with evidence",
|
|
25541
|
+
" describing the gap. The downstream `fix` tentacle will read this.",
|
|
25542
|
+
" - When a requirement is genuinely impossible to assess without running",
|
|
25543
|
+
" code, mark it `unknown` and explain why in evidence.",
|
|
25544
|
+
" - The verdict trailer is the GATE: the executor will not read the",
|
|
25545
|
+
" table if the trailer says PASS. The table is for diagnostics.",
|
|
25546
|
+
" - Keep the table terse. One line per row.",
|
|
25547
|
+
"",
|
|
25548
|
+
"## What you do NOT do",
|
|
25549
|
+
"",
|
|
25550
|
+
" - You do not modify any files. You are a judge, not a fixer.",
|
|
25551
|
+
" - You do not invent requirements that are not in the spec.",
|
|
25552
|
+
" - You do not say PASS when the spec listed requirements you did not",
|
|
25553
|
+
" actually assess. Either assess them or mark them `unknown`."
|
|
25554
|
+
].join("\n");
|
|
25555
|
+
specPersona = {
|
|
25556
|
+
kind: "spec",
|
|
25557
|
+
label: "spec-reviewer",
|
|
25558
|
+
description: "Compares a writer's output against a written spec, per-requirement.",
|
|
25559
|
+
systemPrompt: SYSTEM_PROMPT
|
|
25560
|
+
// Default parser handles both the trailer and the requirements block.
|
|
25561
|
+
};
|
|
25562
|
+
registerPersona(specPersona);
|
|
25563
|
+
}
|
|
25564
|
+
});
|
|
25565
|
+
|
|
25566
|
+
// packages/core/dist/kraken/personas/conformance.js
|
|
25567
|
+
var SYSTEM_PROMPT2, conformancePersona;
|
|
25568
|
+
var init_conformance = __esm({
|
|
25569
|
+
"packages/core/dist/kraken/personas/conformance.js"() {
|
|
25570
|
+
"use strict";
|
|
25571
|
+
init_registry();
|
|
25572
|
+
SYSTEM_PROMPT2 = [
|
|
25573
|
+
"You are the conformance-reviewer for Kraken. You compare a writer's",
|
|
25574
|
+
"delivered work against the USER'S ORIGINAL VERBATIM PROMPT \u2014 not a",
|
|
25575
|
+
"spec, not a plan, not what the writer decided to do.",
|
|
25576
|
+
"",
|
|
25577
|
+
'Your bias is LITERAL. When the user said "use JWT", "use session',
|
|
25578
|
+
'cookies" is a FAIL even if it works. When the user said "ship a CLI",',
|
|
25579
|
+
'"ship a library" is a FAIL even if the library is excellent.',
|
|
25580
|
+
"",
|
|
25581
|
+
"## Decomposition",
|
|
25582
|
+
"",
|
|
25583
|
+
"Before judging, decompose the prompt into the discrete asks the user",
|
|
25584
|
+
"made. A good decomposition:",
|
|
25585
|
+
" - Each user-stated requirement gets one row.",
|
|
25586
|
+
' - Each user-stated preference ("preferably X", "ideally Y") gets one row.',
|
|
25587
|
+
' - Each user-stated constraint ("no dependencies", "stay under 100 LOC") gets one row.',
|
|
25588
|
+
" - If the prompt is ambiguous, decompose into the LITERAL reading AND",
|
|
25589
|
+
" note in the evidence that a more permissive reading would have passed.",
|
|
25590
|
+
"",
|
|
25591
|
+
"## Output format",
|
|
25592
|
+
"",
|
|
25593
|
+
"You MUST end with a verdict trailer on its own line:",
|
|
25594
|
+
"",
|
|
25595
|
+
" VERDICT: PASS",
|
|
25596
|
+
"or",
|
|
25597
|
+
" VERDICT: FAIL",
|
|
25598
|
+
"",
|
|
25599
|
+
"When the verdict is FAIL, you MUST also emit a per-requirement table in",
|
|
25600
|
+
"a JSON code block, BEFORE the trailer:",
|
|
25601
|
+
"",
|
|
25602
|
+
"```json",
|
|
25603
|
+
"{",
|
|
25604
|
+
' "requirements": [',
|
|
25605
|
+
' { "requirement": "<verbatim from the user prompt>", "met": "pass|fail|unknown", "evidence": "<path, line, output>" },',
|
|
25606
|
+
" ...",
|
|
25607
|
+
" ]",
|
|
25608
|
+
"}",
|
|
25609
|
+
"```",
|
|
25610
|
+
"",
|
|
25611
|
+
"Rules:",
|
|
25612
|
+
" - Cite concrete evidence (file path, line, function name, command",
|
|
25613
|
+
' output) for every row. "Looks right" is not evidence.',
|
|
25614
|
+
" - When a literal reading would have failed but a more permissive",
|
|
25615
|
+
" reading would have passed, mark it `fail` with evidence explaining",
|
|
25616
|
+
" the gap. The user is the authority on what they meant.",
|
|
25617
|
+
' - When the prompt is genuinely too vague to decompose (e.g. "make it',
|
|
25618
|
+
' better"), say so explicitly in your findings and mark all rows',
|
|
25619
|
+
" `unknown`. The downstream fix node will then ask the user.",
|
|
25620
|
+
" - Keep the table terse. One line per row.",
|
|
25621
|
+
"",
|
|
25622
|
+
"## What you do NOT do",
|
|
25623
|
+
"",
|
|
25624
|
+
" - You do not consult any spec, plan, or intermediate artifact. Only the",
|
|
25625
|
+
" original user prompt.",
|
|
25626
|
+
" - You do not modify any files. You are a judge, not a fixer.",
|
|
25627
|
+
" - You do not say PASS to be polite. The user has only one chance to",
|
|
25628
|
+
" see your verdict before shipping; be honest."
|
|
25629
|
+
].join("\n");
|
|
25630
|
+
conformancePersona = {
|
|
25631
|
+
kind: "conformance",
|
|
25632
|
+
label: "conformance-reviewer",
|
|
25633
|
+
description: "Compares a writer's output against the user's original verbatim prompt.",
|
|
25634
|
+
systemPrompt: SYSTEM_PROMPT2
|
|
25635
|
+
};
|
|
25636
|
+
registerPersona(conformancePersona);
|
|
25637
|
+
}
|
|
25638
|
+
});
|
|
25639
|
+
|
|
25640
|
+
// packages/core/dist/kraken/personas/index.js
|
|
25641
|
+
var init_personas = __esm({
|
|
25642
|
+
"packages/core/dist/kraken/personas/index.js"() {
|
|
25643
|
+
"use strict";
|
|
25644
|
+
init_specReviewer();
|
|
25645
|
+
init_conformance();
|
|
25646
|
+
init_registry();
|
|
25647
|
+
}
|
|
25648
|
+
});
|
|
25649
|
+
|
|
25650
|
+
// packages/core/dist/kraken/runtime/types.js
|
|
25651
|
+
var PlanError;
|
|
25652
|
+
var init_types6 = __esm({
|
|
25653
|
+
"packages/core/dist/kraken/runtime/types.js"() {
|
|
25654
|
+
"use strict";
|
|
25655
|
+
PlanError = class extends Error {
|
|
25656
|
+
name = "PlanError";
|
|
25657
|
+
kind;
|
|
25658
|
+
cause;
|
|
25659
|
+
constructor(kind, message, cause) {
|
|
25660
|
+
super(message);
|
|
25661
|
+
this.kind = kind;
|
|
25662
|
+
if (cause !== void 0)
|
|
25663
|
+
this.cause = cause;
|
|
25664
|
+
}
|
|
25665
|
+
};
|
|
25666
|
+
}
|
|
25667
|
+
});
|
|
25668
|
+
|
|
25669
|
+
// packages/core/dist/kraken/runtime/sandbox.js
|
|
25670
|
+
import vm from "node:vm";
|
|
25671
|
+
function scanForFootguns(bundleCode) {
|
|
25672
|
+
if (bundleCode.length > MAX_BUNDLE_BYTES) {
|
|
25673
|
+
return [
|
|
25674
|
+
`bundle is ${bundleCode.length} bytes; max is ${MAX_BUNDLE_BYTES} (LLM is almost certainly looping)`
|
|
25675
|
+
];
|
|
25676
|
+
}
|
|
25677
|
+
const hits = [];
|
|
25678
|
+
for (const re of FORBIDDEN_TOKENS) {
|
|
25679
|
+
const m = re.exec(bundleCode);
|
|
25680
|
+
if (m)
|
|
25681
|
+
hits.push(`forbidden token "${m[0]}"`);
|
|
25682
|
+
}
|
|
25683
|
+
return hits;
|
|
25684
|
+
}
|
|
25685
|
+
function deepFreeze(value) {
|
|
25686
|
+
if (value === null || typeof value !== "object")
|
|
25687
|
+
return value;
|
|
25688
|
+
if (Object.isFrozen(value))
|
|
25689
|
+
return value;
|
|
25690
|
+
const obj = value;
|
|
25691
|
+
for (const k of Object.keys(obj))
|
|
25692
|
+
deepFreeze(obj[k]);
|
|
25693
|
+
return Object.freeze(value);
|
|
25694
|
+
}
|
|
25695
|
+
async function runInSandbox(opts) {
|
|
25696
|
+
const { bundleCode, sdk, timeoutMs } = opts;
|
|
25697
|
+
if (!opts.skipFootgunScan) {
|
|
25698
|
+
const footguns = scanForFootguns(bundleCode);
|
|
25699
|
+
if (footguns.length > 0) {
|
|
25700
|
+
throw new PlanError("sandbox_breach", `script bundle failed footgun scan: ${footguns.join("; ")}`);
|
|
25701
|
+
}
|
|
25702
|
+
}
|
|
25703
|
+
const frozen = deepFreeze({ ...sdk });
|
|
25704
|
+
const sandbox = {
|
|
25705
|
+
__zelari_sdk__: frozen,
|
|
25706
|
+
// A console that prefixes lines with the graph id; useful for the
|
|
25707
|
+
// workbench view and harmless to expose (it just logs to stdout).
|
|
25708
|
+
console: makeSandboxConsole()
|
|
25709
|
+
};
|
|
25710
|
+
const context = vm.createContext(sandbox, {
|
|
25711
|
+
name: "kraken-script",
|
|
25712
|
+
codeGeneration: { strings: false, wasm: false }
|
|
25713
|
+
});
|
|
25714
|
+
const start = Date.now();
|
|
25715
|
+
const vmOpts = {
|
|
25716
|
+
timeout: Math.max(1, timeoutMs),
|
|
25717
|
+
displayErrors: true,
|
|
25718
|
+
breakOnSigint: true
|
|
25719
|
+
};
|
|
25720
|
+
try {
|
|
25721
|
+
const value = await vm.runInContext(bundleCode, context, vmOpts);
|
|
25722
|
+
return { value, durationMs: Date.now() - start };
|
|
25723
|
+
} catch (err) {
|
|
25724
|
+
const msg = err && typeof err === "object" && "message" in err ? String(err.message ?? "") : String(err);
|
|
25725
|
+
if (/timed out/i.test(msg)) {
|
|
25726
|
+
throw new PlanError("budget_exceeded", `script exceeded ${timeoutMs}ms budget`);
|
|
25727
|
+
}
|
|
25728
|
+
if (opts.signal?.aborted) {
|
|
25729
|
+
throw new PlanError("cancelled", "script aborted by host");
|
|
25730
|
+
}
|
|
25731
|
+
if (err instanceof PlanError)
|
|
25732
|
+
throw err;
|
|
25733
|
+
throw new PlanError("runtime_error", msg, err);
|
|
25734
|
+
}
|
|
25735
|
+
}
|
|
25736
|
+
function makeSandboxConsole() {
|
|
25737
|
+
const prefix = "[kraken-script]";
|
|
25738
|
+
return {
|
|
25739
|
+
...console,
|
|
25740
|
+
log: (...args) => console.log(prefix, ...args),
|
|
25741
|
+
info: (...args) => console.info(prefix, ...args),
|
|
25742
|
+
warn: (...args) => console.warn(prefix, ...args),
|
|
25743
|
+
error: (...args) => console.error(prefix, ...args)
|
|
25744
|
+
};
|
|
25745
|
+
}
|
|
25746
|
+
var MAX_BUNDLE_BYTES, FORBIDDEN_TOKENS;
|
|
25747
|
+
var init_sandbox = __esm({
|
|
25748
|
+
"packages/core/dist/kraken/runtime/sandbox.js"() {
|
|
25749
|
+
"use strict";
|
|
25750
|
+
init_types6();
|
|
25751
|
+
MAX_BUNDLE_BYTES = 256 * 1024;
|
|
25752
|
+
FORBIDDEN_TOKENS = [
|
|
25753
|
+
/\bprocess\b/,
|
|
25754
|
+
/\brequire\b/,
|
|
25755
|
+
/\bmodule\b/,
|
|
25756
|
+
/\bexports\b/,
|
|
25757
|
+
/\b__dirname\b/,
|
|
25758
|
+
/\b__filename\b/,
|
|
25759
|
+
/\bglobalThis\b/,
|
|
25760
|
+
/\bglobal\b(?!\s*=)/,
|
|
25761
|
+
/\bBuffer\b/,
|
|
25762
|
+
/\beval\b/,
|
|
25763
|
+
/\bFunction\s*\(/,
|
|
25764
|
+
/\bnew\s+Function\b/
|
|
25765
|
+
];
|
|
25766
|
+
}
|
|
25767
|
+
});
|
|
25768
|
+
|
|
25769
|
+
// packages/core/dist/kraken/runtime/runner.js
|
|
25770
|
+
function resolveMaxTentacles(env = process.env) {
|
|
25771
|
+
const raw = env.ZELARI_KRAKEN_MAX_TENTACLES;
|
|
25772
|
+
if (raw === void 0 || raw === "")
|
|
25773
|
+
return DEFAULT_MAX_TENTACLES;
|
|
25774
|
+
const n = Number.parseInt(raw, 10);
|
|
25775
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_MAX_TENTACLES;
|
|
25776
|
+
}
|
|
25777
|
+
function resolvePlanTimeoutMs(env = process.env) {
|
|
25778
|
+
const raw = env.ZELARI_KRAKEN_PLAN_TIMEOUT_MS;
|
|
25779
|
+
if (raw === void 0 || raw === "")
|
|
25780
|
+
return DEFAULT_PLAN_TIMEOUT_MS;
|
|
25781
|
+
const n = Number.parseInt(raw, 10);
|
|
25782
|
+
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_PLAN_TIMEOUT_MS;
|
|
25783
|
+
}
|
|
25784
|
+
function cap(text, max) {
|
|
25785
|
+
if (text.length <= max)
|
|
25786
|
+
return text;
|
|
25787
|
+
return `${text.slice(0, max)}
|
|
25788
|
+
\u2026 [truncated]`;
|
|
25789
|
+
}
|
|
25790
|
+
function buildRef(id, opts, result) {
|
|
25791
|
+
const findings = cap(result.ok ? (result.result ?? "").trim() : result.error ?? "unknown error", MAX_FINDINGS_CHARS);
|
|
25792
|
+
const ref = {
|
|
25793
|
+
id,
|
|
25794
|
+
kind: opts.kind,
|
|
25795
|
+
label: opts.label,
|
|
25796
|
+
status: result.ok ? "done" : "error",
|
|
25797
|
+
findings,
|
|
25798
|
+
scope: opts.scope,
|
|
25799
|
+
...result.durationMs !== void 0 ? { durationMs: result.durationMs } : {},
|
|
25800
|
+
...result.worktree !== void 0 ? { worktree: result.worktree } : {}
|
|
25801
|
+
};
|
|
25802
|
+
if (isReviewerKind(opts.kind)) {
|
|
25803
|
+
ref.verdict = defaultPersonaParse(findings).verdict;
|
|
25804
|
+
}
|
|
25805
|
+
return ref;
|
|
25806
|
+
}
|
|
25807
|
+
var DEFAULT_MAX_TENTACLES, DEFAULT_PLAN_TIMEOUT_MS, ScriptRunner;
|
|
25808
|
+
var init_runner = __esm({
|
|
25809
|
+
"packages/core/dist/kraken/runtime/runner.js"() {
|
|
25810
|
+
"use strict";
|
|
25811
|
+
init_verdict();
|
|
25812
|
+
init_personas();
|
|
25813
|
+
init_types6();
|
|
25814
|
+
DEFAULT_MAX_TENTACLES = 200;
|
|
25815
|
+
DEFAULT_PLAN_TIMEOUT_MS = 30 * 6e4;
|
|
25816
|
+
ScriptRunner = class {
|
|
25817
|
+
host;
|
|
25818
|
+
goal;
|
|
25819
|
+
graphId;
|
|
25820
|
+
parentCwd;
|
|
25821
|
+
sessionId;
|
|
25822
|
+
maxTentacles;
|
|
25823
|
+
planTimeoutMs;
|
|
25824
|
+
startTime;
|
|
25825
|
+
tentaclesById = /* @__PURE__ */ new Map();
|
|
25826
|
+
tentacleCount = 0;
|
|
25827
|
+
mergeCount = 0;
|
|
25828
|
+
cancelled = false;
|
|
25829
|
+
constructor(opts) {
|
|
25830
|
+
this.host = opts.host;
|
|
25831
|
+
this.goal = opts.goal;
|
|
25832
|
+
this.graphId = opts.graphId;
|
|
25833
|
+
this.parentCwd = opts.parentCwd;
|
|
25834
|
+
this.sessionId = opts.sessionId;
|
|
25835
|
+
this.maxTentacles = opts.maxTentacles ?? resolveMaxTentacles();
|
|
25836
|
+
this.planTimeoutMs = opts.planTimeoutMs ?? resolvePlanTimeoutMs();
|
|
25837
|
+
this.startTime = Date.now();
|
|
25838
|
+
}
|
|
25839
|
+
/** Build the `PlanCapabilities` object the sandbox will see. */
|
|
25840
|
+
buildSdk() {
|
|
25841
|
+
return {
|
|
25842
|
+
tentacle: (opts) => this.callTentacle(opts),
|
|
25843
|
+
barrier: (refs) => this.callBarrier(refs),
|
|
25844
|
+
race: (refs) => this.callRace(refs),
|
|
25845
|
+
while_: (cond, body, maxIter) => this.callWhile(cond, body, maxIter),
|
|
25846
|
+
until: (cond, body, maxIter) => this.callUntil(cond, body, maxIter),
|
|
25847
|
+
merge: (refs, opts) => this.callMerge(refs, opts),
|
|
25848
|
+
checkpoint: (label) => this.callCheckpoint(label),
|
|
25849
|
+
log: (msg, data) => this.callLog(msg, data),
|
|
25850
|
+
emit: (payload) => this.callEmit(payload),
|
|
25851
|
+
getContext: () => this.callGetContext(),
|
|
25852
|
+
sendTo: (peerId, payload) => this.callSendTo(peerId, payload)
|
|
25853
|
+
};
|
|
25854
|
+
}
|
|
25855
|
+
/** One tentacle = one `runTentacle` call. The host returns a raw result;
|
|
25856
|
+
* we turn it into a `TentacleRef` and stash it. */
|
|
25857
|
+
async callTentacle(opts) {
|
|
25858
|
+
if (this.cancelled)
|
|
25859
|
+
throw new PlanError("cancelled", "runner is cancelled");
|
|
25860
|
+
if (this.tentacleCount >= this.maxTentacles) {
|
|
25861
|
+
throw new PlanError("budget_exceeded", `tentacle cap (${this.maxTentacles}) reached; raise ZELARI_KRAKEN_MAX_TENTACLES`);
|
|
25862
|
+
}
|
|
25863
|
+
if (this.tentacleCount > 0 && Date.now() - this.startTime > this.planTimeoutMs) {
|
|
25864
|
+
throw new PlanError("budget_exceeded", `plan wall-clock budget (${this.planTimeoutMs}ms) exceeded`);
|
|
25865
|
+
}
|
|
25866
|
+
this.tentacleCount += 1;
|
|
25867
|
+
const id = `t${String(this.tentacleCount).padStart(4, "0")}`;
|
|
25868
|
+
const res = await this.host.runTentacle({
|
|
25869
|
+
node: opts,
|
|
25870
|
+
parentCwd: this.parentCwd,
|
|
25871
|
+
sessionId: this.sessionId
|
|
25872
|
+
});
|
|
25873
|
+
const ref = buildRef(id, opts, res);
|
|
25874
|
+
this.tentaclesById.set(id, ref);
|
|
25875
|
+
return ref;
|
|
25876
|
+
}
|
|
25877
|
+
async callBarrier(refs) {
|
|
25878
|
+
if (refs.some((r) => !this.tentaclesById.has(r.id))) {
|
|
25879
|
+
throw new PlanError("runtime_error", "barrier() received a ref not in this runner");
|
|
25880
|
+
}
|
|
25881
|
+
return refs;
|
|
25882
|
+
}
|
|
25883
|
+
async callRace(refs) {
|
|
25884
|
+
if (refs.length === 0) {
|
|
25885
|
+
throw new PlanError("runtime_error", "race() requires at least one ref");
|
|
25886
|
+
}
|
|
25887
|
+
let winner = refs[0];
|
|
25888
|
+
for (const r of refs) {
|
|
25889
|
+
const a = winner.durationMs ?? Number.POSITIVE_INFINITY;
|
|
25890
|
+
const b = r.durationMs ?? Number.POSITIVE_INFINITY;
|
|
25891
|
+
if (b < a || b === a && r.id < winner.id)
|
|
25892
|
+
winner = r;
|
|
25893
|
+
}
|
|
25894
|
+
return winner;
|
|
25895
|
+
}
|
|
25896
|
+
async callWhile(cond, body, maxIter) {
|
|
25897
|
+
if (maxIter < 1)
|
|
25898
|
+
throw new PlanError("runtime_error", "while_() maxIter must be \u2265 1");
|
|
25899
|
+
const out = [];
|
|
25900
|
+
let iter = 0;
|
|
25901
|
+
while (await cond()) {
|
|
25902
|
+
if (iter >= maxIter) {
|
|
25903
|
+
throw new PlanError("budget_exceeded", `while_() exceeded maxIter=${maxIter} without cond turning false`);
|
|
25904
|
+
}
|
|
25905
|
+
out.push(await body());
|
|
25906
|
+
iter += 1;
|
|
25907
|
+
}
|
|
25908
|
+
return out;
|
|
25909
|
+
}
|
|
25910
|
+
async callUntil(cond, body, maxIter) {
|
|
25911
|
+
return this.callWhile(async () => !await cond(), body, maxIter);
|
|
25912
|
+
}
|
|
25913
|
+
/** One merge per plan. A second call is a structured error. */
|
|
25914
|
+
async callMerge(refs, opts = {}) {
|
|
25915
|
+
if (this.mergeCount > 0) {
|
|
25916
|
+
throw new PlanError("merge_already_done", "merge() called more than once; a plan can merge at most one batch");
|
|
25917
|
+
}
|
|
25918
|
+
if (refs.length === 0) {
|
|
25919
|
+
throw new PlanError("runtime_error", "merge() requires at least one ref");
|
|
25920
|
+
}
|
|
25921
|
+
for (const r of refs) {
|
|
25922
|
+
if (!this.tentaclesById.has(r.id)) {
|
|
25923
|
+
throw new PlanError("runtime_error", `merge() received unknown ref "${r.id}"`);
|
|
25924
|
+
}
|
|
25925
|
+
}
|
|
25926
|
+
this.mergeCount += 1;
|
|
25927
|
+
return this.host.mergeWorktrees({
|
|
25928
|
+
refs,
|
|
25929
|
+
parentCwd: this.parentCwd,
|
|
25930
|
+
strategy: opts.strategy ?? "squash-sequential",
|
|
25931
|
+
...opts.message ? { message: opts.message } : {},
|
|
25932
|
+
...opts.cleanup !== void 0 ? { cleanup: opts.cleanup } : {}
|
|
25933
|
+
});
|
|
25934
|
+
}
|
|
25935
|
+
async callCheckpoint(label) {
|
|
25936
|
+
const snapshot = {
|
|
25937
|
+
graphId: this.graphId,
|
|
25938
|
+
goal: this.goal,
|
|
25939
|
+
takenAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25940
|
+
completed: [...this.tentaclesById.values()].filter((r) => r.status === "done"),
|
|
25941
|
+
inFlight: [...this.tentaclesById.values()].filter((r) => r.status === "running"),
|
|
25942
|
+
failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
|
|
25943
|
+
pending: []
|
|
25944
|
+
};
|
|
25945
|
+
const path47 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
|
|
25946
|
+
this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path47}`);
|
|
25947
|
+
return snapshot;
|
|
25948
|
+
}
|
|
25949
|
+
callLog(msg, data) {
|
|
25950
|
+
const line = data ? `${msg} ${JSON.stringify(data)}` : msg;
|
|
25951
|
+
this.host.log(line);
|
|
25952
|
+
}
|
|
25953
|
+
callEmit(payload) {
|
|
25954
|
+
this.host.log(`emit: ${payload.kind}${payload.detail ? ` \u2014 ${payload.detail}` : ""}`);
|
|
25955
|
+
}
|
|
25956
|
+
callGetContext() {
|
|
25957
|
+
return {
|
|
25958
|
+
graphId: this.graphId,
|
|
25959
|
+
goal: this.goal,
|
|
25960
|
+
parentCwd: this.parentCwd,
|
|
25961
|
+
sessionId: this.sessionId,
|
|
25962
|
+
tentacles: this.tentaclesById,
|
|
25963
|
+
maxTentacles: this.maxTentacles,
|
|
25964
|
+
planTimeoutMs: this.planTimeoutMs
|
|
25965
|
+
};
|
|
25966
|
+
}
|
|
25967
|
+
callSendTo(peerId, payload) {
|
|
25968
|
+
this.host.log(`sendTo(${peerId}): ${payload.kind}${payload.detail ? ` \u2014 ${payload.detail}` : ""}`);
|
|
25969
|
+
}
|
|
25970
|
+
/** Called by the host after the sandbox returns, to mark the run. */
|
|
25971
|
+
finalize(opts) {
|
|
25972
|
+
if (opts.cancelled)
|
|
25973
|
+
this.cancelled = true;
|
|
25974
|
+
const failed = [...this.tentaclesById.values()].filter((r) => r.status === "error");
|
|
25975
|
+
return {
|
|
25976
|
+
tentacles: this.tentaclesById,
|
|
25977
|
+
mergeCount: this.mergeCount,
|
|
25978
|
+
converged: opts.converged,
|
|
25979
|
+
cancelled: this.cancelled,
|
|
25980
|
+
durationMs: Date.now() - this.startTime,
|
|
25981
|
+
unresolvedFindings: failed.map((r) => ({
|
|
25982
|
+
nodeId: r.id,
|
|
25983
|
+
label: r.label,
|
|
25984
|
+
reason: r.status === "error" ? "fail" : "unknown",
|
|
25985
|
+
findings: r.findings
|
|
25986
|
+
}))
|
|
25987
|
+
};
|
|
25988
|
+
}
|
|
25989
|
+
};
|
|
25990
|
+
}
|
|
25991
|
+
});
|
|
25992
|
+
|
|
25993
|
+
// packages/core/dist/kraken/runtime/index.js
|
|
25994
|
+
var init_runtime = __esm({
|
|
25995
|
+
"packages/core/dist/kraken/runtime/index.js"() {
|
|
25996
|
+
"use strict";
|
|
25997
|
+
init_types6();
|
|
25998
|
+
init_sandbox();
|
|
25999
|
+
init_runner();
|
|
26000
|
+
}
|
|
26001
|
+
});
|
|
26002
|
+
|
|
25094
26003
|
// packages/core/dist/kraken/index.js
|
|
25095
26004
|
var init_kraken = __esm({
|
|
25096
26005
|
"packages/core/dist/kraken/index.js"() {
|
|
25097
26006
|
"use strict";
|
|
25098
26007
|
init_graph();
|
|
25099
26008
|
init_conflict();
|
|
26009
|
+
init_verdict();
|
|
26010
|
+
init_personas();
|
|
26011
|
+
init_runtime();
|
|
26012
|
+
init_weakness();
|
|
25100
26013
|
}
|
|
25101
26014
|
});
|
|
25102
26015
|
|
|
25103
26016
|
// packages/core/dist/index.js
|
|
26017
|
+
var dist_exports = {};
|
|
26018
|
+
__export(dist_exports, {
|
|
26019
|
+
ADVANCED_TOOL_DEFINITIONS: () => ADVANCED_TOOL_DEFINITIONS,
|
|
26020
|
+
AGENT_ROLES: () => AGENT_ROLES,
|
|
26021
|
+
ALL_TOOLS: () => ALL_TOOLS,
|
|
26022
|
+
AgentHarness: () => AgentHarness,
|
|
26023
|
+
BENNETTS_RAZOR: () => BENNETTS_RAZOR,
|
|
26024
|
+
BENNETTS_RAZOR_SHORT: () => BENNETTS_RAZOR_SHORT,
|
|
26025
|
+
CLARIFICATION_PROTOCOL_MODULE: () => CLARIFICATION_PROTOCOL_MODULE,
|
|
26026
|
+
CODING_CATEGORY: () => CODING_CATEGORY,
|
|
26027
|
+
CODING_PRACTICES_MODULE: () => CODING_PRACTICES_MODULE,
|
|
26028
|
+
CODING_SKILL_CATALOG: () => CODING_SKILL_CATALOG,
|
|
26029
|
+
COLLABORATION_DIRECTIVE: () => COLLABORATION_DIRECTIVE,
|
|
26030
|
+
COMPOSITOR_ONLY_PROPS: () => COMPOSITOR_ONLY_PROPS,
|
|
26031
|
+
DEFAULT_MAX_NODES: () => DEFAULT_MAX_NODES,
|
|
26032
|
+
DEFAULT_MAX_TENTACLES: () => DEFAULT_MAX_TENTACLES,
|
|
26033
|
+
DEFAULT_NFR_SPEC: () => DEFAULT_NFR_SPEC,
|
|
26034
|
+
DEFAULT_PLAN_TIMEOUT_MS: () => DEFAULT_PLAN_TIMEOUT_MS,
|
|
26035
|
+
DEGRADED_RUN_BANNER: () => DEGRADED_RUN_BANNER,
|
|
26036
|
+
DESIGN_PHASE_MODE_BANNER: () => DESIGN_PHASE_MODE_BANNER,
|
|
26037
|
+
DESIGN_PHASE_REQUIREMENTS: () => DESIGN_PHASE_REQUIREMENTS,
|
|
26038
|
+
DESIGN_PHASE_REQUIREMENT_SETS: () => DESIGN_PHASE_REQUIREMENT_SETS,
|
|
26039
|
+
DOOM_LOOP_THRESHOLD: () => DOOM_LOOP_THRESHOLD,
|
|
26040
|
+
EventBus: () => EventBus,
|
|
26041
|
+
IMPLEMENTATION_ADVISOR_BANNER: () => IMPLEMENTATION_ADVISOR_BANNER,
|
|
26042
|
+
IMPLEMENTATION_IMPLEMENTER_BANNER: () => IMPLEMENTATION_IMPLEMENTER_BANNER,
|
|
26043
|
+
IMPLEMENTATION_MODE_BANNER: () => IMPLEMENTATION_MODE_BANNER,
|
|
26044
|
+
IMPLEMENTATION_WRITE_REQUIREMENTS: () => IMPLEMENTATION_WRITE_REQUIREMENTS,
|
|
26045
|
+
KRAKEN_IDENTITY_MODULE: () => KRAKEN_IDENTITY_MODULE,
|
|
26046
|
+
KRAKEN_LEAD_PLAYBOOK_MODULE: () => KRAKEN_LEAD_PLAYBOOK_MODULE,
|
|
26047
|
+
LANGUAGE_POLICY_MODULE_TYPE: () => LANGUAGE_POLICY_MODULE_TYPE,
|
|
26048
|
+
LAYOUT_MOTION_PROPS: () => LAYOUT_MOTION_PROPS,
|
|
26049
|
+
LESSONS_FILE: () => LESSONS_FILE,
|
|
26050
|
+
MAX_DELIVERY_ATTEMPTS: () => MAX_DELIVERY_ATTEMPTS,
|
|
26051
|
+
MAX_FINDINGS_CHARS: () => MAX_FINDINGS_CHARS,
|
|
26052
|
+
MAX_RETRY_PER_MEMBER: () => MAX_RETRY_PER_MEMBER,
|
|
26053
|
+
MUTATING_PROJECT_TOOLS: () => MUTATING_PROJECT_TOOLS,
|
|
26054
|
+
NATIVE_TOOL_PROTOCOL_MODULE: () => NATIVE_TOOL_PROTOCOL_MODULE,
|
|
26055
|
+
NFR_KEYWORDS: () => NFR_KEYWORDS,
|
|
26056
|
+
NON_RETRY_AGENTS: () => NON_RETRY_AGENTS,
|
|
26057
|
+
OUTPUT_QUALITY_DIRECTIVE: () => OUTPUT_QUALITY_DIRECTIVE,
|
|
26058
|
+
PROMPT_MODULES: () => PROMPT_MODULES,
|
|
26059
|
+
PROPRIETARY_REFUSAL_TEXT: () => PROPRIETARY_REFUSAL_TEXT,
|
|
26060
|
+
PROPRIETARY_SECRECY_MARKER: () => PROPRIETARY_SECRECY_MARKER,
|
|
26061
|
+
PROPRIETARY_SECRECY_MODULE: () => PROPRIETARY_SECRECY_MODULE,
|
|
26062
|
+
PlanError: () => PlanError,
|
|
26063
|
+
SINGLE_AGENT_IDENTITY_MODULE: () => SINGLE_AGENT_IDENTITY_MODULE,
|
|
26064
|
+
SKILL_CATALOG: () => SKILL_CATALOG,
|
|
26065
|
+
STRUCTURED_REASONING_DIRECTIVE: () => STRUCTURED_REASONING_DIRECTIVE,
|
|
26066
|
+
ScriptRunner: () => ScriptRunner,
|
|
26067
|
+
SessionJsonlWriter: () => SessionJsonlWriter,
|
|
26068
|
+
TERMINAL_STATUSES: () => TERMINAL_STATUSES,
|
|
26069
|
+
TEXT_LOOP_RECOVERY_SYSTEM: () => TEXT_LOOP_RECOVERY_SYSTEM,
|
|
26070
|
+
TEXT_LOOP_RECOVERY_USER_PROMPT: () => TEXT_LOOP_RECOVERY_USER_PROMPT,
|
|
26071
|
+
TIER_RANK: () => TIER_RANK,
|
|
26072
|
+
TOOL_DEFINITIONS: () => TOOL_DEFINITIONS,
|
|
26073
|
+
TOOL_USE_PROTOCOL_DIRECTIVE: () => TOOL_USE_PROTOCOL_DIRECTIVE,
|
|
26074
|
+
TURN_COMPLETION_MODULE: () => TURN_COMPLETION_MODULE,
|
|
26075
|
+
UnknownMemberError: () => UnknownMemberError,
|
|
26076
|
+
VAULT_TOOL_DEFINITIONS: () => VAULT_TOOL_DEFINITIONS,
|
|
26077
|
+
WEAKNESS_METER_PROMPT: () => WEAKNESS_METER_PROMPT,
|
|
26078
|
+
WeaknessMeterResponseSchema: () => WeaknessMeterResponseSchema,
|
|
26079
|
+
applyCompletionRetry: () => applyCompletionRetry,
|
|
26080
|
+
applyDeterministicAutofix: () => applyDeterministicAutofix,
|
|
26081
|
+
applyImplementationWriteRetry: () => applyImplementationWriteRetry,
|
|
26082
|
+
applyInlineJsAutofix: () => applyInlineJsAutofix,
|
|
26083
|
+
applyMotionAutofix: () => applyMotionAutofix,
|
|
26084
|
+
applyRetryIfMissing: () => applyRetryIfMissing,
|
|
26085
|
+
auditDegradedBanner: () => auditDegradedBanner,
|
|
26086
|
+
auditSynthesisTiers: () => auditSynthesisTiers,
|
|
26087
|
+
buildCouncilCompletion: () => buildCouncilCompletion,
|
|
26088
|
+
buildCustomParameters: () => buildCustomParameters,
|
|
26089
|
+
buildDeliveryFixPrompt: () => buildDeliveryFixPrompt,
|
|
26090
|
+
buildImplementationVerifyRetryPrompt: () => buildImplementationVerifyRetryPrompt,
|
|
26091
|
+
buildImplementationWriteRetryPrompt: () => buildImplementationWriteRetryPrompt,
|
|
26092
|
+
buildLanguageDirective: () => buildLanguageDirective,
|
|
26093
|
+
buildLanguagePolicyModule: () => buildLanguagePolicyModule,
|
|
26094
|
+
buildLanguagePolicyModuleFor: () => buildLanguagePolicyModuleFor,
|
|
26095
|
+
buildMissionBrief: () => buildMissionBrief,
|
|
26096
|
+
buildMotionFixPrompt: () => buildMotionFixPrompt,
|
|
26097
|
+
buildRetryPrompt: () => buildRetryPrompt,
|
|
26098
|
+
buildSkillDefinition: () => buildSkillDefinition,
|
|
26099
|
+
buildSystemPrompt: () => buildSystemPrompt,
|
|
26100
|
+
buildSystemPromptSplit: () => buildSystemPromptSplit,
|
|
26101
|
+
canRunParallel: () => canRunParallel,
|
|
26102
|
+
captureFailure: () => captureFailure,
|
|
26103
|
+
checkImplementationCompletion: () => checkImplementationCompletion,
|
|
26104
|
+
checkImplementationDelivery: () => checkImplementationDelivery,
|
|
26105
|
+
checkMemberToolEmissionSets: () => checkMemberToolEmissionSets,
|
|
26106
|
+
checkMemberToolEmissions: () => checkMemberToolEmissions,
|
|
26107
|
+
classifyMission: () => classifyMission,
|
|
26108
|
+
classifyTaskScope: () => classifyTaskScope,
|
|
26109
|
+
cleanAgentContent: () => cleanAgentContent,
|
|
26110
|
+
clearCustomTools: () => clearCustomTools,
|
|
26111
|
+
cliToolToEnhanced: () => cliToolToEnhanced,
|
|
26112
|
+
collapseLoopedAssistantText: () => collapseLoopedAssistantText,
|
|
26113
|
+
computeAgentSkills: () => computeAgentSkills,
|
|
26114
|
+
computeAgentTools: () => computeAgentTools,
|
|
26115
|
+
councilModeBanner: () => councilModeBanner,
|
|
26116
|
+
councilTierFromSize: () => councilTierFromSize,
|
|
26117
|
+
countByStatus: () => countByStatus,
|
|
26118
|
+
countEmittedWriteTools: () => countEmittedWriteTools,
|
|
26119
|
+
createBrainEvent: () => createBrainEvent,
|
|
26120
|
+
createDefaultSystemPromptConfig: () => createDefaultSystemPromptConfig,
|
|
26121
|
+
createGraph: () => createGraph,
|
|
26122
|
+
defaultPersonaParse: () => defaultPersonaParse,
|
|
26123
|
+
detectAssistantTextLoop: () => detectAssistantTextLoop,
|
|
26124
|
+
detectDegradedRun: () => detectDegradedRun,
|
|
26125
|
+
detectResponseLanguage: () => detectResponseLanguage,
|
|
26126
|
+
disjointScopeSets: () => disjointScopeSets,
|
|
26127
|
+
enforceDesignPhaseToolEmissions: () => enforceDesignPhaseToolEmissions,
|
|
26128
|
+
executeTool: () => executeTool,
|
|
26129
|
+
extractCitations: () => extractCitations,
|
|
26130
|
+
extractRequirementsBlock: () => extractRequirementsBlock,
|
|
26131
|
+
extractTaskScope: () => extractTaskScope,
|
|
26132
|
+
failedNodeIds: () => failedNodeIds,
|
|
26133
|
+
filterByWeakness: () => filterByWeakness,
|
|
26134
|
+
filterDeliveryBlockingFails: () => filterDeliveryBlockingFails,
|
|
26135
|
+
findCodingSkillsByCategory: () => findCodingSkillsByCategory,
|
|
26136
|
+
findCodingSkillsByTag: () => findCodingSkillsByTag,
|
|
26137
|
+
findSkillsByIds: () => findSkillsByIds,
|
|
26138
|
+
findSkillsByTag: () => findSkillsByTag,
|
|
26139
|
+
formatLessonsForContext: () => formatLessonsForContext,
|
|
26140
|
+
getAgent: () => getAgent,
|
|
26141
|
+
getAllTools: () => getAllTools,
|
|
26142
|
+
getAvailableTools: () => getAvailableTools,
|
|
26143
|
+
getBasePromptModules: () => getBasePromptModules,
|
|
26144
|
+
getBuiltinSkillIds: () => getBuiltinSkillIds,
|
|
26145
|
+
getCodingSkillById: () => getCodingSkillById,
|
|
26146
|
+
getCouncilAgents: () => getCouncilAgents,
|
|
26147
|
+
getCouncilDirectiveModules: () => getCouncilDirectiveModules,
|
|
26148
|
+
getPersona: () => getPersona,
|
|
26149
|
+
getPromptModule: () => getPromptModule,
|
|
26150
|
+
getProviderTools: () => getProviderTools,
|
|
26151
|
+
getReadyNodes: () => getReadyNodes,
|
|
26152
|
+
getSkillById: () => getSkillById,
|
|
26153
|
+
getSkillMetadata: () => getSkillMetadata,
|
|
26154
|
+
getSkillsByCategory: () => getSkillsByCategory,
|
|
26155
|
+
getToolDescriptions: () => getToolDescriptions,
|
|
26156
|
+
hasInteractiveClarification: () => hasInteractiveClarification,
|
|
26157
|
+
hashToolCall: () => hashToolCall,
|
|
26158
|
+
isAnswerLeak: () => isAnswerLeak,
|
|
26159
|
+
isBrainAgentEndEvent: () => isBrainAgentEndEvent,
|
|
26160
|
+
isBrainAgentStartEvent: () => isBrainAgentStartEvent,
|
|
26161
|
+
isBrainCouncilModeEvent: () => isBrainCouncilModeEvent,
|
|
26162
|
+
isBrainErrorEvent: () => isBrainErrorEvent,
|
|
26163
|
+
isBrainMemberCostEvent: () => isBrainMemberCostEvent,
|
|
26164
|
+
isBrainMessageDeltaEvent: () => isBrainMessageDeltaEvent,
|
|
26165
|
+
isBrainMessageEndEvent: () => isBrainMessageEndEvent,
|
|
26166
|
+
isBrainMessageStartEvent: () => isBrainMessageStartEvent,
|
|
26167
|
+
isBrainQueueUpdateEvent: () => isBrainQueueUpdateEvent,
|
|
26168
|
+
isBrainSessionCompactedEvent: () => isBrainSessionCompactedEvent,
|
|
26169
|
+
isBrainThinkingDeltaEvent: () => isBrainThinkingDeltaEvent,
|
|
26170
|
+
isBrainToolExecutionEndEvent: () => isBrainToolExecutionEndEvent,
|
|
26171
|
+
isBrainToolExecutionStartEvent: () => isBrainToolExecutionStartEvent,
|
|
26172
|
+
isBrainToolExecutionUpdateEvent: () => isBrainToolExecutionUpdateEvent,
|
|
26173
|
+
isConverged: () => isConverged,
|
|
26174
|
+
isReviewerKind: () => isReviewerKind,
|
|
26175
|
+
isSettled: () => isSettled,
|
|
26176
|
+
isStatusTheaterUnit: () => isStatusTheaterUnit,
|
|
26177
|
+
isValidTool: () => isValidTool,
|
|
26178
|
+
isVerifyToolCheckSkipped: () => isVerifyToolCheckSkipped,
|
|
26179
|
+
jaccardSimilarity: () => jaccardSimilarity,
|
|
26180
|
+
lintSynthesisHonesty: () => lintSynthesisHonesty,
|
|
26181
|
+
listCodingSkills: () => listCodingSkills,
|
|
26182
|
+
listPersonas: () => listPersonas,
|
|
26183
|
+
listSkills: () => listSkills,
|
|
26184
|
+
loadNfrSpec: () => loadNfrSpec,
|
|
26185
|
+
matchCheckPrefix: () => matchCheckPrefix,
|
|
26186
|
+
normalizeForSignature: () => normalizeForSignature,
|
|
26187
|
+
normalizeLoopUnit: () => normalizeLoopUnit,
|
|
26188
|
+
normalizeScopePath: () => normalizeScopePath,
|
|
26189
|
+
normalizeTextToolArgs: () => normalizeTextToolArgs,
|
|
26190
|
+
parseClarificationRequest: () => parseClarificationRequest,
|
|
26191
|
+
parseEvidenceTier: () => parseEvidenceTier,
|
|
26192
|
+
parseMinimaxStyleToolCalls: () => parseMinimaxStyleToolCalls,
|
|
26193
|
+
parsePersonaVerdict: () => parsePersonaVerdict,
|
|
26194
|
+
parseProjectRootFromWorkspaceContext: () => parseProjectRootFromWorkspaceContext,
|
|
26195
|
+
parseTextToolCalls: () => parseTextToolCalls,
|
|
26196
|
+
parseThinking: () => parseThinking,
|
|
26197
|
+
parseVerificationTable: () => parseVerificationTable,
|
|
26198
|
+
parseVerifyVerdict: () => parseVerifyVerdict,
|
|
26199
|
+
pathsOverlap: () => pathsOverlap,
|
|
26200
|
+
pickWeakest: () => pickWeakest,
|
|
26201
|
+
promoteMember: () => promoteMember,
|
|
26202
|
+
rankByWeakness: () => rankByWeakness,
|
|
26203
|
+
readLessonsDeduped: () => readLessonsDeduped,
|
|
26204
|
+
readSession: () => readSession,
|
|
26205
|
+
recallLessons: () => recallLessons,
|
|
26206
|
+
registerCodingSkill: () => registerCodingSkill,
|
|
26207
|
+
registerCustomTool: () => registerCustomTool,
|
|
26208
|
+
registerPersona: () => registerPersona,
|
|
26209
|
+
registerSkill: () => registerSkill,
|
|
26210
|
+
renderSkillMarkdown: () => renderSkillMarkdown,
|
|
26211
|
+
replayChairmanTextTools: () => replayChairmanTextTools,
|
|
26212
|
+
resolveAgentSkills: () => resolveAgentSkills,
|
|
26213
|
+
resolveCouncilRunMode: () => resolveCouncilRunMode,
|
|
26214
|
+
resolveMaxTentacles: () => resolveMaxTentacles,
|
|
26215
|
+
resolvePlanTimeoutMs: () => resolvePlanTimeoutMs,
|
|
26216
|
+
resolveResponseLanguage: () => resolveResponseLanguage,
|
|
26217
|
+
resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
|
|
26218
|
+
resolveSkillDependencies: () => resolveSkillDependencies,
|
|
26219
|
+
resolveVerifyRetryTool: () => resolveVerifyRetryTool,
|
|
26220
|
+
restrictImplementationWrites: () => restrictImplementationWrites,
|
|
26221
|
+
runChairmanDeliveryLoop: () => runChairmanDeliveryLoop,
|
|
26222
|
+
runChairmanFixLoop: () => runChairmanFixLoop,
|
|
26223
|
+
runChairmanMicroGate: () => runChairmanMicroGate,
|
|
26224
|
+
runCouncilPure: () => runCouncilPure,
|
|
26225
|
+
runImplementationVerification: () => runImplementationVerification,
|
|
26226
|
+
runInSandbox: () => runInSandbox,
|
|
26227
|
+
runMicroVerificationOnFile: () => runMicroVerificationOnFile,
|
|
26228
|
+
runRetryTurnForMember: () => runRetryTurnForMember,
|
|
26229
|
+
scanForFootguns: () => scanForFootguns,
|
|
26230
|
+
scanKeyframesViolations: () => scanKeyframesViolations,
|
|
26231
|
+
scanTransitionViolations: () => scanTransitionViolations,
|
|
26232
|
+
scrubProprietaryLeak: () => scrubProprietaryLeak,
|
|
26233
|
+
selectParallelWave: () => selectParallelWave,
|
|
26234
|
+
setWorkspaceStubs: () => setWorkspaceStubs,
|
|
26235
|
+
shouldRetryMember: () => shouldRetryMember,
|
|
26236
|
+
slugify: () => slugify2,
|
|
26237
|
+
specificityFromAssumptions: () => specificityFromAssumptions,
|
|
26238
|
+
stripClarificationProtocol: () => stripClarificationProtocol,
|
|
26239
|
+
swapMembers: () => swapMembers,
|
|
26240
|
+
systemMessagesFromSplit: () => systemMessagesFromSplit,
|
|
26241
|
+
taskMatchesNfrKeywords: () => taskMatchesNfrKeywords,
|
|
26242
|
+
tierAtLeast: () => tierAtLeast,
|
|
26243
|
+
tokenizeForSignature: () => tokenizeForSignature,
|
|
26244
|
+
topoLevels: () => topoLevels,
|
|
26245
|
+
unregisterCustomTool: () => unregisterCustomTool,
|
|
26246
|
+
unregisterSkill: () => unregisterSkill,
|
|
26247
|
+
validateCodingSkillRequires: () => validateCodingSkillRequires,
|
|
26248
|
+
validateGraph: () => validateGraph,
|
|
26249
|
+
verifyCitations: () => verifyCitations,
|
|
26250
|
+
warnIfNfrSpecMissing: () => warnIfNfrSpecMissing,
|
|
26251
|
+
weaknessFromMeter: () => weaknessFromMeter,
|
|
26252
|
+
weaknessFromVerdict: () => weaknessFromVerdict,
|
|
26253
|
+
weaknessScoreFromText: () => weaknessScoreFromText,
|
|
26254
|
+
wrapLegacyStream: () => wrapLegacyStream,
|
|
26255
|
+
writeCouncilCompletion: () => writeCouncilCompletion,
|
|
26256
|
+
writeVerificationReport: () => writeVerificationReport
|
|
26257
|
+
});
|
|
25104
26258
|
var init_dist = __esm({
|
|
25105
26259
|
"packages/core/dist/index.js"() {
|
|
25106
26260
|
"use strict";
|
|
@@ -25118,8 +26272,11 @@ var init_dist = __esm({
|
|
|
25118
26272
|
// src/cli/kraken/graphStatus.ts
|
|
25119
26273
|
var graphStatus_exports = {};
|
|
25120
26274
|
__export(graphStatus_exports, {
|
|
26275
|
+
DEFAULT_DIGEST_RESULT_CHARS: () => DEFAULT_DIGEST_RESULT_CHARS,
|
|
25121
26276
|
endKrakenGraphLive: () => endKrakenGraphLive,
|
|
26277
|
+
formatDuration: () => formatDuration2,
|
|
25122
26278
|
formatKrakenGraphAscii: () => formatKrakenGraphAscii,
|
|
26279
|
+
formatKrakenGraphDigest: () => formatKrakenGraphDigest,
|
|
25123
26280
|
formatKrakenGraphSummary: () => formatKrakenGraphSummary,
|
|
25124
26281
|
getKrakenGraphLive: () => getKrakenGraphLive,
|
|
25125
26282
|
resetKrakenGraphLive: () => resetKrakenGraphLive,
|
|
@@ -25147,6 +26304,46 @@ function formatKrakenGraphAscii(graph) {
|
|
|
25147
26304
|
if (lines.length === 0) return summary;
|
|
25148
26305
|
return [summary, ...lines].join("\n");
|
|
25149
26306
|
}
|
|
26307
|
+
function formatDuration2(ms) {
|
|
26308
|
+
if (!Number.isFinite(ms) || ms < 0) return "?";
|
|
26309
|
+
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
26310
|
+
const totalSeconds = Math.round(ms / 1e3);
|
|
26311
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
26312
|
+
const m = Math.floor(totalSeconds / 60);
|
|
26313
|
+
const s = totalSeconds % 60;
|
|
26314
|
+
return s === 0 ? `${m}m` : `${m}m${s}s`;
|
|
26315
|
+
}
|
|
26316
|
+
function formatKrakenGraphDigest(graph, opts = {}) {
|
|
26317
|
+
const maxChars = opts.maxResultChars ?? DEFAULT_DIGEST_RESULT_CHARS;
|
|
26318
|
+
const durations = opts.durationsMs ?? {};
|
|
26319
|
+
const ordered = topoLevels(graph).flat();
|
|
26320
|
+
for (const id of graph.nodes.keys()) {
|
|
26321
|
+
if (!ordered.includes(id)) ordered.push(id);
|
|
26322
|
+
}
|
|
26323
|
+
const lines = [];
|
|
26324
|
+
for (const id of ordered) {
|
|
26325
|
+
const n = graph.nodes.get(id);
|
|
26326
|
+
if (!n) continue;
|
|
26327
|
+
const took = durations[id] !== void 0 ? `, ${formatDuration2(durations[id])}` : "";
|
|
26328
|
+
const detail = n.status === "error" ? n.error : n.result;
|
|
26329
|
+
const firstLine2 = (detail ?? "").trim().split("\n")[0] ?? "";
|
|
26330
|
+
const body = firstLine2.length > maxChars ? `${firstLine2.slice(0, maxChars)}\u2026` : firstLine2;
|
|
26331
|
+
lines.push(
|
|
26332
|
+
`[${STATUS_ICON[n.status]}] ${n.id} (${n.kind}${took})${body ? ` \u2014 ${body}` : ""}`
|
|
26333
|
+
);
|
|
26334
|
+
}
|
|
26335
|
+
const unresolved = opts.unresolvedFindings ?? [];
|
|
26336
|
+
if (unresolved.length > 0) {
|
|
26337
|
+
lines.push("", "unresolved verify findings:");
|
|
26338
|
+
for (const u of unresolved) {
|
|
26339
|
+
const why = u.reason === "fail" ? "rejected, rework budget spent" : "no parseable verdict";
|
|
26340
|
+
const detail = u.findings.trim().split("\n")[0]?.trim() ?? "";
|
|
26341
|
+
const body = detail.length > maxChars ? `${detail.slice(0, maxChars)}\u2026` : detail;
|
|
26342
|
+
lines.push(` ${u.nodeId} (${why})${body ? ` \u2014 ${body}` : ""}`);
|
|
26343
|
+
}
|
|
26344
|
+
}
|
|
26345
|
+
return lines.join("\n");
|
|
26346
|
+
}
|
|
25150
26347
|
function state() {
|
|
25151
26348
|
return globalThis;
|
|
25152
26349
|
}
|
|
@@ -25195,7 +26392,7 @@ function formatKrakenGraphSummary() {
|
|
|
25195
26392
|
if (live.error) parts.push(`${live.error}\u2717`);
|
|
25196
26393
|
return `graph ${parts.join(" \xB7 ")}`;
|
|
25197
26394
|
}
|
|
25198
|
-
var STATUS_ICON;
|
|
26395
|
+
var STATUS_ICON, DEFAULT_DIGEST_RESULT_CHARS;
|
|
25199
26396
|
var init_graphStatus = __esm({
|
|
25200
26397
|
"src/cli/kraken/graphStatus.ts"() {
|
|
25201
26398
|
"use strict";
|
|
@@ -25207,6 +26404,7 @@ var init_graphStatus = __esm({
|
|
|
25207
26404
|
error: "\u2717",
|
|
25208
26405
|
skipped: "\xBB"
|
|
25209
26406
|
};
|
|
26407
|
+
DEFAULT_DIGEST_RESULT_CHARS = 200;
|
|
25210
26408
|
}
|
|
25211
26409
|
});
|
|
25212
26410
|
|
|
@@ -25700,9 +26898,9 @@ function spillToolOutput(fullText, meta3) {
|
|
|
25700
26898
|
const rnd = randomBytes(3).toString("hex");
|
|
25701
26899
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
25702
26900
|
const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
|
|
25703
|
-
const
|
|
25704
|
-
writeFileSync10(
|
|
25705
|
-
return
|
|
26901
|
+
const path47 = join11(dir, file2);
|
|
26902
|
+
writeFileSync10(path47, fullText, "utf8");
|
|
26903
|
+
return path47;
|
|
25706
26904
|
} catch {
|
|
25707
26905
|
return null;
|
|
25708
26906
|
}
|
|
@@ -25718,21 +26916,21 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
|
25718
26916
|
if (text.length === 0)
|
|
25719
26917
|
return text;
|
|
25720
26918
|
const opts = typeof capOrOpts === "number" ? { cap: capOrOpts } : capOrOpts ?? {};
|
|
25721
|
-
const
|
|
26919
|
+
const cap3 = typeof opts.cap === "number" && Number.isFinite(opts.cap) && opts.cap >= 10 ? opts.cap : TOOL_RESULT_LINE_CAP;
|
|
25722
26920
|
const doSpill = opts.spill !== false;
|
|
25723
26921
|
const lines = text.split("\n");
|
|
25724
|
-
const charBudget =
|
|
25725
|
-
const overLines = lines.length >
|
|
25726
|
-
const overChars = text.length > charBudget && lines.length <=
|
|
26922
|
+
const charBudget = cap3 * 80;
|
|
26923
|
+
const overLines = lines.length > cap3;
|
|
26924
|
+
const overChars = text.length > charBudget && lines.length <= cap3;
|
|
25727
26925
|
if (!overLines && !overChars)
|
|
25728
26926
|
return text;
|
|
25729
26927
|
let preview;
|
|
25730
26928
|
let marker;
|
|
25731
26929
|
if (overLines) {
|
|
25732
|
-
const half = Math.floor(
|
|
26930
|
+
const half = Math.floor(cap3 / 2);
|
|
25733
26931
|
const head = lines.slice(0, half);
|
|
25734
26932
|
const tail = lines.slice(lines.length - half);
|
|
25735
|
-
const omitted = lines.length -
|
|
26933
|
+
const omitted = lines.length - cap3;
|
|
25736
26934
|
marker = `+${omitted} lines omitted \u2014 showing head:${half}, tail:${half} of ${lines.length} total`;
|
|
25737
26935
|
preview = head.join("\n") + `
|
|
25738
26936
|
\u2026 [${marker}] \u2026
|
|
@@ -25748,10 +26946,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
|
25748
26946
|
${tail}`;
|
|
25749
26947
|
}
|
|
25750
26948
|
if (doSpill) {
|
|
25751
|
-
const
|
|
25752
|
-
if (
|
|
26949
|
+
const path47 = spillToolOutput(text, { toolName: opts.toolName });
|
|
26950
|
+
if (path47) {
|
|
25753
26951
|
const spillNote = `
|
|
25754
|
-
\u2026 [full output spilled to: ${
|
|
26952
|
+
\u2026 [full output spilled to: ${path47} \u2014 re-read with read_file if you need the complete text] \u2026`;
|
|
25755
26953
|
if (preview.includes("] \u2026\n")) {
|
|
25756
26954
|
preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
|
|
25757
26955
|
`);
|
|
@@ -25763,7 +26961,7 @@ ${tail}`;
|
|
|
25763
26961
|
return preview;
|
|
25764
26962
|
}
|
|
25765
26963
|
var TOOL_NAME_ALIASES, TOOL_RESULT_LINE_CAP, ToolRegistry;
|
|
25766
|
-
var
|
|
26964
|
+
var init_registry2 = __esm({
|
|
25767
26965
|
"packages/core/dist/core/tools/registry.js"() {
|
|
25768
26966
|
"use strict";
|
|
25769
26967
|
init_zodBridge();
|
|
@@ -26621,7 +27819,7 @@ async function runTentacle(opts) {
|
|
|
26621
27819
|
const started = Date.now();
|
|
26622
27820
|
const g = globalThis;
|
|
26623
27821
|
let worktree = null;
|
|
26624
|
-
let effectiveCwd = parentCwd;
|
|
27822
|
+
let effectiveCwd = opts.cwdOverride || parentCwd;
|
|
26625
27823
|
const wantWt = agent === "general" && deps.allowWorktree !== false && isKrakenWorktreeEnabled();
|
|
26626
27824
|
if (wantWt) {
|
|
26627
27825
|
try {
|
|
@@ -26697,7 +27895,7 @@ async function runTentacle(opts) {
|
|
|
26697
27895
|
model: sub.model,
|
|
26698
27896
|
provider: sub.provider,
|
|
26699
27897
|
messages: [
|
|
26700
|
-
{ role: "system", content: systemPromptForAgent(agent) },
|
|
27898
|
+
{ role: "system", content: opts.systemPromptOverride ?? systemPromptForAgent(agent) },
|
|
26701
27899
|
{ role: "user", content: userContent }
|
|
26702
27900
|
],
|
|
26703
27901
|
tools: sub.tools,
|
|
@@ -28692,21 +29890,21 @@ function normalizeAuth(auth) {
|
|
|
28692
29890
|
return "agent";
|
|
28693
29891
|
}
|
|
28694
29892
|
function readSecrets() {
|
|
28695
|
-
const
|
|
28696
|
-
if (!existsSync17(
|
|
29893
|
+
const path47 = getSshSecretsPath();
|
|
29894
|
+
if (!existsSync17(path47)) return {};
|
|
28697
29895
|
try {
|
|
28698
|
-
return JSON.parse(readFileSync16(
|
|
29896
|
+
return JSON.parse(readFileSync16(path47, "utf8"));
|
|
28699
29897
|
} catch {
|
|
28700
29898
|
return {};
|
|
28701
29899
|
}
|
|
28702
29900
|
}
|
|
28703
29901
|
function writeSecrets(data) {
|
|
28704
|
-
const
|
|
28705
|
-
mkdirSync9(dirname2(
|
|
28706
|
-
writeFileSync11(
|
|
29902
|
+
const path47 = getSshSecretsPath();
|
|
29903
|
+
mkdirSync9(dirname2(path47), { recursive: true });
|
|
29904
|
+
writeFileSync11(path47, `${JSON.stringify(data, null, 2)}
|
|
28707
29905
|
`, "utf8");
|
|
28708
29906
|
try {
|
|
28709
|
-
chmodSync(
|
|
29907
|
+
chmodSync(path47, 384);
|
|
28710
29908
|
} catch {
|
|
28711
29909
|
}
|
|
28712
29910
|
}
|
|
@@ -28735,10 +29933,10 @@ function deleteSshPassword(id) {
|
|
|
28735
29933
|
writeSecrets({ passwords });
|
|
28736
29934
|
}
|
|
28737
29935
|
function readStore2() {
|
|
28738
|
-
const
|
|
28739
|
-
if (!existsSync17(
|
|
29936
|
+
const path47 = getSshTargetsPath();
|
|
29937
|
+
if (!existsSync17(path47)) return [];
|
|
28740
29938
|
try {
|
|
28741
|
-
const parsed = JSON.parse(readFileSync16(
|
|
29939
|
+
const parsed = JSON.parse(readFileSync16(path47, "utf8"));
|
|
28742
29940
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
28743
29941
|
return list.filter(
|
|
28744
29942
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -28753,11 +29951,11 @@ function readStore2() {
|
|
|
28753
29951
|
}
|
|
28754
29952
|
}
|
|
28755
29953
|
function writeStore2(targets) {
|
|
28756
|
-
const
|
|
28757
|
-
mkdirSync9(dirname2(
|
|
29954
|
+
const path47 = getSshTargetsPath();
|
|
29955
|
+
mkdirSync9(dirname2(path47), { recursive: true });
|
|
28758
29956
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
28759
29957
|
writeFileSync11(
|
|
28760
|
-
|
|
29958
|
+
path47,
|
|
28761
29959
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
28762
29960
|
`,
|
|
28763
29961
|
"utf8"
|
|
@@ -28916,12 +30114,12 @@ function runSsh(target, remoteCommand, timeoutMs = 6e4) {
|
|
|
28916
30114
|
});
|
|
28917
30115
|
let stdout = "";
|
|
28918
30116
|
let stderr = "";
|
|
28919
|
-
const
|
|
30117
|
+
const cap3 = 4e4;
|
|
28920
30118
|
child.stdout?.on("data", (d) => {
|
|
28921
|
-
if (stdout.length <
|
|
30119
|
+
if (stdout.length < cap3) stdout += d.toString("utf8");
|
|
28922
30120
|
});
|
|
28923
30121
|
child.stderr?.on("data", (d) => {
|
|
28924
|
-
if (stderr.length <
|
|
30122
|
+
if (stderr.length < cap3) stderr += d.toString("utf8");
|
|
28925
30123
|
});
|
|
28926
30124
|
const timer = setTimeout(() => {
|
|
28927
30125
|
child.kill("SIGTERM");
|
|
@@ -29003,11 +30201,11 @@ function formatSshTargetsForPrompt() {
|
|
|
29003
30201
|
];
|
|
29004
30202
|
for (const t of targets) {
|
|
29005
30203
|
const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
|
|
29006
|
-
const
|
|
30204
|
+
const path47 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
|
|
29007
30205
|
const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
|
|
29008
30206
|
const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
|
|
29009
30207
|
lines.push(
|
|
29010
|
-
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${
|
|
30208
|
+
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path47}${tags}${allow}`
|
|
29011
30209
|
);
|
|
29012
30210
|
}
|
|
29013
30211
|
return lines.join("\n");
|
|
@@ -29518,7 +30716,8 @@ __export(krakenModel_exports, {
|
|
|
29518
30716
|
isKrakenAutoModelEnabled: () => isKrakenAutoModelEnabled,
|
|
29519
30717
|
pickCheapModel: () => pickCheapModel,
|
|
29520
30718
|
resolveKrakenSubModel: () => resolveKrakenSubModel,
|
|
29521
|
-
resolveKrakenSubModelAsync: () => resolveKrakenSubModelAsync
|
|
30719
|
+
resolveKrakenSubModelAsync: () => resolveKrakenSubModelAsync,
|
|
30720
|
+
resolvePersonaModel: () => resolvePersonaModel
|
|
29522
30721
|
});
|
|
29523
30722
|
function isCheapModelId(id) {
|
|
29524
30723
|
if (!id) return false;
|
|
@@ -29568,6 +30767,12 @@ function resolveKrakenSubModel(agent, parentModel, env = process.env, opts = {})
|
|
|
29568
30767
|
}
|
|
29569
30768
|
return parentModel;
|
|
29570
30769
|
}
|
|
30770
|
+
function resolvePersonaModel(kind, parentModel, env = process.env, opts = {}) {
|
|
30771
|
+
const personaKey = kind === "spec" ? "ZELARI_KRAKEN_SPEC_MODEL" : kind === "conformance" ? "ZELARI_KRAKEN_CONFORMANCE_MODEL" : kind === "oracle" ? "ZELARI_KRAKEN_ORACLE_MODEL" : "";
|
|
30772
|
+
const specific = personaKey ? env[personaKey]?.trim() : void 0;
|
|
30773
|
+
if (specific) return specific;
|
|
30774
|
+
return resolveKrakenSubModel("verify", parentModel, env, opts);
|
|
30775
|
+
}
|
|
29571
30776
|
async function resolveKrakenSubModelAsync(agent, parentModel, env = process.env, opts = {}) {
|
|
29572
30777
|
let candidates = [];
|
|
29573
30778
|
if (opts.provider) {
|
|
@@ -29617,7 +30822,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
29617
30822
|
const safeBash = wrapWithShellSafety(bashTool, audit, sessionId);
|
|
29618
30823
|
const safeFetchUrl = wrapWithAudit(fetchUrlTool, audit, sessionId);
|
|
29619
30824
|
const safeWebSearch = wrapWithAudit(webSearchTool, audit, sessionId);
|
|
29620
|
-
const
|
|
30825
|
+
const registry4 = new ToolRegistry();
|
|
29621
30826
|
const profile = options.profile ?? "full";
|
|
29622
30827
|
const readOnly = options.readOnly === true || options.planMode === true || profile === "explore";
|
|
29623
30828
|
const verifyMode = profile === "verify";
|
|
@@ -29625,34 +30830,34 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
29625
30830
|
const allowBash = allowMutators || verifyMode;
|
|
29626
30831
|
const permPolicy = options.permissionPolicy ?? defaultPermissionPolicy();
|
|
29627
30832
|
const withPerm = (t) => wrapWithPermissions(t, permPolicy, options.onPermissionAsk);
|
|
29628
|
-
|
|
29629
|
-
|
|
29630
|
-
|
|
29631
|
-
|
|
29632
|
-
|
|
29633
|
-
|
|
30833
|
+
registry4.register(withPerm(safeReadFile));
|
|
30834
|
+
registry4.register(withPerm(safeGrepContent));
|
|
30835
|
+
registry4.register(withPerm(safeListFiles));
|
|
30836
|
+
registry4.register(withPerm(safeShowDiff));
|
|
30837
|
+
registry4.register(withPerm(safeFetchUrl));
|
|
30838
|
+
registry4.register(withPerm(safeWebSearch));
|
|
29634
30839
|
if (allowMutators) {
|
|
29635
|
-
|
|
29636
|
-
|
|
29637
|
-
|
|
30840
|
+
registry4.register(withPerm(safeWriteFile));
|
|
30841
|
+
registry4.register(withPerm(safeEditFile));
|
|
30842
|
+
registry4.register(withPerm(safeApplyDiff));
|
|
29638
30843
|
}
|
|
29639
30844
|
if (allowBash) {
|
|
29640
|
-
|
|
30845
|
+
registry4.register(withPerm(safeBash));
|
|
29641
30846
|
}
|
|
29642
30847
|
const askUserTool = options.readOnly === true || profile === "explore" || profile === "verify" ? null : createAskUserTool(options.onAskUser);
|
|
29643
30848
|
if (askUserTool) {
|
|
29644
|
-
|
|
30849
|
+
registry4.register(withPerm(askUserTool));
|
|
29645
30850
|
}
|
|
29646
30851
|
const enableSkill = options.enableSkill !== false && options.readOnly !== true && profile !== "explore" && profile !== "verify";
|
|
29647
30852
|
const skillTool = enableSkill ? withPerm(createSkillTool({ cwd: root })) : null;
|
|
29648
30853
|
if (skillTool) {
|
|
29649
|
-
|
|
30854
|
+
registry4.register(skillTool);
|
|
29650
30855
|
}
|
|
29651
30856
|
const enableTodos = options.enableTodos !== false && options.readOnly !== true && profile === "full";
|
|
29652
30857
|
const todoWrite = enableTodos ? withPerm(createTodoWriteTool()) : null;
|
|
29653
30858
|
const todoRead = enableTodos ? withPerm(createTodoReadTool()) : null;
|
|
29654
|
-
if (todoWrite)
|
|
29655
|
-
if (todoRead)
|
|
30859
|
+
if (todoWrite) registry4.register(todoWrite);
|
|
30860
|
+
if (todoRead) registry4.register(todoRead);
|
|
29656
30861
|
const summary = [
|
|
29657
30862
|
safeReadFile,
|
|
29658
30863
|
safeGrepContent,
|
|
@@ -29674,13 +30879,13 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
29674
30879
|
}));
|
|
29675
30880
|
if (process.env.ZELARI_AST !== "0") {
|
|
29676
30881
|
for (const t of createAstTools()) {
|
|
29677
|
-
|
|
30882
|
+
registry4.register(t);
|
|
29678
30883
|
tools.push({ name: t.name, description: t.description, permissions: t.permissions ?? [] });
|
|
29679
30884
|
}
|
|
29680
30885
|
}
|
|
29681
30886
|
if (process.env.ZELARI_SEMANTIC !== "0") {
|
|
29682
30887
|
const semanticTool = createSemanticTool({ root });
|
|
29683
|
-
|
|
30888
|
+
registry4.register(semanticTool);
|
|
29684
30889
|
tools.push({
|
|
29685
30890
|
name: semanticTool.name,
|
|
29686
30891
|
description: semanticTool.description,
|
|
@@ -29689,7 +30894,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
29689
30894
|
}
|
|
29690
30895
|
if (!readOnly && process.env.ZELARI_BROWSER !== "0") {
|
|
29691
30896
|
const browserTool = createBrowserTool();
|
|
29692
|
-
|
|
30897
|
+
registry4.register(browserTool);
|
|
29693
30898
|
tools.push({
|
|
29694
30899
|
name: browserTool.name,
|
|
29695
30900
|
description: browserTool.description,
|
|
@@ -29698,7 +30903,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
29698
30903
|
}
|
|
29699
30904
|
if (!readOnly && process.env.ZELARI_SSH !== "0") {
|
|
29700
30905
|
for (const t of createSshTools()) {
|
|
29701
|
-
|
|
30906
|
+
registry4.register(t);
|
|
29702
30907
|
tools.push({
|
|
29703
30908
|
name: t.name,
|
|
29704
30909
|
description: t.description,
|
|
@@ -29709,7 +30914,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
29709
30914
|
if (!readOnly) {
|
|
29710
30915
|
for (const t of createWorldModelTools()) {
|
|
29711
30916
|
const safe = wrapWithAudit(t, audit, sessionId);
|
|
29712
|
-
|
|
30917
|
+
registry4.register(safe);
|
|
29713
30918
|
tools.push({
|
|
29714
30919
|
name: t.name,
|
|
29715
30920
|
description: t.description,
|
|
@@ -29722,7 +30927,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
29722
30927
|
const taskTool = createTaskTool({
|
|
29723
30928
|
createSubAgentContext: createKrakenSubAgentContextFactory({ root, audit, sessionId })
|
|
29724
30929
|
});
|
|
29725
|
-
|
|
30930
|
+
registry4.register(withPerm(taskTool));
|
|
29726
30931
|
tools.push({
|
|
29727
30932
|
name: taskTool.name,
|
|
29728
30933
|
description: taskTool.description,
|
|
@@ -29732,17 +30937,17 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
29732
30937
|
if (!readOnly && process.env.ZELARI_LSP !== "0" && options.lspProvider !== null) {
|
|
29733
30938
|
const lspTools = options.lspProvider ? createLspTools(options.lspProvider, root) : createLspTools(getSharedLspManager(root), root);
|
|
29734
30939
|
for (const t of lspTools) {
|
|
29735
|
-
|
|
30940
|
+
registry4.register(t);
|
|
29736
30941
|
tools.push({ name: t.name, description: t.description, permissions: t.permissions ?? [] });
|
|
29737
30942
|
}
|
|
29738
30943
|
}
|
|
29739
|
-
return { registry:
|
|
30944
|
+
return { registry: registry4, tools };
|
|
29740
30945
|
}
|
|
29741
|
-
function getCliToolCatalogEntries(
|
|
30946
|
+
function getCliToolCatalogEntries(registry4) {
|
|
29742
30947
|
const entries = [];
|
|
29743
|
-
for (const name of
|
|
30948
|
+
for (const name of registry4.list()) {
|
|
29744
30949
|
if (HARNESS_BUILTIN_NAMES.has(name)) continue;
|
|
29745
|
-
const def =
|
|
30950
|
+
const def = registry4.get(name);
|
|
29746
30951
|
if (!def) continue;
|
|
29747
30952
|
try {
|
|
29748
30953
|
entries.push(cliToolToEnhanced(def));
|
|
@@ -29751,8 +30956,8 @@ function getCliToolCatalogEntries(registry3) {
|
|
|
29751
30956
|
}
|
|
29752
30957
|
return entries;
|
|
29753
30958
|
}
|
|
29754
|
-
function registerCliToolsIntoCouncilCatalog(
|
|
29755
|
-
for (const entry of getCliToolCatalogEntries(
|
|
30959
|
+
function registerCliToolsIntoCouncilCatalog(registry4) {
|
|
30960
|
+
for (const entry of getCliToolCatalogEntries(registry4)) {
|
|
29756
30961
|
try {
|
|
29757
30962
|
registerCustomTool(entry);
|
|
29758
30963
|
} catch {
|
|
@@ -29999,7 +31204,7 @@ var HARNESS_BUILTIN_NAMES;
|
|
|
29999
31204
|
var init_toolRegistry = __esm({
|
|
30000
31205
|
"src/cli/toolRegistry.ts"() {
|
|
30001
31206
|
"use strict";
|
|
30002
|
-
|
|
31207
|
+
init_registry2();
|
|
30003
31208
|
init_filesystem();
|
|
30004
31209
|
init_shell();
|
|
30005
31210
|
init_search();
|
|
@@ -31352,7 +32557,7 @@ __export(composeContext_exports, {
|
|
|
31352
32557
|
});
|
|
31353
32558
|
import { existsSync as existsSync23, readdirSync as readdirSync4, readFileSync as readFileSync19 } from "node:fs";
|
|
31354
32559
|
import { join as join20 } from "node:path";
|
|
31355
|
-
function
|
|
32560
|
+
function cap2(text, max, label) {
|
|
31356
32561
|
if (!text || text.length <= max) return { text: text || "", truncated: false };
|
|
31357
32562
|
return {
|
|
31358
32563
|
text: text.slice(0, max) + `
|
|
@@ -31396,7 +32601,7 @@ function buildDesignIndex(projectRoot, maxChars) {
|
|
|
31396
32601
|
}
|
|
31397
32602
|
}
|
|
31398
32603
|
const raw = lines.join("\n");
|
|
31399
|
-
return
|
|
32604
|
+
return cap2(raw, maxChars, "design-index").text;
|
|
31400
32605
|
}
|
|
31401
32606
|
function composeProjectContext(input) {
|
|
31402
32607
|
const cwd = input.cwd ?? process.cwd();
|
|
@@ -31450,7 +32655,7 @@ function composeProjectContext(input) {
|
|
|
31450
32655
|
default: 12e3,
|
|
31451
32656
|
min: 2e3
|
|
31452
32657
|
});
|
|
31453
|
-
const totalCapped =
|
|
32658
|
+
const totalCapped = cap2(workspaceContext, totalCap, "workspaceContext");
|
|
31454
32659
|
workspaceContext = totalCapped.text;
|
|
31455
32660
|
if (totalCapped.truncated) {
|
|
31456
32661
|
warnings.push(
|
|
@@ -31468,14 +32673,14 @@ function composeProjectContext(input) {
|
|
|
31468
32673
|
}
|
|
31469
32674
|
const ragParts = [];
|
|
31470
32675
|
if (durableRaw) {
|
|
31471
|
-
const d =
|
|
32676
|
+
const d = cap2(durableRaw, durableMax, "durable-state");
|
|
31472
32677
|
ragParts.push(d.text);
|
|
31473
32678
|
if (d.truncated) {
|
|
31474
32679
|
warnings.push(`[context] durable state truncated to ${durableMax} chars.`);
|
|
31475
32680
|
}
|
|
31476
32681
|
}
|
|
31477
32682
|
if (input.memoryHits?.trim()) {
|
|
31478
|
-
const m =
|
|
32683
|
+
const m = cap2(input.memoryHits.trim(), memoryMax, "memory");
|
|
31479
32684
|
ragParts.push(m.text);
|
|
31480
32685
|
if (m.truncated) warnings.push(`[context] memory RAG truncated to ${memoryMax} chars.`);
|
|
31481
32686
|
}
|
|
@@ -31859,28 +33064,28 @@ var init_storage = __esm({
|
|
|
31859
33064
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
31860
33065
|
Storage = class {
|
|
31861
33066
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
31862
|
-
read(
|
|
31863
|
-
if (!existsSync25(
|
|
31864
|
-
throw new Error(`File not found: ${
|
|
33067
|
+
read(path47) {
|
|
33068
|
+
if (!existsSync25(path47)) {
|
|
33069
|
+
throw new Error(`File not found: ${path47}`);
|
|
31865
33070
|
}
|
|
31866
|
-
const md = readFileSync21(
|
|
33071
|
+
const md = readFileSync21(path47, "utf8");
|
|
31867
33072
|
return parseFrontmatter(md);
|
|
31868
33073
|
}
|
|
31869
33074
|
/** Read a Markdown file; returns null if not found. */
|
|
31870
|
-
readIfExists(
|
|
31871
|
-
if (!existsSync25(
|
|
31872
|
-
return this.read(
|
|
33075
|
+
readIfExists(path47) {
|
|
33076
|
+
if (!existsSync25(path47)) return null;
|
|
33077
|
+
return this.read(path47);
|
|
31873
33078
|
}
|
|
31874
33079
|
/**
|
|
31875
33080
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
31876
33081
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
31877
33082
|
*/
|
|
31878
|
-
write(
|
|
31879
|
-
mkdirSync11(dirname4(
|
|
31880
|
-
const tmp =
|
|
33083
|
+
write(path47, meta3, body) {
|
|
33084
|
+
mkdirSync11(dirname4(path47), { recursive: true });
|
|
33085
|
+
const tmp = path47 + ".tmp-" + process.pid;
|
|
31881
33086
|
const md = serializeFrontmatter(meta3, body);
|
|
31882
33087
|
writeFileSync13(tmp, md, "utf8");
|
|
31883
|
-
renameSync2(tmp,
|
|
33088
|
+
renameSync2(tmp, path47);
|
|
31884
33089
|
}
|
|
31885
33090
|
/** List all .md files in a directory (non-recursive). */
|
|
31886
33091
|
listMarkdown(dir) {
|
|
@@ -31958,8 +33163,8 @@ function readPlan(ctx) {
|
|
|
31958
33163
|
} catch {
|
|
31959
33164
|
}
|
|
31960
33165
|
}
|
|
31961
|
-
const
|
|
31962
|
-
const doc = ctx.storage.readIfExists(
|
|
33166
|
+
const path47 = workspaceFile(ctx.rootDir, "plan");
|
|
33167
|
+
const doc = ctx.storage.readIfExists(path47);
|
|
31963
33168
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
31964
33169
|
const meta3 = doc.meta;
|
|
31965
33170
|
return {
|
|
@@ -32124,7 +33329,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
32124
33329
|
dueDate: input.dueDate,
|
|
32125
33330
|
targetVersion: version2
|
|
32126
33331
|
});
|
|
32127
|
-
const
|
|
33332
|
+
const path47 = join23(ctx.rootDir, "milestones", `${id}.md`);
|
|
32128
33333
|
const meta3 = {
|
|
32129
33334
|
kind: "milestone",
|
|
32130
33335
|
id,
|
|
@@ -32141,7 +33346,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
32141
33346
|
`Target version: ${version2}`,
|
|
32142
33347
|
""
|
|
32143
33348
|
].join("\n");
|
|
32144
|
-
ctx.storage.write(
|
|
33349
|
+
ctx.storage.write(path47, meta3, body);
|
|
32145
33350
|
return { id, created: true };
|
|
32146
33351
|
}
|
|
32147
33352
|
function readPlanSummary(ctx) {
|
|
@@ -32345,7 +33550,7 @@ function addIdeaStub(ctx) {
|
|
|
32345
33550
|
const tags = args["tags"] ?? [];
|
|
32346
33551
|
const category = args["category"] ?? "General";
|
|
32347
33552
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
32348
|
-
const
|
|
33553
|
+
const path47 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
32349
33554
|
const meta3 = {
|
|
32350
33555
|
kind: "adr",
|
|
32351
33556
|
status: "proposed",
|
|
@@ -32371,7 +33576,7 @@ function addIdeaStub(ctx) {
|
|
|
32371
33576
|
...consequences.map((c) => `- ${c}`),
|
|
32372
33577
|
""
|
|
32373
33578
|
].join("\n");
|
|
32374
|
-
ctx.storage.write(
|
|
33579
|
+
ctx.storage.write(path47, meta3, body);
|
|
32375
33580
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
32376
33581
|
});
|
|
32377
33582
|
}
|
|
@@ -32453,14 +33658,14 @@ function createDocumentStub(ctx) {
|
|
|
32453
33658
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
32454
33659
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
32455
33660
|
}
|
|
32456
|
-
const
|
|
33661
|
+
const path47 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
32457
33662
|
const meta3 = {
|
|
32458
33663
|
kind: "doc",
|
|
32459
33664
|
id: slug,
|
|
32460
33665
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
32461
33666
|
tags
|
|
32462
33667
|
};
|
|
32463
|
-
ctx.storage.write(
|
|
33668
|
+
ctx.storage.write(path47, meta3, content);
|
|
32464
33669
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
32465
33670
|
});
|
|
32466
33671
|
}
|
|
@@ -32621,12 +33826,12 @@ __export(toolRegistry_exports2, {
|
|
|
32621
33826
|
});
|
|
32622
33827
|
function createWorkspaceToolRegistry(ctx) {
|
|
32623
33828
|
const stubs = createWorkspaceStubs(ctx);
|
|
32624
|
-
const
|
|
33829
|
+
const registry4 = new ToolRegistry();
|
|
32625
33830
|
for (const stub of stubs) {
|
|
32626
33831
|
const td = adaptStubToToolDefinition(stub, ctx);
|
|
32627
|
-
|
|
33832
|
+
registry4.register(td);
|
|
32628
33833
|
}
|
|
32629
|
-
return
|
|
33834
|
+
return registry4;
|
|
32630
33835
|
}
|
|
32631
33836
|
function adaptStubToToolDefinition(stub, workspaceCtx) {
|
|
32632
33837
|
return {
|
|
@@ -32658,7 +33863,7 @@ var init_toolRegistry2 = __esm({
|
|
|
32658
33863
|
"src/cli/workspace/toolRegistry.ts"() {
|
|
32659
33864
|
"use strict";
|
|
32660
33865
|
init_zod();
|
|
32661
|
-
|
|
33866
|
+
init_registry2();
|
|
32662
33867
|
init_toolTypes();
|
|
32663
33868
|
init_stubs();
|
|
32664
33869
|
}
|
|
@@ -33004,10 +34209,10 @@ function getUserMcpPath() {
|
|
|
33004
34209
|
function getProjectMcpPath(projectRoot) {
|
|
33005
34210
|
return join24(projectRoot, ".zelari", "mcp.json");
|
|
33006
34211
|
}
|
|
33007
|
-
function readFile2(
|
|
33008
|
-
if (!existsSync28(
|
|
34212
|
+
function readFile2(path47) {
|
|
34213
|
+
if (!existsSync28(path47)) return {};
|
|
33009
34214
|
try {
|
|
33010
|
-
const parsed = JSON.parse(readFileSync23(
|
|
34215
|
+
const parsed = JSON.parse(readFileSync23(path47, "utf8"));
|
|
33011
34216
|
const out = {};
|
|
33012
34217
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
33013
34218
|
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
@@ -33023,10 +34228,10 @@ function readFile2(path44) {
|
|
|
33023
34228
|
return {};
|
|
33024
34229
|
}
|
|
33025
34230
|
}
|
|
33026
|
-
function writeFile(
|
|
33027
|
-
mkdirSync13(dirname6(
|
|
34231
|
+
function writeFile(path47, servers) {
|
|
34232
|
+
mkdirSync13(dirname6(path47), { recursive: true });
|
|
33028
34233
|
const body = { mcpServers: servers };
|
|
33029
|
-
writeFileSync15(
|
|
34234
|
+
writeFileSync15(path47, `${JSON.stringify(body, null, 2)}
|
|
33030
34235
|
`, "utf8");
|
|
33031
34236
|
}
|
|
33032
34237
|
function listMcpServers(projectRoot) {
|
|
@@ -33059,9 +34264,9 @@ function upsertMcpServer(opts) {
|
|
|
33059
34264
|
if (!opts.config.command?.trim()) {
|
|
33060
34265
|
return { ok: false, error: "command is required" };
|
|
33061
34266
|
}
|
|
33062
|
-
let
|
|
34267
|
+
let path47;
|
|
33063
34268
|
if (opts.scope === "user") {
|
|
33064
|
-
|
|
34269
|
+
path47 = getUserMcpPath();
|
|
33065
34270
|
} else {
|
|
33066
34271
|
const root = opts.projectRoot?.trim();
|
|
33067
34272
|
if (!root) {
|
|
@@ -33070,30 +34275,30 @@ function upsertMcpServer(opts) {
|
|
|
33070
34275
|
error: "projectRoot required for project scope (Open Folder first)"
|
|
33071
34276
|
};
|
|
33072
34277
|
}
|
|
33073
|
-
|
|
34278
|
+
path47 = getProjectMcpPath(root);
|
|
33074
34279
|
}
|
|
33075
|
-
const current = readFile2(
|
|
34280
|
+
const current = readFile2(path47);
|
|
33076
34281
|
current[name] = {
|
|
33077
34282
|
command: opts.config.command.trim(),
|
|
33078
34283
|
args: opts.config.args,
|
|
33079
34284
|
env: opts.config.env,
|
|
33080
34285
|
enabled: opts.config.enabled !== false
|
|
33081
34286
|
};
|
|
33082
|
-
writeFile(
|
|
33083
|
-
return { ok: true, path:
|
|
34287
|
+
writeFile(path47, current);
|
|
34288
|
+
return { ok: true, path: path47 };
|
|
33084
34289
|
}
|
|
33085
34290
|
function removeMcpServer(opts) {
|
|
33086
|
-
const
|
|
33087
|
-
if (!
|
|
34291
|
+
const path47 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
|
|
34292
|
+
if (!path47) {
|
|
33088
34293
|
return { ok: false, error: "projectRoot required for project scope" };
|
|
33089
34294
|
}
|
|
33090
|
-
const current = readFile2(
|
|
34295
|
+
const current = readFile2(path47);
|
|
33091
34296
|
if (!(opts.name in current)) {
|
|
33092
|
-
return { ok: false, error: `Server "${opts.name}" not found in ${
|
|
34297
|
+
return { ok: false, error: `Server "${opts.name}" not found in ${path47}` };
|
|
33093
34298
|
}
|
|
33094
34299
|
delete current[opts.name];
|
|
33095
|
-
writeFile(
|
|
33096
|
-
return { ok: true, path:
|
|
34300
|
+
writeFile(path47, current);
|
|
34301
|
+
return { ok: true, path: path47 };
|
|
33097
34302
|
}
|
|
33098
34303
|
var init_mcpConfigIo = __esm({
|
|
33099
34304
|
"src/cli/mcp/mcpConfigIo.ts"() {
|
|
@@ -33237,7 +34442,7 @@ async function ensureLoaded(projectRoot) {
|
|
|
33237
34442
|
function sanitizeToolName(raw) {
|
|
33238
34443
|
return raw.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
|
|
33239
34444
|
}
|
|
33240
|
-
async function registerMcpTools(
|
|
34445
|
+
async function registerMcpTools(registry4, projectRoot = process.cwd(), opts) {
|
|
33241
34446
|
if (process.env["ZELARI_MCP"] === "0") return { registered: [], warnings: [] };
|
|
33242
34447
|
await ensureLoaded(projectRoot);
|
|
33243
34448
|
const skipCuaForCouncil = opts?.councilMode === true && !isCuaAllowedForCouncil();
|
|
@@ -33246,7 +34451,7 @@ async function registerMcpTools(registry3, projectRoot = process.cwd(), opts) {
|
|
|
33246
34451
|
if (skipCuaForCouncil && isCuaMcpServerName(t.serverName)) {
|
|
33247
34452
|
continue;
|
|
33248
34453
|
}
|
|
33249
|
-
|
|
34454
|
+
registry4.register({
|
|
33250
34455
|
name: t.registryName,
|
|
33251
34456
|
description: `[MCP:${t.serverName}] ${t.info.description}`.slice(0, 1024),
|
|
33252
34457
|
// The MCP server owns validation; its JSON Schema is forwarded to the
|
|
@@ -33430,10 +34635,10 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
33430
34635
|
import { join as join26 } from "node:path";
|
|
33431
34636
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
33432
34637
|
async function readPackageJson2(projectRoot) {
|
|
33433
|
-
const
|
|
33434
|
-
if (!existsSync30(
|
|
34638
|
+
const path47 = join26(projectRoot, "package.json");
|
|
34639
|
+
if (!existsSync30(path47)) return null;
|
|
33435
34640
|
try {
|
|
33436
|
-
return JSON.parse(await readFile3(
|
|
34641
|
+
return JSON.parse(await readFile3(path47, "utf8"));
|
|
33437
34642
|
} catch {
|
|
33438
34643
|
return null;
|
|
33439
34644
|
}
|
|
@@ -33515,9 +34720,9 @@ async function genBuild(ctx) {
|
|
|
33515
34720
|
].join("\n");
|
|
33516
34721
|
}
|
|
33517
34722
|
async function genOpenQuestions(ctx) {
|
|
33518
|
-
const
|
|
33519
|
-
if (!existsSync30(
|
|
33520
|
-
const content = readFileSync25(
|
|
34723
|
+
const path47 = join26(ctx.rootDir, "risks.md");
|
|
34724
|
+
if (!existsSync30(path47)) return "_No open questions._";
|
|
34725
|
+
const content = readFileSync25(path47, "utf8");
|
|
33521
34726
|
const lines = content.split("\n");
|
|
33522
34727
|
const questions = [];
|
|
33523
34728
|
let currentTitle = "";
|
|
@@ -34048,9 +35253,9 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
34048
35253
|
lessons = { ran: true, captured: 0, rejected: 0 };
|
|
34049
35254
|
for (const r of verification.report.results) {
|
|
34050
35255
|
if (r.ok) continue;
|
|
34051
|
-
const
|
|
34052
|
-
if (
|
|
34053
|
-
else if (
|
|
35256
|
+
const cap3 = captureFailure(ctx.rootDir, r);
|
|
35257
|
+
if (cap3.rejected) lessons.rejected++;
|
|
35258
|
+
else if (cap3.captured) lessons.captured++;
|
|
34054
35259
|
}
|
|
34055
35260
|
} else if (process.env["ZELARI_LESSONS"] === "0") {
|
|
34056
35261
|
lessons = {
|
|
@@ -34084,8 +35289,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
34084
35289
|
sources: scope.sources
|
|
34085
35290
|
} : void 0
|
|
34086
35291
|
});
|
|
34087
|
-
const
|
|
34088
|
-
completionHook = { ran: true, path:
|
|
35292
|
+
const path47 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
35293
|
+
completionHook = { ran: true, path: path47, completion };
|
|
34089
35294
|
} catch (err) {
|
|
34090
35295
|
completionHook = {
|
|
34091
35296
|
ran: true,
|
|
@@ -35303,10 +36508,18 @@ __export(planner_exports, {
|
|
|
35303
36508
|
KRAKEN_PLANNER_SYSTEM_PROMPT: () => KRAKEN_PLANNER_SYSTEM_PROMPT,
|
|
35304
36509
|
PlannerTransportError: () => PlannerTransportError,
|
|
35305
36510
|
buildGraphFromPlan: () => buildGraphFromPlan,
|
|
36511
|
+
buildPlannerSystemPrompt: () => buildPlannerSystemPrompt,
|
|
36512
|
+
buildPlannerUserPrompt: () => buildPlannerUserPrompt,
|
|
35306
36513
|
extractJsonObject: () => extractJsonObject,
|
|
35307
36514
|
planTaskGraph: () => planTaskGraph,
|
|
35308
36515
|
stripReasoningBlocks: () => stripReasoningBlocks
|
|
35309
36516
|
});
|
|
36517
|
+
function resolvePlannerWorkspaceChars(env = process.env) {
|
|
36518
|
+
const raw = env.ZELARI_KRAKEN_PLANNER_WORKSPACE_CHARS;
|
|
36519
|
+
if (raw === void 0 || raw === "") return DEFAULT_PLANNER_WORKSPACE_CHARS;
|
|
36520
|
+
const n = Number.parseInt(raw, 10);
|
|
36521
|
+
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_PLANNER_WORKSPACE_CHARS;
|
|
36522
|
+
}
|
|
35310
36523
|
function resolvePlannerTimeoutMs(env = process.env) {
|
|
35311
36524
|
const raw = env.ZELARI_KRAKEN_PLANNER_TIMEOUT_MS;
|
|
35312
36525
|
if (raw === void 0 || raw === "") return DEFAULT_LLM_TIMEOUT_MS;
|
|
@@ -35319,6 +36532,15 @@ function resolvePlannerMaxTokens(env = process.env) {
|
|
|
35319
36532
|
const n = Number.parseInt(raw, 10);
|
|
35320
36533
|
return Number.isFinite(n) && n > 0 ? n : DEFAULT_LLM_MAX_TOKENS;
|
|
35321
36534
|
}
|
|
36535
|
+
function resolveBennettsRazorEnabled(env = process.env) {
|
|
36536
|
+
const raw = env.ZELARI_KRAKEN_PLANNER_BENNETTS_RAZOR;
|
|
36537
|
+
if (raw === void 0 || raw === "") return false;
|
|
36538
|
+
return raw === "1" || raw.toLowerCase() === "true";
|
|
36539
|
+
}
|
|
36540
|
+
function buildPlannerSystemPrompt(env = process.env) {
|
|
36541
|
+
if (!resolveBennettsRazorEnabled(env)) return KRAKEN_PLANNER_SYSTEM_PROMPT;
|
|
36542
|
+
return KRAKEN_PLANNER_SYSTEM_PROMPT + "\n" + KRAKEN_PLANNER_BENNETTS_RAZOR_SECTION;
|
|
36543
|
+
}
|
|
35322
36544
|
function extractJsonObject(text, opts = {}) {
|
|
35323
36545
|
const stripped = stripReasoningBlocks(text);
|
|
35324
36546
|
const usable = findJsonIn(stripped, opts);
|
|
@@ -35615,14 +36837,32 @@ async function createDefaultLlmClient(opts) {
|
|
|
35615
36837
|
}
|
|
35616
36838
|
};
|
|
35617
36839
|
}
|
|
35618
|
-
function
|
|
35619
|
-
|
|
35620
|
-
|
|
35621
|
-
|
|
35622
|
-
return
|
|
35623
|
-
|
|
35624
|
-
|
|
35625
|
-
|
|
36840
|
+
function resolveWorkspaceListing(opts) {
|
|
36841
|
+
if (opts.workspace !== void 0) return opts.workspace || void 0;
|
|
36842
|
+
if (!opts.cwd) return void 0;
|
|
36843
|
+
const maxChars = resolvePlannerWorkspaceChars();
|
|
36844
|
+
if (maxChars <= 0) return void 0;
|
|
36845
|
+
try {
|
|
36846
|
+
return buildWorkspaceSummary(opts.cwd, { maxEntries: 24, maxChars }) || void 0;
|
|
36847
|
+
} catch {
|
|
36848
|
+
return void 0;
|
|
36849
|
+
}
|
|
36850
|
+
}
|
|
36851
|
+
function buildPlannerUserPrompt(prompt, opts = {}) {
|
|
36852
|
+
const parts = [`Goal:
|
|
36853
|
+
${prompt.trim()}`];
|
|
36854
|
+
if (opts.workspace?.trim()) {
|
|
36855
|
+
parts.push(
|
|
36856
|
+
"",
|
|
36857
|
+
"## The project this goal is about (real files on disk)",
|
|
36858
|
+
opts.workspace.trim()
|
|
36859
|
+
);
|
|
36860
|
+
}
|
|
36861
|
+
if (opts.previousAttempt?.trim()) {
|
|
36862
|
+
parts.push("", opts.previousAttempt.trim());
|
|
36863
|
+
}
|
|
36864
|
+
parts.push("", "Return ONLY the JSON object described in the system prompt.");
|
|
36865
|
+
return parts.join("\n");
|
|
35626
36866
|
}
|
|
35627
36867
|
function uniqueId(base, existing) {
|
|
35628
36868
|
if (!existing.has(base)) return base;
|
|
@@ -35631,11 +36871,40 @@ function uniqueId(base, existing) {
|
|
|
35631
36871
|
return `${base}-${i}`;
|
|
35632
36872
|
}
|
|
35633
36873
|
function buildAutoVerifyPrompt(general) {
|
|
35634
|
-
const
|
|
35635
|
-
|
|
35636
|
-
|
|
35637
|
-
|
|
35638
|
-
|
|
36874
|
+
const taskPrompt = general.prompt.length > MAX_VERIFY_TASK_PROMPT_CHARS ? `${general.prompt.slice(0, MAX_VERIFY_TASK_PROMPT_CHARS)}
|
|
36875
|
+
\u2026 [truncated]` : general.prompt;
|
|
36876
|
+
const parts = [
|
|
36877
|
+
`Verify on disk that this work was actually completed correctly: ${general.label}.`,
|
|
36878
|
+
"",
|
|
36879
|
+
"## The task that was carried out",
|
|
36880
|
+
taskPrompt
|
|
36881
|
+
];
|
|
36882
|
+
if (general.scope && general.scope.length > 0) {
|
|
36883
|
+
parts.push("", "## Paths the work was scoped to", ...general.scope.map((s) => `- ${s}`));
|
|
36884
|
+
}
|
|
36885
|
+
if (general.acceptance && general.acceptance.length > 0) {
|
|
36886
|
+
parts.push(
|
|
36887
|
+
"",
|
|
36888
|
+
"## Acceptance criteria to check explicitly",
|
|
36889
|
+
...general.acceptance.map((a) => `- ${a}`)
|
|
36890
|
+
);
|
|
36891
|
+
}
|
|
36892
|
+
parts.push(
|
|
36893
|
+
"",
|
|
36894
|
+
"Read the files involved rather than trusting any summary. Report the commands you ran and every gap you found.",
|
|
36895
|
+
"",
|
|
36896
|
+
"## How to report your verdict",
|
|
36897
|
+
"End your final message with a line of exactly this form, as the LAST line:",
|
|
36898
|
+
"",
|
|
36899
|
+
"VERDICT: PASS",
|
|
36900
|
+
"",
|
|
36901
|
+
"or",
|
|
36902
|
+
"",
|
|
36903
|
+
"VERDICT: FAIL",
|
|
36904
|
+
"",
|
|
36905
|
+
"This line is parsed. FAIL sends the work back to the tentacle that wrote it, together with everything you write above the line \u2014 so state each gap concretely enough to be acted on (file, what is wrong, what it should be). Only report FAIL for a real defect against the task or its acceptance criteria: a rework round is expensive and there is only a small number of them. Stylistic preferences are not a FAIL."
|
|
36906
|
+
);
|
|
36907
|
+
return parts.join("\n");
|
|
35639
36908
|
}
|
|
35640
36909
|
function buildGraphFromPlan(graphId, planned) {
|
|
35641
36910
|
const nodes = planned.map((p3) => ({
|
|
@@ -35688,12 +36957,16 @@ async function planTaskGraph(opts) {
|
|
|
35688
36957
|
const client = opts.llmClient ?? await createDefaultLlmClient({ provider: opts.provider, model: opts.model });
|
|
35689
36958
|
const maxNodes = opts.maxNodes ?? DEFAULT_MAX_NODES;
|
|
35690
36959
|
const graphId = opts.graphId ?? `kraken-${Date.now().toString(36)}`;
|
|
35691
|
-
const
|
|
36960
|
+
const workspace = resolveWorkspaceListing(opts);
|
|
36961
|
+
const userBase = buildPlannerUserPrompt(opts.prompt, {
|
|
36962
|
+
...opts.previousAttempt ? { previousAttempt: opts.previousAttempt } : {},
|
|
36963
|
+
...workspace ? { workspace } : {}
|
|
36964
|
+
});
|
|
35692
36965
|
let lastError;
|
|
35693
36966
|
let userMessage = userBase;
|
|
35694
36967
|
for (let attempt = 1; attempt <= MAX_PLAN_ATTEMPTS; attempt++) {
|
|
35695
36968
|
try {
|
|
35696
|
-
const text = await client.complete({ system:
|
|
36969
|
+
const text = await client.complete({ system: buildPlannerSystemPrompt(), user: userMessage });
|
|
35697
36970
|
const parsedJson = extractJsonObject(text, { requireKey: "nodes" });
|
|
35698
36971
|
const validated = PlannedGraphSchema.parse(parsedJson);
|
|
35699
36972
|
if (!validated.nodes.some((n) => n.kind === "general")) {
|
|
@@ -35719,7 +36992,7 @@ Your previous response was invalid (${lastError}). Return ONLY corrected JSON ma
|
|
|
35719
36992
|
`kraken planner: failed to produce a valid task graph after ${MAX_PLAN_ATTEMPTS} attempts \u2014 ${lastError}`
|
|
35720
36993
|
);
|
|
35721
36994
|
}
|
|
35722
|
-
var MAX_PLAN_ATTEMPTS, DEFAULT_LLM_TIMEOUT_MS, PlannerTransportError, DEFAULT_LLM_MAX_TOKENS, DEFAULT_MAX_RETRIES, KRAKEN_PLANNER_SYSTEM_PROMPT, PlannedNodeSchema, PlannedGraphSchema;
|
|
36995
|
+
var MAX_PLAN_ATTEMPTS, DEFAULT_PLANNER_WORKSPACE_CHARS, DEFAULT_LLM_TIMEOUT_MS, PlannerTransportError, DEFAULT_LLM_MAX_TOKENS, DEFAULT_MAX_RETRIES, KRAKEN_PLANNER_SYSTEM_PROMPT, KRAKEN_PLANNER_BENNETTS_RAZOR_SECTION, PlannedNodeSchema, PlannedGraphSchema, MAX_VERIFY_TASK_PROMPT_CHARS;
|
|
35723
36996
|
var init_planner = __esm({
|
|
35724
36997
|
"src/cli/kraken/planner.ts"() {
|
|
35725
36998
|
"use strict";
|
|
@@ -35728,7 +37001,9 @@ var init_planner = __esm({
|
|
|
35728
37001
|
init_providerConfig();
|
|
35729
37002
|
init_keyStore();
|
|
35730
37003
|
init_openai_compatible();
|
|
37004
|
+
init_workspaceSummary();
|
|
35731
37005
|
MAX_PLAN_ATTEMPTS = 2;
|
|
37006
|
+
DEFAULT_PLANNER_WORKSPACE_CHARS = 3e3;
|
|
35732
37007
|
DEFAULT_LLM_TIMEOUT_MS = 3e5;
|
|
35733
37008
|
PlannerTransportError = class extends Error {
|
|
35734
37009
|
constructor(message) {
|
|
@@ -35742,7 +37017,11 @@ var init_planner = __esm({
|
|
|
35742
37017
|
general: 1,
|
|
35743
37018
|
verify: 1,
|
|
35744
37019
|
fix: 0,
|
|
35745
|
-
merge: 0
|
|
37020
|
+
merge: 0,
|
|
37021
|
+
// Pillar 2 persona kinds: same retry budget as `verify` (the gate is
|
|
37022
|
+
// the same — trailer-based).
|
|
37023
|
+
spec: 1,
|
|
37024
|
+
conformance: 1
|
|
35746
37025
|
};
|
|
35747
37026
|
KRAKEN_PLANNER_SYSTEM_PROMPT = [
|
|
35748
37027
|
"You are the PLANNER for Kraken, a multi-agent graph executor.",
|
|
@@ -35754,14 +37033,45 @@ var init_planner = __esm({
|
|
|
35754
37033
|
"Rules:",
|
|
35755
37034
|
'- kind "explore": read-only research (no edits). Use to gather context before edits.',
|
|
35756
37035
|
'- kind "general": can edit files for one bounded, self-contained unit of work.',
|
|
35757
|
-
'- Do NOT emit "verify", "fix", or "merge" nodes \u2014 the executor adds those automatically.',
|
|
37036
|
+
'- Do NOT emit "verify", "spec", "conformance", "fix", or "merge" nodes \u2014 the executor adds those automatically based on the writer kind and the goal shape.',
|
|
35758
37037
|
'- "id" must be short, unique, kebab-case (e.g. "e1", "g-auth", "g-ui").',
|
|
35759
|
-
'- "prompt" must be
|
|
37038
|
+
'- "prompt" must be self-contained: the sub-agent sees ONLY this prompt, not this conversation.',
|
|
35760
37039
|
'- "deps" lists ids of nodes that must finish first (topological order); [] if none.',
|
|
37040
|
+
'- A node DOES receive what its "deps" reported when they finished \u2014 the executor injects their conclusions into its prompt. So write a dependent node as "using the findings above, \u2026" rather than duplicating the research its dependency will do.',
|
|
35761
37041
|
'- When two "general" nodes touch disjoint parts of the codebase, give each a "scope" (path/glob allowlist) so they can run in parallel safely. If scopes might overlap, either omit scope (forces sequential execution) or add a dep between them.',
|
|
37042
|
+
'- The user message includes a listing of the real project on disk. Build every "scope" from paths that appear there (or new paths that clearly belong beside them) \u2014 an invented path makes the parallelism decision meaningless, since scopes are exactly what the executor uses to decide which writers may run at the same time.',
|
|
35762
37043
|
'- Prefer one "explore" node feeding several parallel "general" nodes over one giant node.',
|
|
35763
37044
|
"- Keep the graph small: most goals need 3-8 nodes total.",
|
|
35764
|
-
'- "acceptance" (optional) lists concrete, checkable criteria for a "general" node.'
|
|
37045
|
+
'- "acceptance" (optional) lists concrete, checkable criteria for a "general" node. These are ENFORCED: the executor adds a verify tentacle that checks them on disk and can send the work back for a rework round when they are not met. Write criteria a reader can settle by opening a file or running a command ("exports slugify(input: string): string", "npm test passes"), never subjective ones ("the code is elegant") \u2014 a criterion nobody can check just burns a rework round.',
|
|
37046
|
+
"",
|
|
37047
|
+
"# Reviewer personas (Pillar 2 spec council)",
|
|
37048
|
+
"",
|
|
37049
|
+
"After every `general` node, the executor auto-injects a `verify` tentacle that checks the `acceptance[]` criteria. For goals that have a written spec or that must literally match the user prompt, the executor also auto-injects two more personas:",
|
|
37050
|
+
' - `spec` (spec-reviewer) \u2014 compares the writer\'s output against a written spec/plan, per requirement. Conservative: "reasonable but different" is a FAIL.',
|
|
37051
|
+
' - `conformance` (conformance-reviewer) \u2014 compares the writer\'s output against the USER\'S ORIGINAL VERBATIM PROMPT. Literal: "use session cookies" is a FAIL when the prompt said "use JWT".',
|
|
37052
|
+
"",
|
|
37053
|
+
"You (the JSON planner) do NOT emit these directly; the executor decides when to inject them. To hint that a node needs a `conformance` reviewer, include the user's original ask verbatim in the `prompt`."
|
|
37054
|
+
].join("\n");
|
|
37055
|
+
KRAKEN_PLANNER_BENNETTS_RAZOR_SECTION = [
|
|
37056
|
+
"",
|
|
37057
|
+
"# Bennett's Razor (tie-breaker)",
|
|
37058
|
+
"",
|
|
37059
|
+
'When two valid plans cover the goal, prefer the one that "assumes the least".',
|
|
37060
|
+
"A plan is *more specific* when it pins exact paths, exact semver, exact function",
|
|
37061
|
+
"signatures, or hard invariants about external state. A more general plan that",
|
|
37062
|
+
"still satisfies the goal generalises better (Bennett, AGI 2023: 1.1\xD7\u20135\xD7 the rate",
|
|
37063
|
+
"of MDL in his experiments).",
|
|
37064
|
+
"",
|
|
37065
|
+
"Examples of the bias you should apply:",
|
|
37066
|
+
'- "edit the user model" > "edit /src/models/user.ts and add a nullable email field"',
|
|
37067
|
+
" when the goal can be met either way.",
|
|
37068
|
+
'- "use a stable sort" > "use Array.prototype.sort with a custom comparator" unless',
|
|
37069
|
+
" the user pinned the API.",
|
|
37070
|
+
"- Prefer plans that touch fewer files and assume less about runtime versions.",
|
|
37071
|
+
"",
|
|
37072
|
+
"Hard constraint: do NOT weaken a plan below the bar in the name of weakness. The",
|
|
37073
|
+
"goal must be reachable by following the plan as written; weakness is only a",
|
|
37074
|
+
"tie-breaker among plans that do reach the goal."
|
|
35765
37075
|
].join("\n");
|
|
35766
37076
|
PlannedNodeSchema = external_exports.object({
|
|
35767
37077
|
id: external_exports.string().min(1).max(64).regex(/^[a-zA-Z0-9_-]+$/, "id must be alphanumeric/dash/underscore"),
|
|
@@ -35775,6 +37085,7 @@ var init_planner = __esm({
|
|
|
35775
37085
|
PlannedGraphSchema = external_exports.object({
|
|
35776
37086
|
nodes: external_exports.array(PlannedNodeSchema).min(1).max(DEFAULT_MAX_NODES)
|
|
35777
37087
|
});
|
|
37088
|
+
MAX_VERIFY_TASK_PROMPT_CHARS = 1200;
|
|
35778
37089
|
}
|
|
35779
37090
|
});
|
|
35780
37091
|
|
|
@@ -35792,11 +37103,16 @@ function snapshotPath(cwd) {
|
|
|
35792
37103
|
return path34.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
|
|
35793
37104
|
}
|
|
35794
37105
|
function toGraphSnapshot(graph, opts) {
|
|
37106
|
+
const unresolved = (opts.unresolvedFindings ?? []).map((u) => ({
|
|
37107
|
+
...u,
|
|
37108
|
+
findings: u.findings.slice(0, MAX_SNAPSHOT_FINDINGS_CHARS)
|
|
37109
|
+
}));
|
|
35795
37110
|
return {
|
|
35796
37111
|
graphId: graph.id,
|
|
35797
37112
|
goal: opts.goal,
|
|
35798
37113
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35799
37114
|
converged: opts.converged,
|
|
37115
|
+
...unresolved.length > 0 ? { unresolvedFindings: unresolved } : {},
|
|
35800
37116
|
nodes: [...graph.nodes.values()].map((n) => ({
|
|
35801
37117
|
id: n.id,
|
|
35802
37118
|
kind: n.kind,
|
|
@@ -35831,19 +37147,37 @@ function formatSnapshotForPlanner(snapshot) {
|
|
|
35831
37147
|
const done = snapshot.nodes.filter((n) => n.status === "done");
|
|
35832
37148
|
const failed = snapshot.nodes.filter((n) => n.status === "error");
|
|
35833
37149
|
const skipped = snapshot.nodes.filter((n) => n.status === "skipped");
|
|
35834
|
-
|
|
37150
|
+
const rejected = snapshot.unresolvedFindings ?? [];
|
|
37151
|
+
if (failed.length === 0 && skipped.length === 0 && rejected.length === 0) return "";
|
|
35835
37152
|
const line = (n) => `- ${n.label}${n.scope ? ` [${n.scope.join(", ")}]` : ""}${n.error ? ` \u2014 ${n.error}` : ""}`;
|
|
37153
|
+
const rejectedIds = new Set(rejected.map((r) => r.nodeId));
|
|
37154
|
+
const outcome = [
|
|
37155
|
+
`${done.length} done`,
|
|
37156
|
+
`${failed.length} failed`,
|
|
37157
|
+
`${skipped.length} never ran`,
|
|
37158
|
+
...rejected.length > 0 ? [`${rejected.length} rejected by review`] : []
|
|
37159
|
+
].join(", ");
|
|
35836
37160
|
const parts = [
|
|
35837
37161
|
"",
|
|
35838
37162
|
"## Previous unfinished task graph in this project",
|
|
35839
37163
|
`Goal it was working on: "${snapshot.goal}"`,
|
|
35840
|
-
`It did NOT finish (${
|
|
37164
|
+
`It did NOT finish cleanly (${outcome}).`,
|
|
35841
37165
|
"",
|
|
35842
37166
|
"If that goal is unrelated to the one above, ignore this section entirely.",
|
|
35843
37167
|
""
|
|
35844
37168
|
];
|
|
35845
|
-
|
|
35846
|
-
|
|
37169
|
+
const cleanlyDone = done.filter((n) => !rejectedIds.has(n.id));
|
|
37170
|
+
if (cleanlyDone.length > 0) {
|
|
37171
|
+
parts.push("Already completed \u2014 do NOT redo this work:", ...cleanlyDone.map(line), "");
|
|
37172
|
+
}
|
|
37173
|
+
if (rejected.length > 0) {
|
|
37174
|
+
parts.push(
|
|
37175
|
+
"Completed but REJECTED by review \u2014 the code exists, the defects do not fix themselves:",
|
|
37176
|
+
...rejected.map(
|
|
37177
|
+
(r) => `- ${r.label} \u2014 ${r.findings.trim().split("\n")[0] ?? "no detail"}`
|
|
37178
|
+
),
|
|
37179
|
+
""
|
|
37180
|
+
);
|
|
35847
37181
|
}
|
|
35848
37182
|
if (failed.length > 0) {
|
|
35849
37183
|
parts.push("Failed \u2014 needs to be finished or repaired:", ...failed.map(line), "");
|
|
@@ -35856,12 +37190,13 @@ function formatSnapshotForPlanner(snapshot) {
|
|
|
35856
37190
|
);
|
|
35857
37191
|
return parts.join("\n");
|
|
35858
37192
|
}
|
|
35859
|
-
var SNAPSHOT_DIR, SNAPSHOT_FILE;
|
|
37193
|
+
var SNAPSHOT_DIR, SNAPSHOT_FILE, MAX_SNAPSHOT_FINDINGS_CHARS;
|
|
35860
37194
|
var init_graphMemory = __esm({
|
|
35861
37195
|
"src/cli/kraken/graphMemory.ts"() {
|
|
35862
37196
|
"use strict";
|
|
35863
37197
|
SNAPSHOT_DIR = path34.join(".zelari", "kraken");
|
|
35864
37198
|
SNAPSHOT_FILE = "last-graph.json";
|
|
37199
|
+
MAX_SNAPSHOT_FINDINGS_CHARS = 400;
|
|
35865
37200
|
}
|
|
35866
37201
|
});
|
|
35867
37202
|
|
|
@@ -35873,21 +37208,149 @@ var init_tentacle = __esm({
|
|
|
35873
37208
|
}
|
|
35874
37209
|
});
|
|
35875
37210
|
|
|
37211
|
+
// src/cli/kraken/weaknessMeter.ts
|
|
37212
|
+
var weaknessMeter_exports = {};
|
|
37213
|
+
__export(weaknessMeter_exports, {
|
|
37214
|
+
_resetWeaknessMeterCacheForTests: () => _resetWeaknessMeterCacheForTests,
|
|
37215
|
+
isWeaknessMeterEnabled: () => isWeaknessMeterEnabled,
|
|
37216
|
+
measureWeaknessViaLLM: () => measureWeaknessViaLLM,
|
|
37217
|
+
parseMeterContent: () => parseMeterContent
|
|
37218
|
+
});
|
|
37219
|
+
function isWeaknessMeterEnabled(env = process.env) {
|
|
37220
|
+
if (meterEnabledCache !== null) return meterEnabledCache;
|
|
37221
|
+
const raw = env.ZELARI_KRAKEN_WEAKNESS_METER;
|
|
37222
|
+
const enabled = raw === "1" || raw === "true" || raw === "yes";
|
|
37223
|
+
meterEnabledCache = enabled;
|
|
37224
|
+
return enabled;
|
|
37225
|
+
}
|
|
37226
|
+
function _resetWeaknessMeterCacheForTests() {
|
|
37227
|
+
meterEnabledCache = null;
|
|
37228
|
+
}
|
|
37229
|
+
function resolveMeterModel(env = process.env, fallback) {
|
|
37230
|
+
const raw = env.ZELARI_KRAKEN_WEAKNESS_METER_MODEL;
|
|
37231
|
+
if (raw && raw.trim() !== "") return raw.trim();
|
|
37232
|
+
return fallback;
|
|
37233
|
+
}
|
|
37234
|
+
async function measureWeaknessViaLLM(text, options = {}) {
|
|
37235
|
+
const env = options.env ?? process.env;
|
|
37236
|
+
if (!isWeaknessMeterEnabled(env)) return null;
|
|
37237
|
+
if (typeof text !== "string" || text.trim() === "") return null;
|
|
37238
|
+
const provider = options.providerOverride ?? await resolveActiveProvider2(env);
|
|
37239
|
+
if (!provider) return null;
|
|
37240
|
+
const model = options.modelOverride ?? resolveMeterModel(env, provider.model);
|
|
37241
|
+
const userPayload = JSON.stringify({
|
|
37242
|
+
task: "measure weakness of the following reviewer reply",
|
|
37243
|
+
reply: text
|
|
37244
|
+
});
|
|
37245
|
+
const start = Date.now();
|
|
37246
|
+
try {
|
|
37247
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
37248
|
+
const ac = new AbortController();
|
|
37249
|
+
const timer = setTimeout(() => ac.abort(), METER_TIMEOUT_MS);
|
|
37250
|
+
let response;
|
|
37251
|
+
try {
|
|
37252
|
+
response = await fetchImpl(provider.endpoint, {
|
|
37253
|
+
method: "POST",
|
|
37254
|
+
headers: {
|
|
37255
|
+
"Content-Type": "application/json",
|
|
37256
|
+
Authorization: `Bearer ${provider.apiKey}`
|
|
37257
|
+
},
|
|
37258
|
+
body: JSON.stringify({
|
|
37259
|
+
model,
|
|
37260
|
+
messages: [
|
|
37261
|
+
{ role: "system", content: WEAKNESS_METER_PROMPT },
|
|
37262
|
+
{ role: "user", content: userPayload }
|
|
37263
|
+
],
|
|
37264
|
+
// The meter is a classification call; no need for a hot
|
|
37265
|
+
// sampling distribution. Most providers accept this pair
|
|
37266
|
+
// without complaint; the few that require it get a stable
|
|
37267
|
+
// answer.
|
|
37268
|
+
temperature: 0,
|
|
37269
|
+
stream: false
|
|
37270
|
+
}),
|
|
37271
|
+
signal: ac.signal
|
|
37272
|
+
});
|
|
37273
|
+
} finally {
|
|
37274
|
+
clearTimeout(timer);
|
|
37275
|
+
}
|
|
37276
|
+
if (!response.ok) return null;
|
|
37277
|
+
const json2 = await response.json();
|
|
37278
|
+
const raw = json2.choices?.[0]?.message?.content;
|
|
37279
|
+
if (typeof raw !== "string") return null;
|
|
37280
|
+
return parseMeterContent(raw, model, Date.now() - start);
|
|
37281
|
+
} catch {
|
|
37282
|
+
return null;
|
|
37283
|
+
}
|
|
37284
|
+
}
|
|
37285
|
+
function parseMeterContent(raw, model, durationMs) {
|
|
37286
|
+
const trimmed = raw.trim();
|
|
37287
|
+
const fenced = /^```(?:json)?\s*([\s\S]+?)\s*```$/i.exec(trimmed);
|
|
37288
|
+
const body = fenced ? fenced[1] : trimmed;
|
|
37289
|
+
let parsed;
|
|
37290
|
+
try {
|
|
37291
|
+
parsed = JSON.parse(body);
|
|
37292
|
+
} catch {
|
|
37293
|
+
return null;
|
|
37294
|
+
}
|
|
37295
|
+
const validated = WeaknessMeterResponseSchema.safeParse(parsed);
|
|
37296
|
+
if (!validated.success) return null;
|
|
37297
|
+
return {
|
|
37298
|
+
meter: validated.data,
|
|
37299
|
+
weakness: weaknessFromMeter(validated.data),
|
|
37300
|
+
durationMs,
|
|
37301
|
+
model
|
|
37302
|
+
};
|
|
37303
|
+
}
|
|
37304
|
+
async function resolveActiveProvider2(env) {
|
|
37305
|
+
const cfg = await getProviderConfig();
|
|
37306
|
+
if (!cfg) return null;
|
|
37307
|
+
const providerId = cfg.activeProviderId;
|
|
37308
|
+
const key = await resolveApiKeyWithMeta(providerId, env);
|
|
37309
|
+
if (!key?.apiKey) return null;
|
|
37310
|
+
const model = getModelForProvider(providerId) ?? cfg.modelByProvider[providerId] ?? "";
|
|
37311
|
+
const endpoint = getCustomEndpoint(providerId) ?? `https://api.openai.com/v1/chat/completions`;
|
|
37312
|
+
return { providerId, model, endpoint, apiKey: key.apiKey };
|
|
37313
|
+
}
|
|
37314
|
+
var meterEnabledCache, METER_TIMEOUT_MS;
|
|
37315
|
+
var init_weaknessMeter = __esm({
|
|
37316
|
+
"src/cli/kraken/weaknessMeter.ts"() {
|
|
37317
|
+
"use strict";
|
|
37318
|
+
init_dist();
|
|
37319
|
+
init_providerConfig();
|
|
37320
|
+
init_keyStore();
|
|
37321
|
+
meterEnabledCache = null;
|
|
37322
|
+
METER_TIMEOUT_MS = (() => {
|
|
37323
|
+
const raw = process.env.ZELARI_KRAKEN_WEAKNESS_METER_TIMEOUT_MS;
|
|
37324
|
+
if (!raw) return 12e3;
|
|
37325
|
+
const n = Number.parseInt(raw, 10);
|
|
37326
|
+
return Number.isFinite(n) && n > 0 ? n : 12e3;
|
|
37327
|
+
})();
|
|
37328
|
+
}
|
|
37329
|
+
});
|
|
37330
|
+
|
|
35876
37331
|
// src/cli/kraken/executor.ts
|
|
35877
37332
|
var executor_exports = {};
|
|
35878
37333
|
__export(executor_exports, {
|
|
35879
37334
|
DEFAULT_CANCEL_GRACE_MS: () => DEFAULT_CANCEL_GRACE_MS,
|
|
35880
37335
|
DEFAULT_FIX_BUDGET: () => DEFAULT_FIX_BUDGET,
|
|
37336
|
+
DEFAULT_GRAPH_TIMEOUT_MS: () => DEFAULT_GRAPH_TIMEOUT_MS,
|
|
35881
37337
|
DEFAULT_MAX_PARALLEL: () => DEFAULT_MAX_PARALLEL,
|
|
37338
|
+
DEFAULT_MAX_REVIEW_ROUNDS: () => DEFAULT_MAX_REVIEW_ROUNDS,
|
|
35882
37339
|
DEFAULT_NODE_TIMEOUT_MS: () => DEFAULT_NODE_TIMEOUT_MS,
|
|
35883
37340
|
DEFAULT_WRITER_NODE_TIMEOUT_MS: () => DEFAULT_WRITER_NODE_TIMEOUT_MS,
|
|
35884
37341
|
KrakenGraphExecutor: () => KrakenGraphExecutor,
|
|
37342
|
+
MAX_UPSTREAM_CHARS_PER_DEP: () => MAX_UPSTREAM_CHARS_PER_DEP,
|
|
37343
|
+
MAX_UPSTREAM_CHARS_TOTAL: () => MAX_UPSTREAM_CHARS_TOTAL,
|
|
37344
|
+
buildUpstreamContext: () => buildUpstreamContext,
|
|
35885
37345
|
isKrakenGraphEnabled: () => isKrakenGraphEnabled,
|
|
35886
37346
|
isWorldModelGateEnabled: () => isWorldModelGateEnabled,
|
|
35887
37347
|
resolveCancelGraceMs: () => resolveCancelGraceMs,
|
|
35888
37348
|
resolveFixBudget: () => resolveFixBudget,
|
|
37349
|
+
resolveGraphTimeoutMs: () => resolveGraphTimeoutMs,
|
|
35889
37350
|
resolveMaxParallel: () => resolveMaxParallel,
|
|
35890
|
-
|
|
37351
|
+
resolveMaxReviewRounds: () => resolveMaxReviewRounds,
|
|
37352
|
+
resolveNodeTimeoutMs: () => resolveNodeTimeoutMs,
|
|
37353
|
+
thoroughnessForKind: () => thoroughnessForKind
|
|
35891
37354
|
});
|
|
35892
37355
|
import { existsSync as existsSync34 } from "node:fs";
|
|
35893
37356
|
import path35 from "node:path";
|
|
@@ -35897,11 +37360,25 @@ function resolveMaxParallel(env = process.env) {
|
|
|
35897
37360
|
const n = Number.parseInt(raw, 10);
|
|
35898
37361
|
return Number.isFinite(n) && n > 0 ? n : DEFAULT_MAX_PARALLEL;
|
|
35899
37362
|
}
|
|
35900
|
-
function resolveFixBudget(env = process.env) {
|
|
37363
|
+
function resolveFixBudget(env = process.env, nodeCount = 0) {
|
|
35901
37364
|
const raw = env.ZELARI_KRAKEN_FIX_BUDGET;
|
|
35902
|
-
if (raw === void 0 || raw === "")
|
|
37365
|
+
if (raw === void 0 || raw === "") {
|
|
37366
|
+
return Math.max(DEFAULT_FIX_BUDGET, Math.ceil(nodeCount / 2));
|
|
37367
|
+
}
|
|
35903
37368
|
const n = Number.parseInt(raw, 10);
|
|
35904
|
-
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_FIX_BUDGET;
|
|
37369
|
+
return Number.isFinite(n) && n >= 0 ? n : Math.max(DEFAULT_FIX_BUDGET, Math.ceil(nodeCount / 2));
|
|
37370
|
+
}
|
|
37371
|
+
function resolveMaxReviewRounds(env = process.env) {
|
|
37372
|
+
const raw = env.ZELARI_KRAKEN_MAX_REVIEW_ROUNDS;
|
|
37373
|
+
if (raw === void 0 || raw === "") return DEFAULT_MAX_REVIEW_ROUNDS;
|
|
37374
|
+
const n = Number.parseInt(raw, 10);
|
|
37375
|
+
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_MAX_REVIEW_ROUNDS;
|
|
37376
|
+
}
|
|
37377
|
+
function resolveGraphTimeoutMs(env = process.env) {
|
|
37378
|
+
const raw = env.ZELARI_KRAKEN_GRAPH_TIMEOUT_MS;
|
|
37379
|
+
if (raw === void 0 || raw === "") return DEFAULT_GRAPH_TIMEOUT_MS;
|
|
37380
|
+
const n = Number.parseInt(raw, 10);
|
|
37381
|
+
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_GRAPH_TIMEOUT_MS;
|
|
35905
37382
|
}
|
|
35906
37383
|
function resolveCancelGraceMs(env = process.env) {
|
|
35907
37384
|
const raw = env.ZELARI_KRAKEN_CANCEL_GRACE_MS;
|
|
@@ -35939,7 +37416,51 @@ function defaultChecksExists(cwd) {
|
|
|
35939
37416
|
return false;
|
|
35940
37417
|
}
|
|
35941
37418
|
}
|
|
35942
|
-
|
|
37419
|
+
function buildUpstreamContext(graph, node) {
|
|
37420
|
+
const parts = [];
|
|
37421
|
+
const omitted = [];
|
|
37422
|
+
let budget = MAX_UPSTREAM_CHARS_TOTAL;
|
|
37423
|
+
for (const depId of node.deps) {
|
|
37424
|
+
const dep = graph.nodes.get(depId);
|
|
37425
|
+
if (!dep || dep.status !== "done") continue;
|
|
37426
|
+
const raw = (dep.result ?? "").trim();
|
|
37427
|
+
if (!raw) continue;
|
|
37428
|
+
const cap3 = Math.min(MAX_UPSTREAM_CHARS_PER_DEP, budget);
|
|
37429
|
+
if (cap3 <= 0) {
|
|
37430
|
+
omitted.push(dep.label);
|
|
37431
|
+
continue;
|
|
37432
|
+
}
|
|
37433
|
+
const body = raw.length > cap3 ? `${raw.slice(0, cap3)}
|
|
37434
|
+
\u2026 [truncated ${raw.length}\u2192${cap3} chars]` : raw;
|
|
37435
|
+
budget -= Math.min(raw.length, cap3);
|
|
37436
|
+
const scope = dep.scope && dep.scope.length > 0 ? `, scope: ${dep.scope.join(", ")}` : "";
|
|
37437
|
+
parts.push(`### ${dep.label} (${dep.kind}${scope})
|
|
37438
|
+
${body}`);
|
|
37439
|
+
}
|
|
37440
|
+
if (parts.length === 0) return "";
|
|
37441
|
+
const lines = [
|
|
37442
|
+
"",
|
|
37443
|
+
"## Context from completed upstream tasks",
|
|
37444
|
+
"Results reported by the tasks this one depends on. Treat them as hypotheses \u2014 prefer the actual files on disk where they conflict.",
|
|
37445
|
+
"",
|
|
37446
|
+
...parts
|
|
37447
|
+
];
|
|
37448
|
+
if (omitted.length > 0) {
|
|
37449
|
+
lines.push("", `(omitted for context budget: ${omitted.join(", ")})`);
|
|
37450
|
+
}
|
|
37451
|
+
return lines.join("\n");
|
|
37452
|
+
}
|
|
37453
|
+
function thoroughnessForKind(kind) {
|
|
37454
|
+
return kind === "general" || kind === "fix" ? "deep" : "medium";
|
|
37455
|
+
}
|
|
37456
|
+
function firstLine(text, maxChars = 160) {
|
|
37457
|
+
const line = text.trim().split("\n").find((l) => l.trim() !== "")?.trim() ?? "";
|
|
37458
|
+
return line.length > maxChars ? `${line.slice(0, maxChars)}\u2026` : line;
|
|
37459
|
+
}
|
|
37460
|
+
function agentForNode(node) {
|
|
37461
|
+
return node.kind === "explore" || node.kind === "verify" ? node.kind : "general";
|
|
37462
|
+
}
|
|
37463
|
+
var DEFAULT_MAX_PARALLEL, DEFAULT_FIX_BUDGET, DEFAULT_MAX_REVIEW_ROUNDS, DEFAULT_GRAPH_TIMEOUT_MS, DEFAULT_NODE_TIMEOUT_MS, DEFAULT_WRITER_NODE_TIMEOUT_MS, DEFAULT_CANCEL_GRACE_MS, MAX_UPSTREAM_CHARS_PER_DEP, MAX_UPSTREAM_CHARS_TOTAL, KrakenGraphExecutor;
|
|
35943
37464
|
var init_executor = __esm({
|
|
35944
37465
|
"src/cli/kraken/executor.ts"() {
|
|
35945
37466
|
"use strict";
|
|
@@ -35952,9 +37473,13 @@ var init_executor = __esm({
|
|
|
35952
37473
|
init_graphMemory();
|
|
35953
37474
|
DEFAULT_MAX_PARALLEL = 12;
|
|
35954
37475
|
DEFAULT_FIX_BUDGET = 3;
|
|
37476
|
+
DEFAULT_MAX_REVIEW_ROUNDS = 1;
|
|
37477
|
+
DEFAULT_GRAPH_TIMEOUT_MS = 0;
|
|
35955
37478
|
DEFAULT_NODE_TIMEOUT_MS = 3e5;
|
|
35956
37479
|
DEFAULT_WRITER_NODE_TIMEOUT_MS = 9e5;
|
|
35957
37480
|
DEFAULT_CANCEL_GRACE_MS = 3e4;
|
|
37481
|
+
MAX_UPSTREAM_CHARS_PER_DEP = 2800;
|
|
37482
|
+
MAX_UPSTREAM_CHARS_TOTAL = 8e3;
|
|
35958
37483
|
KrakenGraphExecutor = class {
|
|
35959
37484
|
deps;
|
|
35960
37485
|
parentCwd;
|
|
@@ -35964,12 +37489,44 @@ var init_executor = __esm({
|
|
|
35964
37489
|
/** Explicit all-kinds override; when undefined the budget is per-kind. */
|
|
35965
37490
|
nodeTimeoutMs;
|
|
35966
37491
|
cancelGraceMs;
|
|
37492
|
+
/** Explicit override; when undefined the budget scales with the graph size. */
|
|
37493
|
+
fixBudgetOption;
|
|
35967
37494
|
fixBudgetRemaining;
|
|
37495
|
+
maxReviewRounds;
|
|
37496
|
+
graphTimeoutMs;
|
|
35968
37497
|
worldModelGateOverride;
|
|
35969
37498
|
runTentacleFn;
|
|
35970
37499
|
mergeFn;
|
|
35971
37500
|
backtestFn;
|
|
35972
37501
|
nodeRunState = /* @__PURE__ */ new Map();
|
|
37502
|
+
/** fix node id → id of the failed node it was spawned to repair. */
|
|
37503
|
+
repairs = /* @__PURE__ */ new Map();
|
|
37504
|
+
/**
|
|
37505
|
+
* rework node id → id of the writer whose work it is redoing. A rework is a
|
|
37506
|
+
* `fix` node, but unlike a repair it must NOT create a worktree of its own:
|
|
37507
|
+
* it edits the writer's existing one (see {@link spawnReworkPair}).
|
|
37508
|
+
*/
|
|
37509
|
+
reworks = /* @__PURE__ */ new Map();
|
|
37510
|
+
/** lineage root writer id → rework rounds already spent on that lineage. */
|
|
37511
|
+
reviewRounds = /* @__PURE__ */ new Map();
|
|
37512
|
+
/**
|
|
37513
|
+
* rework node id → the ORIGINAL writer its lineage started from.
|
|
37514
|
+
*
|
|
37515
|
+
* The budget has to be per lineage, not per node: a rework is itself a
|
|
37516
|
+
* writer, so counting rounds against the node being reworked reset the
|
|
37517
|
+
* counter every round and the graph chained rework → verify → rework
|
|
37518
|
+
* forever, terminating only on the scheduler's iteration cap.
|
|
37519
|
+
*/
|
|
37520
|
+
reviewLineage = /* @__PURE__ */ new Map();
|
|
37521
|
+
/** Verify verdicts left unresolved when the run ends. */
|
|
37522
|
+
unresolved = [];
|
|
37523
|
+
/** Live cancellation handles for the tentacles currently running. */
|
|
37524
|
+
nodeControllers = /* @__PURE__ */ new Map();
|
|
37525
|
+
/** Wall-clock duration of each node's last run, by node id. */
|
|
37526
|
+
durationsMs = /* @__PURE__ */ new Map();
|
|
37527
|
+
signal;
|
|
37528
|
+
/** Set once the run has been cancelled: stops admission, retries and fixes. */
|
|
37529
|
+
aborted = false;
|
|
35973
37530
|
fixCounter = 0;
|
|
35974
37531
|
constructor(opts) {
|
|
35975
37532
|
this.deps = opts.taskToolDeps;
|
|
@@ -35979,16 +37536,50 @@ var init_executor = __esm({
|
|
|
35979
37536
|
this.maxParallel = opts.maxParallel ?? resolveMaxParallel();
|
|
35980
37537
|
this.nodeTimeoutMs = opts.nodeTimeoutMs;
|
|
35981
37538
|
this.cancelGraceMs = opts.cancelGraceMs;
|
|
37539
|
+
this.fixBudgetOption = opts.fixBudget;
|
|
35982
37540
|
this.fixBudgetRemaining = opts.fixBudget ?? resolveFixBudget();
|
|
37541
|
+
this.maxReviewRounds = opts.maxReviewRounds ?? resolveMaxReviewRounds();
|
|
37542
|
+
this.graphTimeoutMs = opts.graphTimeoutMs ?? resolveGraphTimeoutMs();
|
|
35983
37543
|
this.worldModelGateOverride = opts.worldModelGate;
|
|
37544
|
+
this.signal = opts.signal;
|
|
35984
37545
|
this.runTentacleFn = opts.runTentacleFn ?? runTentacle;
|
|
35985
37546
|
this.mergeFn = opts.mergeFn ?? mergeKrakenWorktree;
|
|
35986
37547
|
this.backtestFn = opts.backtestFn ?? runBacktest;
|
|
35987
37548
|
}
|
|
35988
37549
|
/** Execute the graph in place (mutates node statuses) until it settles. */
|
|
35989
37550
|
async execute(graph) {
|
|
37551
|
+
if (this.fixBudgetOption === void 0) {
|
|
37552
|
+
this.fixBudgetRemaining = resolveFixBudget(process.env, graph.nodes.size);
|
|
37553
|
+
}
|
|
37554
|
+
const onAbort = () => this.cancelRun();
|
|
37555
|
+
if (this.signal) {
|
|
37556
|
+
if (this.signal.aborted) this.aborted = true;
|
|
37557
|
+
else this.signal.addEventListener("abort", onAbort, { once: true });
|
|
37558
|
+
}
|
|
37559
|
+
let graphTimer;
|
|
37560
|
+
if (this.graphTimeoutMs > 0) {
|
|
37561
|
+
graphTimer = setTimeout(() => {
|
|
37562
|
+
this.radio("graph_failed", {
|
|
37563
|
+
description: "graph executor",
|
|
37564
|
+
detail: `graph exceeded its ${this.graphTimeoutMs}ms wall-clock budget \u2014 cancelling`,
|
|
37565
|
+
ok: false
|
|
37566
|
+
});
|
|
37567
|
+
this.cancelRun();
|
|
37568
|
+
}, this.graphTimeoutMs);
|
|
37569
|
+
graphTimer.unref?.();
|
|
37570
|
+
}
|
|
37571
|
+
try {
|
|
37572
|
+
return await this.schedule(graph);
|
|
37573
|
+
} finally {
|
|
37574
|
+
if (graphTimer) clearTimeout(graphTimer);
|
|
37575
|
+
this.signal?.removeEventListener("abort", onAbort);
|
|
37576
|
+
}
|
|
37577
|
+
}
|
|
37578
|
+
/** The scheduling loop proper. See {@link execute} for the cancellation wrapper. */
|
|
37579
|
+
async schedule(graph) {
|
|
35990
37580
|
const maxIterations = Math.max(64, graph.nodes.size * 8);
|
|
35991
37581
|
let iterations = 0;
|
|
37582
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
35992
37583
|
startKrakenGraphLive(graph);
|
|
35993
37584
|
while (!isSettled(graph)) {
|
|
35994
37585
|
iterations += 1;
|
|
@@ -36000,24 +37591,39 @@ var init_executor = __esm({
|
|
|
36000
37591
|
});
|
|
36001
37592
|
break;
|
|
36002
37593
|
}
|
|
36003
|
-
|
|
36004
|
-
|
|
36005
|
-
|
|
36006
|
-
|
|
37594
|
+
if (this.aborted && inFlight.size === 0) break;
|
|
37595
|
+
const admitted = this.aborted ? [] : this.admit(graph, inFlight);
|
|
37596
|
+
if (admitted.length > 0) {
|
|
37597
|
+
for (const node of admitted) node.status = "running";
|
|
37598
|
+
updateKrakenGraphLive(graph);
|
|
37599
|
+
for (const node of admitted) inFlight.set(node.id, this.runNodeSafely(node, graph));
|
|
37600
|
+
}
|
|
37601
|
+
if (inFlight.size === 0) {
|
|
37602
|
+
if (!this.skipBlockedNodes(graph)) {
|
|
36007
37603
|
break;
|
|
36008
37604
|
}
|
|
36009
37605
|
continue;
|
|
36010
37606
|
}
|
|
36011
|
-
const
|
|
36012
|
-
|
|
36013
|
-
const
|
|
36014
|
-
|
|
36015
|
-
this.applyResult(graph, wave[i], results[i]);
|
|
36016
|
-
}
|
|
37607
|
+
const { id, res } = await Promise.race(inFlight.values());
|
|
37608
|
+
inFlight.delete(id);
|
|
37609
|
+
const settledNode = graph.nodes.get(id);
|
|
37610
|
+
if (settledNode) this.applyResult(graph, settledNode, res);
|
|
36017
37611
|
updateKrakenGraphLive(graph);
|
|
36018
37612
|
}
|
|
37613
|
+
if (inFlight.size > 0) {
|
|
37614
|
+
for (const { id, res } of await Promise.all(inFlight.values())) {
|
|
37615
|
+
const node = graph.nodes.get(id);
|
|
37616
|
+
if (node) this.applyResult(graph, node, res);
|
|
37617
|
+
}
|
|
37618
|
+
inFlight.clear();
|
|
37619
|
+
}
|
|
37620
|
+
if (this.aborted) {
|
|
37621
|
+
for (const n of graph.nodes.values()) {
|
|
37622
|
+
if (n.status === "pending") n.status = "skipped";
|
|
37623
|
+
}
|
|
37624
|
+
}
|
|
36019
37625
|
let backtest;
|
|
36020
|
-
const converged = isConverged(graph);
|
|
37626
|
+
const converged = !this.aborted && isConverged(graph);
|
|
36021
37627
|
if (converged) {
|
|
36022
37628
|
const gateOn = this.worldModelGateOverride ?? isWorldModelGateEnabled(this.parentCwd);
|
|
36023
37629
|
if (gateOn) {
|
|
@@ -36031,47 +37637,133 @@ var init_executor = __esm({
|
|
|
36031
37637
|
} else {
|
|
36032
37638
|
this.radio("graph_failed", {
|
|
36033
37639
|
description: "graph executor",
|
|
36034
|
-
detail: `failed nodes: ${failedNodeIds(graph).join(", ") || "none"}`,
|
|
37640
|
+
detail: this.aborted ? "cancelled by caller" : `failed nodes: ${failedNodeIds(graph).join(", ") || "none"}`,
|
|
36035
37641
|
ok: false
|
|
36036
37642
|
});
|
|
36037
37643
|
}
|
|
36038
37644
|
endKrakenGraphLive(graph, converged);
|
|
36039
37645
|
await saveGraphSnapshot(
|
|
36040
37646
|
this.parentCwd,
|
|
36041
|
-
toGraphSnapshot(graph, {
|
|
37647
|
+
toGraphSnapshot(graph, {
|
|
37648
|
+
goal: this.goal ?? graph.id,
|
|
37649
|
+
converged,
|
|
37650
|
+
unresolvedFindings: this.unresolved
|
|
37651
|
+
})
|
|
36042
37652
|
);
|
|
36043
37653
|
return {
|
|
36044
37654
|
graph,
|
|
36045
37655
|
converged,
|
|
36046
37656
|
failedNodeIds: failedNodeIds(graph),
|
|
36047
37657
|
counts: countByStatus(graph),
|
|
37658
|
+
durationsMs: Object.fromEntries(this.durationsMs),
|
|
37659
|
+
cancelled: this.aborted,
|
|
37660
|
+
unresolvedFindings: [...this.unresolved],
|
|
36048
37661
|
...backtest ? { backtest } : {}
|
|
36049
37662
|
};
|
|
36050
37663
|
}
|
|
37664
|
+
/**
|
|
37665
|
+
* Stop the run: no further admissions, and every tentacle currently running
|
|
37666
|
+
* is told to unwind. Each node's own timeout/grace machinery then resolves
|
|
37667
|
+
* it, so `execute()` settles instead of leaving orphans behind.
|
|
37668
|
+
*/
|
|
37669
|
+
cancelRun() {
|
|
37670
|
+
if (this.aborted) return;
|
|
37671
|
+
this.aborted = true;
|
|
37672
|
+
this.radio("graph_failed", {
|
|
37673
|
+
description: "graph executor",
|
|
37674
|
+
detail: `cancelling ${this.nodeControllers.size} running tentacle(s)`,
|
|
37675
|
+
ok: false
|
|
37676
|
+
});
|
|
37677
|
+
for (const controller of this.nodeControllers.values()) controller.abort();
|
|
37678
|
+
}
|
|
37679
|
+
/**
|
|
37680
|
+
* Pick the ready nodes that may start right now: parallel-safe against every
|
|
37681
|
+
* node already running AND against each other, within the concurrency cap.
|
|
37682
|
+
*
|
|
37683
|
+
* Unlike a wave-at-a-time scheduler this is called on every completion, so a
|
|
37684
|
+
* node becomes eligible the moment its blocker settles instead of waiting
|
|
37685
|
+
* for the slowest member of some earlier batch.
|
|
37686
|
+
*/
|
|
37687
|
+
admit(graph, inFlight) {
|
|
37688
|
+
const capacity = this.maxParallel - inFlight.size;
|
|
37689
|
+
if (capacity <= 0) return [];
|
|
37690
|
+
const running = [];
|
|
37691
|
+
for (const id of inFlight.keys()) {
|
|
37692
|
+
const n = graph.nodes.get(id);
|
|
37693
|
+
if (n) running.push(n);
|
|
37694
|
+
}
|
|
37695
|
+
const admitted = [];
|
|
37696
|
+
for (const node of getReadyNodes(graph)) {
|
|
37697
|
+
if (admitted.length >= capacity) break;
|
|
37698
|
+
const safe = running.every((r) => canRunParallel(r, node)) && admitted.every((a) => canRunParallel(a, node));
|
|
37699
|
+
if (safe) admitted.push(node);
|
|
37700
|
+
}
|
|
37701
|
+
return admitted;
|
|
37702
|
+
}
|
|
37703
|
+
/**
|
|
37704
|
+
* Run one node, tagging the result with its id and converting an unexpected
|
|
37705
|
+
* throw into a node failure. The scheduler races these promises, so a
|
|
37706
|
+
* rejection would abandon every other in-flight tentacle mid-write; one
|
|
37707
|
+
* failed node that the retry/fix machinery can reason about is strictly
|
|
37708
|
+
* better than an aborted graph.
|
|
37709
|
+
*/
|
|
37710
|
+
runNodeSafely(node, graph) {
|
|
37711
|
+
return this.runNode(node, graph).then(
|
|
37712
|
+
(res) => ({ id: node.id, res }),
|
|
37713
|
+
(err) => ({
|
|
37714
|
+
id: node.id,
|
|
37715
|
+
res: {
|
|
37716
|
+
ok: false,
|
|
37717
|
+
agent: agentForNode(node),
|
|
37718
|
+
error: `tentacle threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
37719
|
+
cancelled: true
|
|
37720
|
+
}
|
|
37721
|
+
})
|
|
37722
|
+
);
|
|
37723
|
+
}
|
|
36051
37724
|
/** Run one node: dispatch to the merge handler for `merge` nodes, else a tentacle. */
|
|
36052
37725
|
async runNode(node, graph) {
|
|
37726
|
+
const startedAt = Date.now();
|
|
37727
|
+
try {
|
|
37728
|
+
return await this.runNodeInner(node, graph);
|
|
37729
|
+
} finally {
|
|
37730
|
+
this.durationsMs.set(node.id, Date.now() - startedAt);
|
|
37731
|
+
this.nodeControllers.delete(node.id);
|
|
37732
|
+
}
|
|
37733
|
+
}
|
|
37734
|
+
async runNodeInner(node, graph) {
|
|
36053
37735
|
this.radio("node_start", { description: node.label, agent: node.kind });
|
|
36054
37736
|
if (node.kind === "merge") {
|
|
36055
37737
|
return this.runMergeNode(node, graph);
|
|
36056
37738
|
}
|
|
36057
|
-
const
|
|
36058
|
-
const
|
|
37739
|
+
const isRework = this.reworks.has(node.id);
|
|
37740
|
+
const usesWorktree = (node.kind === "general" || node.kind === "fix") && !isRework;
|
|
37741
|
+
const agent = node.kind === "fix" ? "general" : node.kind === "spec" || node.kind === "conformance" ? "verify" : node.kind;
|
|
36059
37742
|
const controller = new AbortController();
|
|
37743
|
+
this.nodeControllers.set(node.id, controller);
|
|
37744
|
+
if (this.aborted) controller.abort();
|
|
37745
|
+
const upstream = buildUpstreamContext(graph, node);
|
|
37746
|
+
const inheritedCwd = node.kind === "verify" || isRework ? this.inheritedWorktreeCwdFor(node, graph) : void 0;
|
|
36060
37747
|
const res = await this.withNodeTimeout(
|
|
36061
37748
|
this.runTentacleFn({
|
|
36062
|
-
|
|
37749
|
+
// `allowWorktree: false` is what actually stops a rework from opening
|
|
37750
|
+
// its own worktree: creation is driven by the agent kind ('general')
|
|
37751
|
+
// inside runTentacle, not by anything the executor passes per-call.
|
|
37752
|
+
deps: isRework ? { ...this.deps, allowWorktree: false } : this.deps,
|
|
36063
37753
|
args: {
|
|
36064
37754
|
description: node.label,
|
|
36065
|
-
prompt: node.prompt
|
|
37755
|
+
prompt: upstream ? `${node.prompt}
|
|
37756
|
+
${upstream}` : node.prompt,
|
|
36066
37757
|
scope: node.scope,
|
|
36067
37758
|
acceptance: node.acceptance
|
|
36068
37759
|
},
|
|
36069
37760
|
agent,
|
|
36070
|
-
thoroughness:
|
|
37761
|
+
thoroughness: thoroughnessForKind(node.kind),
|
|
36071
37762
|
parentCwd: this.parentCwd,
|
|
37763
|
+
...inheritedCwd ? { cwdOverride: inheritedCwd } : {},
|
|
36072
37764
|
sessionId: this.sessionId,
|
|
36073
37765
|
// Defer merge for writers so the executor controls merge ordering
|
|
36074
|
-
// (Correction 4); explore/verify never create a worktree.
|
|
37766
|
+
// (Correction 4); explore/verify/rework never create a worktree.
|
|
36075
37767
|
deferMerge: usesWorktree,
|
|
36076
37768
|
graphId: graph.id,
|
|
36077
37769
|
nodeId: node.id,
|
|
@@ -36132,20 +37824,71 @@ var init_executor = __esm({
|
|
|
36132
37824
|
};
|
|
36133
37825
|
}
|
|
36134
37826
|
/**
|
|
36135
|
-
*
|
|
36136
|
-
*
|
|
36137
|
-
*
|
|
36138
|
-
*
|
|
36139
|
-
*
|
|
36140
|
-
*
|
|
37827
|
+
* The worktree a node should run in, inherited from the writer behind it.
|
|
37828
|
+
*
|
|
37829
|
+
* For a `verify`: verification happens BEFORE the merge node, so when its
|
|
37830
|
+
* writer worked in an isolated worktree the changes are not in the parent
|
|
37831
|
+
* tree yet — a verify tentacle pointed at `parentCwd` was inspecting a tree
|
|
37832
|
+
* that provably did not contain the work it was asked to check, and reported
|
|
37833
|
+
* it missing.
|
|
37834
|
+
*
|
|
37835
|
+
* For a rework: the same tree, for the stronger reason that writing anywhere
|
|
37836
|
+
* else would strand the round on a second branch.
|
|
37837
|
+
*
|
|
37838
|
+
* Returns undefined when there is no single tree: no worktrees (isolation
|
|
37839
|
+
* disabled — the writers edited the parent tree directly), or several
|
|
37840
|
+
* distinct ones, in which case no single cwd is correct and the parent tree
|
|
37841
|
+
* is the honest default.
|
|
37842
|
+
*/
|
|
37843
|
+
inheritedWorktreeCwdFor(node, graph) {
|
|
37844
|
+
const paths = new Set(
|
|
37845
|
+
this.collectWorktreeSources(node, graph).map((s) => s.handle.path)
|
|
37846
|
+
);
|
|
37847
|
+
return paths.size === 1 ? [...paths][0] : void 0;
|
|
37848
|
+
}
|
|
37849
|
+
/**
|
|
37850
|
+
* Resolve the deferred worktrees produced behind a node's dependencies, in
|
|
37851
|
+
* ancestors-first order. Used to decide what a `merge` node must merge, and
|
|
37852
|
+
* which tree a `verify` node should actually inspect.
|
|
37853
|
+
*
|
|
37854
|
+
* A merge node's direct deps are NOT the writers: `buildGraphFromPlan`
|
|
37855
|
+
* injects a `verify` node after every `general` node and points the merge at
|
|
37856
|
+
* those verifies, while worktree handles are recorded against the writer
|
|
37857
|
+
* node ids. Looking only at direct deps therefore found nothing to merge and
|
|
37858
|
+
* silently reported success while every tentacle's work stayed stranded on
|
|
37859
|
+
* its branch. Walk up through non-writer deps until the writers are found.
|
|
37860
|
+
*
|
|
37861
|
+
* Post-order so a writer that depends on another writer merges after it (the
|
|
37862
|
+
* later branch was cut from a HEAD that already contained the earlier work).
|
|
37863
|
+
* `merge` nodes terminate the walk: another merge already owns its subtree.
|
|
37864
|
+
*/
|
|
37865
|
+
collectWorktreeSources(node, graph) {
|
|
37866
|
+
const out = [];
|
|
37867
|
+
const seen = /* @__PURE__ */ new Set();
|
|
37868
|
+
const visit = (id) => {
|
|
37869
|
+
if (seen.has(id)) return;
|
|
37870
|
+
seen.add(id);
|
|
37871
|
+
const n = graph.nodes.get(id);
|
|
37872
|
+
if (!n || n.kind === "merge") return;
|
|
37873
|
+
for (const dep of n.deps) visit(dep);
|
|
37874
|
+
const handle = this.nodeRunState.get(id)?.worktreeHandle;
|
|
37875
|
+
if (handle) out.push({ id, handle });
|
|
37876
|
+
};
|
|
37877
|
+
for (const depId of node.deps) visit(depId);
|
|
37878
|
+
return out;
|
|
37879
|
+
}
|
|
37880
|
+
/**
|
|
37881
|
+
* Sequentially merge every deferred worktree this node covers (in
|
|
37882
|
+
* ancestors-first order) into parent HEAD. Nodes without a recorded worktree
|
|
37883
|
+
* handle (worktree isolation disabled, or a read-only node) are a no-op. On
|
|
37884
|
+
* conflict the branch is kept and the conflict is surfaced in the merge
|
|
37885
|
+
* node's error — remaining sources still attempt to merge (independent
|
|
37886
|
+
* branches shouldn't be blocked by one conflict).
|
|
36141
37887
|
*/
|
|
36142
37888
|
async runMergeNode(node, graph) {
|
|
36143
37889
|
const conflicts = [];
|
|
36144
37890
|
const merged = [];
|
|
36145
|
-
for (const depId of node
|
|
36146
|
-
const state3 = this.nodeRunState.get(depId);
|
|
36147
|
-
const handle = state3?.worktreeHandle;
|
|
36148
|
-
if (!handle) continue;
|
|
37891
|
+
for (const { id: depId, handle } of this.collectWorktreeSources(node, graph)) {
|
|
36149
37892
|
let result;
|
|
36150
37893
|
try {
|
|
36151
37894
|
result = await this.mergeFn(handle, {
|
|
@@ -36163,6 +37906,7 @@ var init_executor = __esm({
|
|
|
36163
37906
|
if (!result.ok) {
|
|
36164
37907
|
conflicts.push(`${depId}: ${result.message}`);
|
|
36165
37908
|
} else {
|
|
37909
|
+
this.nodeRunState.set(depId, { worktreeHandle: null });
|
|
36166
37910
|
merged.push(depId);
|
|
36167
37911
|
}
|
|
36168
37912
|
}
|
|
@@ -36190,9 +37934,21 @@ var init_executor = __esm({
|
|
|
36190
37934
|
node.status = "done";
|
|
36191
37935
|
node.result = res.result;
|
|
36192
37936
|
this.radio("node_end", { description: node.label, agent: node.kind, ok: true });
|
|
37937
|
+
this.reconcileRepairedNode(graph, node);
|
|
37938
|
+
if (node.kind === "verify") this.applyVerifyVerdict(graph, node);
|
|
36193
37939
|
return;
|
|
36194
37940
|
}
|
|
36195
37941
|
node.error = res.error;
|
|
37942
|
+
if (this.aborted) {
|
|
37943
|
+
node.status = "error";
|
|
37944
|
+
this.radio("node_end", {
|
|
37945
|
+
description: node.label,
|
|
37946
|
+
agent: node.kind,
|
|
37947
|
+
detail: res.error,
|
|
37948
|
+
ok: false
|
|
37949
|
+
});
|
|
37950
|
+
return;
|
|
37951
|
+
}
|
|
36196
37952
|
if (res.cancelled === false) {
|
|
36197
37953
|
node.status = "error";
|
|
36198
37954
|
this.radio("node_end", {
|
|
@@ -36235,6 +37991,181 @@ var init_executor = __esm({
|
|
|
36235
37991
|
ok: false
|
|
36236
37992
|
});
|
|
36237
37993
|
}
|
|
37994
|
+
/**
|
|
37995
|
+
* A `fix` node just completed the work its failed predecessor could not.
|
|
37996
|
+
* That unit of work IS done — but the predecessor was left terminally
|
|
37997
|
+
* `error`, and since `isConverged` requires every node to be `done`/
|
|
37998
|
+
* `skipped`, a fully repaired graph reported "did not converge" and listed
|
|
37999
|
+
* the repaired node under `failedNodeIds`. The cross-run snapshot then told
|
|
38000
|
+
* the next planner to redo work the fix had already completed.
|
|
38001
|
+
*
|
|
38002
|
+
* Marking it `done` has no scheduling effect (dependents were re-pointed at
|
|
38003
|
+
* the fix when it was spawned) — it is purely how the run is reported. The
|
|
38004
|
+
* original failure stays visible as the separate `fix: …` node and in the
|
|
38005
|
+
* repaired node's result line.
|
|
38006
|
+
*/
|
|
38007
|
+
reconcileRepairedNode(graph, fixNode) {
|
|
38008
|
+
const failedId = this.repairs.get(fixNode.id);
|
|
38009
|
+
if (!failedId) return;
|
|
38010
|
+
const failed = graph.nodes.get(failedId);
|
|
38011
|
+
if (!failed || failed.status !== "error") return;
|
|
38012
|
+
const original = failed.error ? ` (original failure: ${failed.error})` : "";
|
|
38013
|
+
failed.status = "done";
|
|
38014
|
+
failed.result = `repaired by "${fixNode.label}"${original}${fixNode.result ? `: ${fixNode.result}` : ""}`;
|
|
38015
|
+
failed.error = void 0;
|
|
38016
|
+
this.radio("node_end", {
|
|
38017
|
+
description: failed.label,
|
|
38018
|
+
agent: failed.kind,
|
|
38019
|
+
detail: `repaired by ${fixNode.id}`,
|
|
38020
|
+
ok: true
|
|
38021
|
+
});
|
|
38022
|
+
}
|
|
38023
|
+
/**
|
|
38024
|
+
* Act on what a completed `verify` node concluded.
|
|
38025
|
+
*
|
|
38026
|
+
* The verify itself stays `done` either way — it did its job, and doing it
|
|
38027
|
+
* well means being free to say "no". A FAIL instead sends the WRITER back
|
|
38028
|
+
* through a bounded rework round.
|
|
38029
|
+
*
|
|
38030
|
+
* Without this the verdict text was never read: a verify that reported the
|
|
38031
|
+
* work as wrong was recorded exactly like one that reported it correct, the
|
|
38032
|
+
* graph converged over the defect, and the only iteration the engine could
|
|
38033
|
+
* do was on execution failure. An `unknown` verdict (no parseable trailer)
|
|
38034
|
+
* is deliberately non-blocking — a prompt drift must not be able to wedge
|
|
38035
|
+
* every graph — but it is recorded, because a gate that has silently stopped
|
|
38036
|
+
* working is worse than no gate.
|
|
38037
|
+
*/
|
|
38038
|
+
applyVerifyVerdict(graph, verify) {
|
|
38039
|
+
const { verdict, findings } = parseVerifyVerdict(verify.result);
|
|
38040
|
+
if (verdict === "pass") return;
|
|
38041
|
+
const writer = this.writerBehind(verify, graph);
|
|
38042
|
+
if (!writer) return;
|
|
38043
|
+
void this.maybeRunWeaknessMeter(verify, verdict);
|
|
38044
|
+
if (verdict === "unknown") {
|
|
38045
|
+
this.unresolved.push({
|
|
38046
|
+
nodeId: writer.id,
|
|
38047
|
+
label: writer.label,
|
|
38048
|
+
reason: "unknown",
|
|
38049
|
+
findings: findings || "(verify produced no parseable VERDICT line)"
|
|
38050
|
+
});
|
|
38051
|
+
return;
|
|
38052
|
+
}
|
|
38053
|
+
const root = this.reviewLineage.get(writer.id) ?? writer.id;
|
|
38054
|
+
const spent = this.reviewRounds.get(root) ?? 0;
|
|
38055
|
+
if (this.aborted || spent >= this.maxReviewRounds) {
|
|
38056
|
+
this.unresolved.push({
|
|
38057
|
+
nodeId: writer.id,
|
|
38058
|
+
label: writer.label,
|
|
38059
|
+
reason: "fail",
|
|
38060
|
+
findings
|
|
38061
|
+
});
|
|
38062
|
+
writer.result = `${writer.result ?? ""}
|
|
38063
|
+
|
|
38064
|
+
[accepted with unresolved verify findings from ` + `"${verify.label}"]${findings ? `: ${firstLine(findings)}` : ""}`.trim();
|
|
38065
|
+
this.radio("node_end", {
|
|
38066
|
+
description: writer.label,
|
|
38067
|
+
agent: writer.kind,
|
|
38068
|
+
detail: this.aborted ? "verify FAIL left unresolved (run cancelled)" : `verify FAIL left unresolved (rework budget ${this.maxReviewRounds} spent)`,
|
|
38069
|
+
ok: false
|
|
38070
|
+
});
|
|
38071
|
+
return;
|
|
38072
|
+
}
|
|
38073
|
+
this.reviewRounds.set(root, spent + 1);
|
|
38074
|
+
this.spawnReworkPair(graph, writer, verify, findings, root, spent + 1);
|
|
38075
|
+
}
|
|
38076
|
+
/**
|
|
38077
|
+
* The writer whose work a `verify` node judged.
|
|
38078
|
+
*
|
|
38079
|
+
* Walks up through non-writer deps, the same shape `collectWorktreeSources`
|
|
38080
|
+
* relies on: a verify's dep is normally its writer directly, but after a
|
|
38081
|
+
* rework round the chain is writer → verify → rework → verify, and the
|
|
38082
|
+
* rework (a `fix` node) is itself the writer to send back.
|
|
38083
|
+
*/
|
|
38084
|
+
writerBehind(verify, graph) {
|
|
38085
|
+
const seen = /* @__PURE__ */ new Set();
|
|
38086
|
+
const visit = (id) => {
|
|
38087
|
+
if (seen.has(id)) return void 0;
|
|
38088
|
+
seen.add(id);
|
|
38089
|
+
const n = graph.nodes.get(id);
|
|
38090
|
+
if (!n || n.kind === "merge") return void 0;
|
|
38091
|
+
if (n.kind === "general" || n.kind === "fix") return n;
|
|
38092
|
+
for (const dep of n.deps) {
|
|
38093
|
+
const found = visit(dep);
|
|
38094
|
+
if (found) return found;
|
|
38095
|
+
}
|
|
38096
|
+
return void 0;
|
|
38097
|
+
};
|
|
38098
|
+
for (const depId of verify.deps) {
|
|
38099
|
+
const found = visit(depId);
|
|
38100
|
+
if (found) return found;
|
|
38101
|
+
}
|
|
38102
|
+
return void 0;
|
|
38103
|
+
}
|
|
38104
|
+
/**
|
|
38105
|
+
* Send a writer's work back for one more round: a rework node carrying the
|
|
38106
|
+
* verify's findings, plus a fresh verify to judge the result.
|
|
38107
|
+
*
|
|
38108
|
+
* The rework runs INSIDE the writer's worktree instead of creating one of
|
|
38109
|
+
* its own. Two worktrees for one scope means two branches, and the merge
|
|
38110
|
+
* node walks up to the writer — so a rework on its own branch would be
|
|
38111
|
+
* merged never or twice, exactly the stranded-work failure the merge fix
|
|
38112
|
+
* addressed. `allowWorktree: false` on this node's deps suppresses creation,
|
|
38113
|
+
* and `cwdOverride` (resolved via {@link inheritedWorktreeCwdFor}) points it
|
|
38114
|
+
* at the existing tree; the handle stays registered against the writer.
|
|
38115
|
+
*
|
|
38116
|
+
* Acyclicity is preserved by construction: both new nodes point only at
|
|
38117
|
+
* nodes that already exist, and the rewiring moves an existing edge forward
|
|
38118
|
+
* along the chain rather than back into it.
|
|
38119
|
+
*/
|
|
38120
|
+
spawnReworkPair(graph, writer, verify, findings, root, round) {
|
|
38121
|
+
const reworkId = `rework-${root}-${round}`;
|
|
38122
|
+
const reworkNode = {
|
|
38123
|
+
id: reworkId,
|
|
38124
|
+
kind: "fix",
|
|
38125
|
+
label: `rework: ${writer.label}`,
|
|
38126
|
+
prompt: `A reviewer inspected this work on disk and REJECTED it. Address every finding below, then leave the work in a state that satisfies the original task.
|
|
38127
|
+
|
|
38128
|
+
## Original task
|
|
38129
|
+
${writer.prompt}
|
|
38130
|
+
|
|
38131
|
+
## Reviewer findings (these are what must change)
|
|
38132
|
+
${findings || "(the reviewer reported FAIL without detail)"}`,
|
|
38133
|
+
...writer.scope ? { scope: writer.scope } : {},
|
|
38134
|
+
...writer.acceptance ? { acceptance: writer.acceptance } : {},
|
|
38135
|
+
// The verify is already `done`, so the rework is immediately ready.
|
|
38136
|
+
deps: [verify.id],
|
|
38137
|
+
status: "pending",
|
|
38138
|
+
retryCount: 0,
|
|
38139
|
+
maxRetries: 0
|
|
38140
|
+
};
|
|
38141
|
+
graph.nodes.set(reworkId, reworkNode);
|
|
38142
|
+
this.reworks.set(reworkId, writer.id);
|
|
38143
|
+
this.reviewLineage.set(reworkId, root);
|
|
38144
|
+
const reVerifyId = `verify-${reworkId}`;
|
|
38145
|
+
const reVerifyNode = {
|
|
38146
|
+
id: reVerifyId,
|
|
38147
|
+
kind: "verify",
|
|
38148
|
+
label: `verify: ${writer.label} (rework ${round})`,
|
|
38149
|
+
prompt: verify.prompt,
|
|
38150
|
+
deps: [reworkId],
|
|
38151
|
+
status: "pending",
|
|
38152
|
+
retryCount: 0,
|
|
38153
|
+
maxRetries: verify.maxRetries
|
|
38154
|
+
};
|
|
38155
|
+
graph.nodes.set(reVerifyId, reVerifyNode);
|
|
38156
|
+
for (const other of graph.nodes.values()) {
|
|
38157
|
+
if (other.id === reworkId || other.id === reVerifyId) continue;
|
|
38158
|
+
if (other.deps.includes(verify.id)) {
|
|
38159
|
+
other.deps = other.deps.map((d) => d === verify.id ? reVerifyId : d);
|
|
38160
|
+
}
|
|
38161
|
+
}
|
|
38162
|
+
this.radio("node_fix", {
|
|
38163
|
+
description: reworkNode.label,
|
|
38164
|
+
agent: "fix",
|
|
38165
|
+
detail: `verify FAIL on "${writer.label}" \u2014 rework round ${round}/${this.maxReviewRounds}`,
|
|
38166
|
+
ok: false
|
|
38167
|
+
});
|
|
38168
|
+
}
|
|
36238
38169
|
/**
|
|
36239
38170
|
* Create a `fix` node that attempts to redo the failed node's work, wired
|
|
36240
38171
|
* so downstream dependents of the failed node also wait on the fix.
|
|
@@ -36261,6 +38192,7 @@ ${failed.error ?? "unknown error"}`,
|
|
|
36261
38192
|
// no further retries — one fix attempt per failed node in v1
|
|
36262
38193
|
};
|
|
36263
38194
|
graph.nodes.set(fixId, fixNode);
|
|
38195
|
+
this.repairs.set(fixId, failed.id);
|
|
36264
38196
|
for (const other of graph.nodes.values()) {
|
|
36265
38197
|
if (other.id === fixId) continue;
|
|
36266
38198
|
if (other.deps.includes(failed.id)) {
|
|
@@ -36303,6 +38235,41 @@ ${failed.error ?? "unknown error"}`,
|
|
|
36303
38235
|
...fields.ok !== void 0 ? { ok: fields.ok } : {}
|
|
36304
38236
|
});
|
|
36305
38237
|
}
|
|
38238
|
+
/**
|
|
38239
|
+
* Bennett's Razor meter (Slice L/N+3 wiring): when the env flag is
|
|
38240
|
+
* set, fire a non-blocking LLM call to refine the persona verdict's
|
|
38241
|
+
* weakness score. The local heuristic already produced a score in
|
|
38242
|
+
* `parsePersonaVerdict`; the meter just refines it. Results land in
|
|
38243
|
+
* the radio stream as a `node_meter` event so the desktop / tail
|
|
38244
|
+
* can surface the distinction between a "tightly asserted" PASS
|
|
38245
|
+
* (specificity > 0.6) and a "loosely claimed" one (specificity < 0.3).
|
|
38246
|
+
*
|
|
38247
|
+
* No-op when:
|
|
38248
|
+
* - the meter is disabled (default)
|
|
38249
|
+
* - the result text is empty
|
|
38250
|
+
* - the meter call fails (silent: the local score is good enough)
|
|
38251
|
+
*
|
|
38252
|
+
* @since v1.31.x
|
|
38253
|
+
*/
|
|
38254
|
+
async maybeRunWeaknessMeter(verify, verdict) {
|
|
38255
|
+
const text = typeof verify.result === "string" ? verify.result : "";
|
|
38256
|
+
if (text.length === 0) return;
|
|
38257
|
+
let meter;
|
|
38258
|
+
try {
|
|
38259
|
+
({ measureWeaknessViaLLM: meter } = await Promise.resolve().then(() => (init_weaknessMeter(), weaknessMeter_exports)));
|
|
38260
|
+
} catch {
|
|
38261
|
+
return;
|
|
38262
|
+
}
|
|
38263
|
+
if (!meter) return;
|
|
38264
|
+
const outcome = await meter(text);
|
|
38265
|
+
if (!outcome) return;
|
|
38266
|
+
this.radio("node_meter", {
|
|
38267
|
+
description: `meter: ${verify.id}`,
|
|
38268
|
+
agent: "weakness-meter",
|
|
38269
|
+
detail: `v=${verdict} specificity=${outcome.meter.specificity.toFixed(2)} weakness=${outcome.weakness.toFixed(2)} model=${outcome.model} dur=${outcome.durationMs}ms assumptions=${outcome.meter.assumptions.length}`,
|
|
38270
|
+
ok: true
|
|
38271
|
+
});
|
|
38272
|
+
}
|
|
36306
38273
|
};
|
|
36307
38274
|
}
|
|
36308
38275
|
});
|
|
@@ -36691,10 +38658,10 @@ var init_prereqChecks = __esm({
|
|
|
36691
38658
|
|
|
36692
38659
|
// src/cli/plugins/prefs.ts
|
|
36693
38660
|
import { existsSync as existsSync36, readFileSync as readFileSync29, writeFileSync as writeFileSync18, mkdirSync as mkdirSync15 } from "node:fs";
|
|
36694
|
-
import
|
|
38661
|
+
import path38 from "node:path";
|
|
36695
38662
|
import os9 from "node:os";
|
|
36696
38663
|
function getPluginPrefsPath() {
|
|
36697
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
38664
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path38.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
36698
38665
|
}
|
|
36699
38666
|
function getPluginPrefs() {
|
|
36700
38667
|
const file2 = getPluginPrefsPath();
|
|
@@ -36715,7 +38682,7 @@ function getPluginPrefs() {
|
|
|
36715
38682
|
}
|
|
36716
38683
|
function writePluginPrefs(prefs) {
|
|
36717
38684
|
const file2 = getPluginPrefsPath();
|
|
36718
|
-
mkdirSync15(
|
|
38685
|
+
mkdirSync15(path38.dirname(file2), { recursive: true });
|
|
36719
38686
|
writeFileSync18(file2, JSON.stringify(prefs, null, 2), {
|
|
36720
38687
|
encoding: "utf-8",
|
|
36721
38688
|
mode: 384
|
|
@@ -36752,7 +38719,7 @@ __export(registry_exports, {
|
|
|
36752
38719
|
isBinaryOnPath: () => isBinaryOnPath
|
|
36753
38720
|
});
|
|
36754
38721
|
import { existsSync as existsSync37 } from "node:fs";
|
|
36755
|
-
import
|
|
38722
|
+
import path39 from "node:path";
|
|
36756
38723
|
function detectLocalBin(bin) {
|
|
36757
38724
|
return (cwd) => {
|
|
36758
38725
|
try {
|
|
@@ -36770,7 +38737,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
36770
38737
|
const platform = opts.platform ?? process.platform;
|
|
36771
38738
|
const exists = opts.exists ?? existsSync37;
|
|
36772
38739
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
36773
|
-
const pathMod = platform === "win32" ?
|
|
38740
|
+
const pathMod = platform === "win32" ? path39.win32 : path39.posix;
|
|
36774
38741
|
const sep2 = platform === "win32" ? ";" : ":";
|
|
36775
38742
|
const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
|
|
36776
38743
|
const candidates = [bin];
|
|
@@ -36839,7 +38806,7 @@ function findPlugin(id) {
|
|
|
36839
38806
|
return PLUGINS.find((p3) => p3.id === id);
|
|
36840
38807
|
}
|
|
36841
38808
|
var PLUGINS;
|
|
36842
|
-
var
|
|
38809
|
+
var init_registry3 = __esm({
|
|
36843
38810
|
"src/cli/plugins/registry.ts"() {
|
|
36844
38811
|
"use strict";
|
|
36845
38812
|
init_engine();
|
|
@@ -36908,7 +38875,7 @@ __export(atMentions_exports, {
|
|
|
36908
38875
|
hasAtMentions: () => hasAtMentions
|
|
36909
38876
|
});
|
|
36910
38877
|
import { existsSync as existsSync40, readFileSync as readFileSync31, statSync as statSync6 } from "node:fs";
|
|
36911
|
-
import { isAbsolute, relative as relative3, resolve, sep } from "node:path";
|
|
38878
|
+
import { isAbsolute as isAbsolute2, relative as relative3, resolve, sep } from "node:path";
|
|
36912
38879
|
function isProbablyText(name, head) {
|
|
36913
38880
|
if (/\.(txt|md|markdown|json|jsonc|ts|tsx|js|jsx|mjs|cjs|css|scss|html|htm|xml|yml|yaml|toml|ini|cfg|conf|rs|go|py|java|kt|swift|c|cc|cpp|h|hpp|cs|rb|php|sh|bash|zsh|ps1|sql|graphql|env|gitignore|dockerfile|makefile|cmake|lock|svg)$/i.test(
|
|
36914
38881
|
name
|
|
@@ -36939,8 +38906,8 @@ function extractAtMentions(text) {
|
|
|
36939
38906
|
return out;
|
|
36940
38907
|
}
|
|
36941
38908
|
function resolveMention(token, cwd) {
|
|
36942
|
-
const abs =
|
|
36943
|
-
if (!underRoot(abs, cwd) && !
|
|
38909
|
+
const abs = isAbsolute2(token) ? resolve(token) : resolve(cwd, token);
|
|
38910
|
+
if (!underRoot(abs, cwd) && !isAbsolute2(token)) {
|
|
36944
38911
|
if (!underRoot(abs, cwd)) {
|
|
36945
38912
|
return {
|
|
36946
38913
|
raw: token,
|
|
@@ -36951,7 +38918,7 @@ function resolveMention(token, cwd) {
|
|
|
36951
38918
|
};
|
|
36952
38919
|
}
|
|
36953
38920
|
}
|
|
36954
|
-
if (
|
|
38921
|
+
if (isAbsolute2(token) && !underRoot(abs, cwd)) {
|
|
36955
38922
|
return {
|
|
36956
38923
|
raw: token,
|
|
36957
38924
|
path: token,
|
|
@@ -37079,10 +39046,10 @@ __export(triggerLock_exports, {
|
|
|
37079
39046
|
lockPath: () => lockPath,
|
|
37080
39047
|
releaseLock: () => releaseLock
|
|
37081
39048
|
});
|
|
37082
|
-
import { promises as
|
|
37083
|
-
import * as
|
|
39049
|
+
import { promises as fs27 } from "node:fs";
|
|
39050
|
+
import * as path44 from "node:path";
|
|
37084
39051
|
function lockPath(projectRoot) {
|
|
37085
|
-
return
|
|
39052
|
+
return path44.join(projectRoot, ".zelari", "trigger.lock");
|
|
37086
39053
|
}
|
|
37087
39054
|
function isPidAlive(pid) {
|
|
37088
39055
|
try {
|
|
@@ -37095,10 +39062,10 @@ function isPidAlive(pid) {
|
|
|
37095
39062
|
}
|
|
37096
39063
|
async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
|
|
37097
39064
|
const lp = lockPath(projectRoot);
|
|
37098
|
-
const dir =
|
|
37099
|
-
await
|
|
39065
|
+
const dir = path44.dirname(lp);
|
|
39066
|
+
await fs27.mkdir(dir, { recursive: true });
|
|
37100
39067
|
try {
|
|
37101
|
-
const raw = await
|
|
39068
|
+
const raw = await fs27.readFile(lp, "utf8");
|
|
37102
39069
|
const existing = JSON.parse(raw);
|
|
37103
39070
|
if (existing.pid && isPidAlive(existing.pid)) {
|
|
37104
39071
|
return { acquired: false, heldBy: existing.pid, lockPath: lp };
|
|
@@ -37109,13 +39076,13 @@ async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date())
|
|
|
37109
39076
|
pid: process.pid,
|
|
37110
39077
|
acquiredAt: now().toISOString()
|
|
37111
39078
|
};
|
|
37112
|
-
await
|
|
39079
|
+
await fs27.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
37113
39080
|
return { acquired: true, lockPath: lp };
|
|
37114
39081
|
}
|
|
37115
39082
|
async function releaseLock(projectRoot) {
|
|
37116
39083
|
const lp = lockPath(projectRoot);
|
|
37117
39084
|
try {
|
|
37118
|
-
await
|
|
39085
|
+
await fs27.unlink(lp);
|
|
37119
39086
|
} catch {
|
|
37120
39087
|
}
|
|
37121
39088
|
}
|
|
@@ -37386,7 +39353,7 @@ import {
|
|
|
37386
39353
|
} from "node:fs";
|
|
37387
39354
|
import { join as join35 } from "node:path";
|
|
37388
39355
|
import { homedir as homedir11 } from "node:os";
|
|
37389
|
-
import { createHash as createHash6, randomBytes as
|
|
39356
|
+
import { createHash as createHash6, randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
|
|
37390
39357
|
function getZelariHome() {
|
|
37391
39358
|
return join35(homedir11(), ".zelari-code");
|
|
37392
39359
|
}
|
|
@@ -37403,12 +39370,12 @@ function ensureHome() {
|
|
|
37403
39370
|
}
|
|
37404
39371
|
}
|
|
37405
39372
|
function loadCompanionConfig() {
|
|
37406
|
-
const
|
|
37407
|
-
if (!existsSync43(
|
|
39373
|
+
const path47 = getCompanionConfigPath();
|
|
39374
|
+
if (!existsSync43(path47)) {
|
|
37408
39375
|
return { projects: [] };
|
|
37409
39376
|
}
|
|
37410
39377
|
try {
|
|
37411
|
-
const raw = JSON.parse(readFileSync34(
|
|
39378
|
+
const raw = JSON.parse(readFileSync34(path47, "utf8"));
|
|
37412
39379
|
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
37413
39380
|
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
37414
39381
|
).map((p3) => ({
|
|
@@ -37446,16 +39413,16 @@ function loadOrCreateToken(explicit) {
|
|
|
37446
39413
|
return { token: explicit.trim(), created: false };
|
|
37447
39414
|
}
|
|
37448
39415
|
ensureHome();
|
|
37449
|
-
const
|
|
37450
|
-
if (existsSync43(
|
|
37451
|
-
const t = readFileSync34(
|
|
39416
|
+
const path47 = getCompanionTokenPath();
|
|
39417
|
+
if (existsSync43(path47)) {
|
|
39418
|
+
const t = readFileSync34(path47, "utf8").trim();
|
|
37452
39419
|
if (t) return { token: t, created: false };
|
|
37453
39420
|
}
|
|
37454
|
-
const token =
|
|
37455
|
-
writeFileSync21(
|
|
39421
|
+
const token = randomBytes4(24).toString("base64url");
|
|
39422
|
+
writeFileSync21(path47, token + "\n", "utf8");
|
|
37456
39423
|
try {
|
|
37457
|
-
const
|
|
37458
|
-
|
|
39424
|
+
const fs29 = __require("node:fs");
|
|
39425
|
+
fs29.chmodSync?.(path47, 384);
|
|
37459
39426
|
} catch {
|
|
37460
39427
|
}
|
|
37461
39428
|
return { token, created: true };
|
|
@@ -37480,17 +39447,17 @@ function mergeProjects(cfg, extraPaths) {
|
|
|
37480
39447
|
byId.set(p3.id, p3);
|
|
37481
39448
|
}
|
|
37482
39449
|
for (const raw of extraPaths) {
|
|
37483
|
-
const
|
|
37484
|
-
if (!
|
|
37485
|
-
let id = slugFromPath(
|
|
39450
|
+
const path47 = raw.trim();
|
|
39451
|
+
if (!path47) continue;
|
|
39452
|
+
let id = slugFromPath(path47);
|
|
37486
39453
|
let n = 2;
|
|
37487
|
-
while (byId.has(id) && byId.get(id).path !==
|
|
37488
|
-
id = `${slugFromPath(
|
|
39454
|
+
while (byId.has(id) && byId.get(id).path !== path47) {
|
|
39455
|
+
id = `${slugFromPath(path47)}-${n++}`;
|
|
37489
39456
|
}
|
|
37490
39457
|
byId.set(id, {
|
|
37491
39458
|
id,
|
|
37492
|
-
name: slugFromPath(
|
|
37493
|
-
path:
|
|
39459
|
+
name: slugFromPath(path47),
|
|
39460
|
+
path: path47
|
|
37494
39461
|
});
|
|
37495
39462
|
}
|
|
37496
39463
|
return [...byId.values()];
|
|
@@ -37540,7 +39507,7 @@ var init_config = __esm({
|
|
|
37540
39507
|
// src/cli/companion/runManager.ts
|
|
37541
39508
|
import { spawn as spawn11 } from "node:child_process";
|
|
37542
39509
|
import { createInterface } from "node:readline";
|
|
37543
|
-
import { randomUUID as
|
|
39510
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
37544
39511
|
import { writeFileSync as writeFileSync22, unlinkSync as unlinkSync2 } from "node:fs";
|
|
37545
39512
|
import { join as join36 } from "node:path";
|
|
37546
39513
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
@@ -37609,7 +39576,7 @@ var init_runManager = __esm({
|
|
|
37609
39576
|
}
|
|
37610
39577
|
const prompt = args.prompt?.trim();
|
|
37611
39578
|
if (!prompt) return { ok: false, error: "prompt is required" };
|
|
37612
|
-
const id =
|
|
39579
|
+
const id = randomUUID7();
|
|
37613
39580
|
const mode = (args.mode || "kraken").toLowerCase();
|
|
37614
39581
|
const phase2 = (args.phase || "build").toLowerCase();
|
|
37615
39582
|
const run = {
|
|
@@ -37867,9 +39834,9 @@ async function runCompanionServe(opts = {}) {
|
|
|
37867
39834
|
return;
|
|
37868
39835
|
}
|
|
37869
39836
|
const url2 = parseUrl(req);
|
|
37870
|
-
const
|
|
39837
|
+
const path47 = url2.pathname.replace(/\/+$/, "") || "/";
|
|
37871
39838
|
try {
|
|
37872
|
-
if (req.method === "GET" && (
|
|
39839
|
+
if (req.method === "GET" && (path47 === "/health" || path47 === "/v1/health")) {
|
|
37873
39840
|
sendJson(res, 200, {
|
|
37874
39841
|
ok: true,
|
|
37875
39842
|
service: "zelari-companion",
|
|
@@ -37881,18 +39848,18 @@ async function runCompanionServe(opts = {}) {
|
|
|
37881
39848
|
});
|
|
37882
39849
|
return;
|
|
37883
39850
|
}
|
|
37884
|
-
if (
|
|
39851
|
+
if (path47.startsWith("/v1")) {
|
|
37885
39852
|
if (!tokenMatches(token, getBearer(req))) {
|
|
37886
39853
|
sendJson(res, 401, { ok: false, error: "unauthorized" });
|
|
37887
39854
|
return;
|
|
37888
39855
|
}
|
|
37889
39856
|
}
|
|
37890
|
-
if (req.method === "GET" &&
|
|
39857
|
+
if (req.method === "GET" && path47 === "/v1/config") {
|
|
37891
39858
|
const snap = buildDesktopConfigSnapshot();
|
|
37892
39859
|
sendJson(res, 200, { ok: true, ...snap });
|
|
37893
39860
|
return;
|
|
37894
39861
|
}
|
|
37895
|
-
if (req.method === "GET" &&
|
|
39862
|
+
if (req.method === "GET" && path47 === "/v1/projects") {
|
|
37896
39863
|
sendJson(res, 200, {
|
|
37897
39864
|
ok: true,
|
|
37898
39865
|
projects: projects.map((p3) => ({
|
|
@@ -37903,7 +39870,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
37903
39870
|
});
|
|
37904
39871
|
return;
|
|
37905
39872
|
}
|
|
37906
|
-
if (req.method === "GET" &&
|
|
39873
|
+
if (req.method === "GET" && path47 === "/v1/runs") {
|
|
37907
39874
|
sendJson(res, 200, {
|
|
37908
39875
|
ok: true,
|
|
37909
39876
|
active: runs.getActive(),
|
|
@@ -37921,7 +39888,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
37921
39888
|
});
|
|
37922
39889
|
return;
|
|
37923
39890
|
}
|
|
37924
|
-
if (req.method === "POST" &&
|
|
39891
|
+
if (req.method === "POST" && path47 === "/v1/runs") {
|
|
37925
39892
|
const raw = await readBody(req);
|
|
37926
39893
|
let body = {};
|
|
37927
39894
|
try {
|
|
@@ -37968,7 +39935,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
37968
39935
|
});
|
|
37969
39936
|
return;
|
|
37970
39937
|
}
|
|
37971
|
-
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(
|
|
39938
|
+
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path47);
|
|
37972
39939
|
if (req.method === "GET" && eventsMatch) {
|
|
37973
39940
|
const runId = eventsMatch[1];
|
|
37974
39941
|
const run = runs.getRun(runId);
|
|
@@ -38033,7 +40000,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
38033
40000
|
}, 500);
|
|
38034
40001
|
return;
|
|
38035
40002
|
}
|
|
38036
|
-
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(
|
|
40003
|
+
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path47);
|
|
38037
40004
|
if (req.method === "POST" && cancelMatch) {
|
|
38038
40005
|
const runId = cancelMatch[1];
|
|
38039
40006
|
const result = runs.cancel(runId);
|
|
@@ -38145,11 +40112,11 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
38145
40112
|
import { existsSync as existsSync45, readFileSync as readFileSync35, readlinkSync, statSync as statSync7 } from "node:fs";
|
|
38146
40113
|
import { createRequire as createRequire3 } from "node:module";
|
|
38147
40114
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
38148
|
-
import
|
|
40115
|
+
import path46 from "node:path";
|
|
38149
40116
|
function findPackageRoot(start) {
|
|
38150
40117
|
let dir = start;
|
|
38151
40118
|
for (let i = 0; i < 6; i += 1) {
|
|
38152
|
-
const candidate =
|
|
40119
|
+
const candidate = path46.join(dir, "package.json");
|
|
38153
40120
|
if (existsSync45(candidate)) {
|
|
38154
40121
|
try {
|
|
38155
40122
|
const pkg = JSON.parse(readFileSync35(candidate, "utf8"));
|
|
@@ -38157,11 +40124,11 @@ function findPackageRoot(start) {
|
|
|
38157
40124
|
} catch {
|
|
38158
40125
|
}
|
|
38159
40126
|
}
|
|
38160
|
-
const parent =
|
|
40127
|
+
const parent = path46.dirname(dir);
|
|
38161
40128
|
if (parent === dir) break;
|
|
38162
40129
|
dir = parent;
|
|
38163
40130
|
}
|
|
38164
|
-
return
|
|
40131
|
+
return path46.resolve(__dirname3, "..", "..", "..");
|
|
38165
40132
|
}
|
|
38166
40133
|
function tryExec(cmd) {
|
|
38167
40134
|
try {
|
|
@@ -38175,7 +40142,7 @@ function tryExec(cmd) {
|
|
|
38175
40142
|
}
|
|
38176
40143
|
function readPackageJson3() {
|
|
38177
40144
|
try {
|
|
38178
|
-
const pkgPath =
|
|
40145
|
+
const pkgPath = path46.join(packageRoot, "package.json");
|
|
38179
40146
|
return JSON.parse(readFileSync35(pkgPath, "utf8"));
|
|
38180
40147
|
} catch {
|
|
38181
40148
|
return null;
|
|
@@ -38191,7 +40158,7 @@ function checkShim(pkgName) {
|
|
|
38191
40158
|
}
|
|
38192
40159
|
const isWin = process.platform === "win32";
|
|
38193
40160
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
38194
|
-
const shimPath =
|
|
40161
|
+
const shimPath = path46.join(prefix, shimName);
|
|
38195
40162
|
if (!existsSync45(shimPath)) {
|
|
38196
40163
|
return FAIL(
|
|
38197
40164
|
`shim not found at ${shimPath}
|
|
@@ -38219,8 +40186,8 @@ function checkShim(pkgName) {
|
|
|
38219
40186
|
fix: npm install -g ${pkgName}@latest --force`
|
|
38220
40187
|
);
|
|
38221
40188
|
}
|
|
38222
|
-
const resolved =
|
|
38223
|
-
const expected =
|
|
40189
|
+
const resolved = path46.resolve(path46.dirname(shimPath), target);
|
|
40190
|
+
const expected = path46.join(
|
|
38224
40191
|
prefix,
|
|
38225
40192
|
"node_modules",
|
|
38226
40193
|
pkgName,
|
|
@@ -38259,7 +40226,7 @@ function checkNode(pkg) {
|
|
|
38259
40226
|
return OK(`node ${raw}`);
|
|
38260
40227
|
}
|
|
38261
40228
|
function checkBundle() {
|
|
38262
|
-
const bundle =
|
|
40229
|
+
const bundle = path46.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
38263
40230
|
if (!existsSync45(bundle)) {
|
|
38264
40231
|
return FAIL(
|
|
38265
40232
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
@@ -38280,7 +40247,7 @@ function checkRuntimeDeps() {
|
|
|
38280
40247
|
const missing = [];
|
|
38281
40248
|
for (const dep of required2) {
|
|
38282
40249
|
try {
|
|
38283
|
-
const localReq = createRequire3(
|
|
40250
|
+
const localReq = createRequire3(path46.join(packageRoot, "package.json"));
|
|
38284
40251
|
localReq.resolve(dep);
|
|
38285
40252
|
} catch {
|
|
38286
40253
|
missing.push(dep);
|
|
@@ -38357,7 +40324,7 @@ function prereqToCheckResult(r) {
|
|
|
38357
40324
|
return r.severity === "critical" ? FAIL(r.message, "critical") : WARN(r.message);
|
|
38358
40325
|
}
|
|
38359
40326
|
async function checkOptionalPlugins() {
|
|
38360
|
-
const { detectMissingPlugins: detectMissingPlugins2 } = await Promise.resolve().then(() => (
|
|
40327
|
+
const { detectMissingPlugins: detectMissingPlugins2 } = await Promise.resolve().then(() => (init_registry3(), registry_exports));
|
|
38361
40328
|
let missing;
|
|
38362
40329
|
try {
|
|
38363
40330
|
missing = await detectMissingPlugins2(packageRoot, { includeMuted: true });
|
|
@@ -38456,7 +40423,7 @@ var init_doctor = __esm({
|
|
|
38456
40423
|
"use strict";
|
|
38457
40424
|
init_prereqChecks();
|
|
38458
40425
|
require3 = createRequire3(import.meta.url);
|
|
38459
|
-
__dirname3 =
|
|
40426
|
+
__dirname3 = path46.dirname(fileURLToPath2(import.meta.url));
|
|
38460
40427
|
packageRoot = findPackageRoot(__dirname3);
|
|
38461
40428
|
OK = (message) => ({
|
|
38462
40429
|
ok: true,
|
|
@@ -38696,9 +40663,9 @@ function tryParseJson(s) {
|
|
|
38696
40663
|
}
|
|
38697
40664
|
}
|
|
38698
40665
|
function truncateLines(lines) {
|
|
38699
|
-
const
|
|
38700
|
-
if (lines.length <=
|
|
38701
|
-
return [...lines.slice(0,
|
|
40666
|
+
const cap3 = toolOutputLineCap();
|
|
40667
|
+
if (lines.length <= cap3) return lines;
|
|
40668
|
+
return [...lines.slice(0, cap3), `\u2026 (+${lines.length - cap3} lines)`];
|
|
38702
40669
|
}
|
|
38703
40670
|
function formatBytes(n) {
|
|
38704
40671
|
if (n >= 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
@@ -38886,7 +40853,7 @@ function rel(p3) {
|
|
|
38886
40853
|
}
|
|
38887
40854
|
}
|
|
38888
40855
|
function formatToolSummary(toolName, args, maxWidth) {
|
|
38889
|
-
const
|
|
40856
|
+
const cap3 = maxWidth && maxWidth > 10 ? maxWidth : 100;
|
|
38890
40857
|
const lower = toolName.toLowerCase();
|
|
38891
40858
|
const a = args && typeof args === "object" ? args : {};
|
|
38892
40859
|
let summary;
|
|
@@ -38909,7 +40876,7 @@ function formatToolSummary(toolName, args, maxWidth) {
|
|
|
38909
40876
|
const json2 = JSON.stringify(args) ?? "";
|
|
38910
40877
|
summary = json2;
|
|
38911
40878
|
}
|
|
38912
|
-
return summary.length >
|
|
40879
|
+
return summary.length > cap3 ? `${summary.slice(0, cap3 - 1)}\u2026` : summary;
|
|
38913
40880
|
}
|
|
38914
40881
|
|
|
38915
40882
|
// src/cli/components/ToolOutput.tsx
|
|
@@ -44321,6 +46288,20 @@ ${formatSkillList(availableSkills)}`
|
|
|
44321
46288
|
graphPrompt: args.slice(1).join(" ").trim()
|
|
44322
46289
|
};
|
|
44323
46290
|
}
|
|
46291
|
+
if (args[0] === "fanout") {
|
|
46292
|
+
return {
|
|
46293
|
+
handled: true,
|
|
46294
|
+
kind: "kraken_fanout",
|
|
46295
|
+
fanoutArgs: args.slice(1).join(" ").trim()
|
|
46296
|
+
};
|
|
46297
|
+
}
|
|
46298
|
+
if (args[0] === "workbench") {
|
|
46299
|
+
return {
|
|
46300
|
+
handled: true,
|
|
46301
|
+
kind: "kraken_workbench",
|
|
46302
|
+
workbenchArgs: args.slice(1).join(" ").trim()
|
|
46303
|
+
};
|
|
46304
|
+
}
|
|
44324
46305
|
const sessionArg = args.join(" ").trim();
|
|
44325
46306
|
return {
|
|
44326
46307
|
handled: true,
|
|
@@ -44904,6 +46885,7 @@ async function handleKrakenGraph(ctx, prompt) {
|
|
|
44904
46885
|
const graph = await planTaskGraph({
|
|
44905
46886
|
prompt,
|
|
44906
46887
|
graphId: `kraken-${Date.now().toString(36)}`,
|
|
46888
|
+
cwd: ctx.cwd,
|
|
44907
46889
|
...previousAttempt ? { previousAttempt } : {}
|
|
44908
46890
|
});
|
|
44909
46891
|
appendSystem(ctx.setMessages, formatKrakenGraphAscii(graph));
|
|
@@ -44914,11 +46896,17 @@ async function handleKrakenGraph(ctx, prompt) {
|
|
|
44914
46896
|
goal: prompt
|
|
44915
46897
|
});
|
|
44916
46898
|
const summary = await executor.execute(graph);
|
|
46899
|
+
const digest = formatKrakenGraphDigest(summary.graph, {
|
|
46900
|
+
durationsMs: summary.durationsMs,
|
|
46901
|
+
unresolvedFindings: summary.unresolvedFindings
|
|
46902
|
+
});
|
|
44917
46903
|
appendSystem(
|
|
44918
46904
|
ctx.setMessages,
|
|
44919
46905
|
`${formatKrakenGraphAscii(summary.graph)}
|
|
44920
46906
|
|
|
44921
|
-
|
|
46907
|
+
${digest}
|
|
46908
|
+
|
|
46909
|
+
` + (summary.converged ? "[kraken] graph converged." : summary.cancelled ? "[kraken] graph cancelled." : `[kraken] graph did not converge \u2014 failed: ${summary.failedNodeIds.join(", ") || "none"}`)
|
|
44922
46910
|
);
|
|
44923
46911
|
} catch (err) {
|
|
44924
46912
|
appendSystem(
|
|
@@ -44928,6 +46916,508 @@ async function handleKrakenGraph(ctx, prompt) {
|
|
|
44928
46916
|
}
|
|
44929
46917
|
}
|
|
44930
46918
|
|
|
46919
|
+
// src/cli/slashHandlers/krakenFanout.ts
|
|
46920
|
+
init_auditLogger();
|
|
46921
|
+
init_toolRegistry();
|
|
46922
|
+
import { promises as fs21 } from "node:fs";
|
|
46923
|
+
|
|
46924
|
+
// src/cli/tools/krakenCsvFanout.ts
|
|
46925
|
+
init_zod();
|
|
46926
|
+
init_taskTool();
|
|
46927
|
+
import { promises as fs20 } from "node:fs";
|
|
46928
|
+
import path36 from "node:path";
|
|
46929
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
46930
|
+
var CsvFanoutArgsSchema = external_exports.object({
|
|
46931
|
+
csv_path: external_exports.string().min(1),
|
|
46932
|
+
id_column: external_exports.string().min(1),
|
|
46933
|
+
output_csv_path: external_exports.string().min(1),
|
|
46934
|
+
/** Template with `{column_name}` placeholders. The tentacle's prompt is
|
|
46935
|
+
* the template with each row's values substituted. */
|
|
46936
|
+
instruction_template: external_exports.string().min(1),
|
|
46937
|
+
/** Default 'verify' — read-mostly workloads are the common case. */
|
|
46938
|
+
agent_kind: external_exports.enum(["explore", "general", "verify"]).default("verify"),
|
|
46939
|
+
thoroughness: external_exports.enum(["quick", "medium", "deep"]).default("medium"),
|
|
46940
|
+
/** Optional: per-row scope template with `{column}` placeholders. The
|
|
46941
|
+
* resulting path/glob list is fed to the scope-overlap check. */
|
|
46942
|
+
scope_template: external_exports.array(external_exports.string()).optional(),
|
|
46943
|
+
/** Default: ZELARI_KRAKEN_MAX_PARALLEL. */
|
|
46944
|
+
max_concurrency: external_exports.number().int().positive().optional(),
|
|
46945
|
+
/** Per-row timeout (ms). Default: 5 min for verify, 15 min for general. */
|
|
46946
|
+
max_runtime_seconds: external_exports.number().int().positive().optional()
|
|
46947
|
+
});
|
|
46948
|
+
async function readCsv(filePath) {
|
|
46949
|
+
const text = await fs20.readFile(filePath, "utf8");
|
|
46950
|
+
return parseCsv(text);
|
|
46951
|
+
}
|
|
46952
|
+
function parseCsv(text) {
|
|
46953
|
+
const records = [];
|
|
46954
|
+
let row = [];
|
|
46955
|
+
let field = "";
|
|
46956
|
+
let inQuotes = false;
|
|
46957
|
+
for (let i = 0; i < text.length; i++) {
|
|
46958
|
+
const ch = text[i];
|
|
46959
|
+
if (inQuotes) {
|
|
46960
|
+
if (ch === '"') {
|
|
46961
|
+
if (text[i + 1] === '"') {
|
|
46962
|
+
field += '"';
|
|
46963
|
+
i += 1;
|
|
46964
|
+
} else inQuotes = false;
|
|
46965
|
+
} else {
|
|
46966
|
+
field += ch;
|
|
46967
|
+
}
|
|
46968
|
+
} else {
|
|
46969
|
+
if (ch === '"') inQuotes = true;
|
|
46970
|
+
else if (ch === ",") {
|
|
46971
|
+
row.push(field);
|
|
46972
|
+
field = "";
|
|
46973
|
+
} else if (ch === "\n") {
|
|
46974
|
+
row.push(field);
|
|
46975
|
+
records.push(row);
|
|
46976
|
+
row = [];
|
|
46977
|
+
field = "";
|
|
46978
|
+
} else if (ch === "\r") {
|
|
46979
|
+
} else field += ch;
|
|
46980
|
+
}
|
|
46981
|
+
}
|
|
46982
|
+
if (field !== "" || row.length > 0) {
|
|
46983
|
+
row.push(field);
|
|
46984
|
+
records.push(row);
|
|
46985
|
+
}
|
|
46986
|
+
while (records.length > 0 && records[records.length - 1].length === 1 && records[records.length - 1][0] === "") {
|
|
46987
|
+
records.pop();
|
|
46988
|
+
}
|
|
46989
|
+
if (records.length === 0) return { headers: [], rows: [] };
|
|
46990
|
+
const headers = records[0];
|
|
46991
|
+
const rows = records.slice(1).map((r) => {
|
|
46992
|
+
const obj = {};
|
|
46993
|
+
for (let i = 0; i < headers.length; i++) obj[headers[i]] = r[i] ?? "";
|
|
46994
|
+
return obj;
|
|
46995
|
+
});
|
|
46996
|
+
return { headers, rows };
|
|
46997
|
+
}
|
|
46998
|
+
function applyTemplate(template, row) {
|
|
46999
|
+
return template.replace(/\{([a-zA-Z_][\w-]*)\}/g, (_, k) => row[k] ?? "");
|
|
47000
|
+
}
|
|
47001
|
+
function serializeCsv(headers, rows) {
|
|
47002
|
+
const escape = (v) => {
|
|
47003
|
+
if (v.includes(",") || v.includes("\n") || v.includes('"')) {
|
|
47004
|
+
return `"${v.replace(/"/g, '""')}"`;
|
|
47005
|
+
}
|
|
47006
|
+
return v;
|
|
47007
|
+
};
|
|
47008
|
+
const out = [headers.map(escape).join(",")];
|
|
47009
|
+
for (const row of rows) {
|
|
47010
|
+
out.push(headers.map((h) => escape(row[h] ?? "")).join(","));
|
|
47011
|
+
}
|
|
47012
|
+
return out.join("\n") + "\n";
|
|
47013
|
+
}
|
|
47014
|
+
function resolveMaxConcurrency(env = process.env) {
|
|
47015
|
+
const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
|
|
47016
|
+
if (raw === void 0 || raw === "") return 12;
|
|
47017
|
+
const n = Number.parseInt(raw, 10);
|
|
47018
|
+
return Number.isFinite(n) && n > 0 ? n : 12;
|
|
47019
|
+
}
|
|
47020
|
+
async function runCsvFanout(args, deps, opts) {
|
|
47021
|
+
const start = Date.now();
|
|
47022
|
+
const absCsv = path36.isAbsolute(args.csv_path) ? args.csv_path : path36.join(opts.parentCwd, args.csv_path);
|
|
47023
|
+
const absOut = path36.isAbsolute(args.output_csv_path) ? args.output_csv_path : path36.join(opts.parentCwd, args.output_csv_path);
|
|
47024
|
+
const { headers, rows } = await readCsv(absCsv);
|
|
47025
|
+
if (headers.length === 0) {
|
|
47026
|
+
throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
|
|
47027
|
+
}
|
|
47028
|
+
if (!args.id_column) {
|
|
47029
|
+
throw new Error("kraken_csv_fanout: id_column is required");
|
|
47030
|
+
}
|
|
47031
|
+
if (!headers.includes(args.id_column)) {
|
|
47032
|
+
throw new Error(`kraken_csv_fanout: id_column "${args.id_column}" not in CSV header [${headers.join(", ")}]`);
|
|
47033
|
+
}
|
|
47034
|
+
const concurrency = args.max_concurrency ?? resolveMaxConcurrency();
|
|
47035
|
+
opts.onLog?.(`csv fanout: ${rows.length} rows \xD7 ${args.agent_kind} @ concurrency=${concurrency}`);
|
|
47036
|
+
const perRowMs = args.max_runtime_seconds !== void 0 ? args.max_runtime_seconds * 1e3 : args.agent_kind === "general" ? 9e5 : 3e5;
|
|
47037
|
+
const outputRecords = rows.map((r) => ({ ...r, status: "pending", result: "", error: "" }));
|
|
47038
|
+
const outHeaders = [...headers, "status", "result", "error"];
|
|
47039
|
+
let writeChain2 = Promise.resolve();
|
|
47040
|
+
function queueWrite(contents) {
|
|
47041
|
+
const next = writeChain2.then(() => atomicWrite(absOut, contents));
|
|
47042
|
+
writeChain2 = next.catch(() => {
|
|
47043
|
+
});
|
|
47044
|
+
return next;
|
|
47045
|
+
}
|
|
47046
|
+
let nextIndex = 0;
|
|
47047
|
+
let completed = 0;
|
|
47048
|
+
let errored = 0;
|
|
47049
|
+
const errors = [];
|
|
47050
|
+
async function worker() {
|
|
47051
|
+
while (true) {
|
|
47052
|
+
const i = nextIndex;
|
|
47053
|
+
nextIndex += 1;
|
|
47054
|
+
if (i >= rows.length) return;
|
|
47055
|
+
const row = rows[i];
|
|
47056
|
+
const prompt = applyTemplate(args.instruction_template, row);
|
|
47057
|
+
const scope = args.scope_template?.map((t) => applyTemplate(t, row));
|
|
47058
|
+
const res = await runTentacle({
|
|
47059
|
+
deps,
|
|
47060
|
+
args: {
|
|
47061
|
+
description: `csv-row ${row[args.id_column] ?? i}`,
|
|
47062
|
+
prompt,
|
|
47063
|
+
...scope ? { scope } : {}
|
|
47064
|
+
},
|
|
47065
|
+
agent: args.agent_kind,
|
|
47066
|
+
thoroughness: args.thoroughness,
|
|
47067
|
+
parentCwd: opts.parentCwd,
|
|
47068
|
+
sessionId: opts.sessionId
|
|
47069
|
+
});
|
|
47070
|
+
outputRecords[i].status = res.ok ? "ok" : "error";
|
|
47071
|
+
if (res.ok) {
|
|
47072
|
+
outputRecords[i].result = res.result;
|
|
47073
|
+
completed += 1;
|
|
47074
|
+
} else {
|
|
47075
|
+
outputRecords[i].error = res.error;
|
|
47076
|
+
errored += 1;
|
|
47077
|
+
errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
|
|
47078
|
+
}
|
|
47079
|
+
await fs20.mkdir(path36.dirname(absOut), { recursive: true });
|
|
47080
|
+
await queueWrite(serializeCsv(outHeaders, outputRecords));
|
|
47081
|
+
}
|
|
47082
|
+
}
|
|
47083
|
+
const pool = Array.from({ length: Math.min(concurrency, rows.length) }, () => worker());
|
|
47084
|
+
await Promise.all(pool);
|
|
47085
|
+
const durationMs = Date.now() - start;
|
|
47086
|
+
opts.onLog?.(`csv fanout: ${completed} ok, ${errored} error, ${durationMs}ms`);
|
|
47087
|
+
if (errored > 0) {
|
|
47088
|
+
opts.onLog?.(`csv fanout: first 3 errors:
|
|
47089
|
+
- ${errors.slice(0, 3).join("\n - ")}`);
|
|
47090
|
+
}
|
|
47091
|
+
return {
|
|
47092
|
+
rows: rows.length,
|
|
47093
|
+
completed,
|
|
47094
|
+
errored,
|
|
47095
|
+
output_csv_path: absOut,
|
|
47096
|
+
durationMs
|
|
47097
|
+
};
|
|
47098
|
+
}
|
|
47099
|
+
async function atomicWrite(file2, contents) {
|
|
47100
|
+
const tmp = `${file2}.${process.pid}.${Date.now()}.${randomBytes3(6).toString("hex")}.tmp`;
|
|
47101
|
+
await fs20.writeFile(tmp, contents, "utf8");
|
|
47102
|
+
await fs20.rename(tmp, file2);
|
|
47103
|
+
}
|
|
47104
|
+
|
|
47105
|
+
// src/cli/slashHandlers/krakenFanout.ts
|
|
47106
|
+
function parseFanoutArgs(argv) {
|
|
47107
|
+
if (argv.length === 0) {
|
|
47108
|
+
return { ok: false, usage: USAGE };
|
|
47109
|
+
}
|
|
47110
|
+
const positional = [];
|
|
47111
|
+
const flags = {};
|
|
47112
|
+
const listFlags = {};
|
|
47113
|
+
for (let i = 0; i < argv.length; i++) {
|
|
47114
|
+
const a = argv[i];
|
|
47115
|
+
if (a.startsWith("--")) {
|
|
47116
|
+
const eq = a.indexOf("=");
|
|
47117
|
+
if (eq >= 0) {
|
|
47118
|
+
flags[a.slice(2, eq)] = a.slice(eq + 1);
|
|
47119
|
+
} else if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
|
|
47120
|
+
flags[a.slice(2)] = argv[i + 1];
|
|
47121
|
+
i += 1;
|
|
47122
|
+
} else {
|
|
47123
|
+
flags[a.slice(2)] = true;
|
|
47124
|
+
}
|
|
47125
|
+
} else {
|
|
47126
|
+
positional.push(a);
|
|
47127
|
+
}
|
|
47128
|
+
}
|
|
47129
|
+
const csv = positional[0] ?? (typeof flags.csv === "string" ? flags.csv : void 0);
|
|
47130
|
+
const col = typeof flags.col === "string" ? flags.col : void 0;
|
|
47131
|
+
const out = typeof flags.out === "string" ? flags.out : void 0;
|
|
47132
|
+
const instruction = typeof flags.instruction === "string" ? flags.instruction : void 0;
|
|
47133
|
+
if (!csv) return { ok: false, error: "csv_path is required (positional or --csv)" };
|
|
47134
|
+
if (!col) return { ok: false, error: "--col <id_column> is required" };
|
|
47135
|
+
if (!out) return { ok: false, error: "--out <output_csv_path> is required" };
|
|
47136
|
+
if (!instruction) return { ok: false, error: "--instruction <template> is required" };
|
|
47137
|
+
const agentKindRaw = typeof flags.agent === "string" ? flags.agent : "verify";
|
|
47138
|
+
if (agentKindRaw !== "explore" && agentKindRaw !== "verify" && agentKindRaw !== "general") {
|
|
47139
|
+
return { ok: false, error: `--agent must be one of explore, verify, general (got "${agentKindRaw}")` };
|
|
47140
|
+
}
|
|
47141
|
+
const thoroughnessRaw = typeof flags.thoroughness === "string" ? flags.thoroughness : "medium";
|
|
47142
|
+
if (thoroughnessRaw !== "quick" && thoroughnessRaw !== "medium" && thoroughnessRaw !== "deep") {
|
|
47143
|
+
return { ok: false, error: `--thoroughness must be one of quick, medium, deep (got "${thoroughnessRaw}")` };
|
|
47144
|
+
}
|
|
47145
|
+
let concurrency;
|
|
47146
|
+
if (typeof flags.concurrency === "string") {
|
|
47147
|
+
concurrency = Number.parseInt(flags.concurrency, 10);
|
|
47148
|
+
if (!Number.isFinite(concurrency) || concurrency <= 0) {
|
|
47149
|
+
return { ok: false, error: `--concurrency must be a positive integer (got "${flags.concurrency}")` };
|
|
47150
|
+
}
|
|
47151
|
+
}
|
|
47152
|
+
let maxRuntimeSeconds;
|
|
47153
|
+
if (typeof flags["max-runtime"] === "string") {
|
|
47154
|
+
maxRuntimeSeconds = Number.parseInt(flags["max-runtime"], 10);
|
|
47155
|
+
if (!Number.isFinite(maxRuntimeSeconds) || maxRuntimeSeconds <= 0) {
|
|
47156
|
+
return { ok: false, error: `--max-runtime must be a positive integer (got "${flags["max-runtime"]}")` };
|
|
47157
|
+
}
|
|
47158
|
+
}
|
|
47159
|
+
const scopeList = listFlags.scope ?? (typeof flags.scope === "string" ? [flags.scope] : void 0);
|
|
47160
|
+
return {
|
|
47161
|
+
ok: true,
|
|
47162
|
+
args: {
|
|
47163
|
+
csv_path: csv,
|
|
47164
|
+
id_column: col,
|
|
47165
|
+
output_csv_path: out,
|
|
47166
|
+
instruction_template: instruction,
|
|
47167
|
+
agent_kind: agentKindRaw,
|
|
47168
|
+
thoroughness: thoroughnessRaw,
|
|
47169
|
+
...scopeList ? { scope_template: scopeList } : {},
|
|
47170
|
+
...concurrency !== void 0 ? { max_concurrency: concurrency } : {},
|
|
47171
|
+
...maxRuntimeSeconds !== void 0 ? { max_runtime_seconds: maxRuntimeSeconds } : {}
|
|
47172
|
+
}
|
|
47173
|
+
};
|
|
47174
|
+
}
|
|
47175
|
+
var USAGE = `Usage:
|
|
47176
|
+
/kraken fanout <csv_path>
|
|
47177
|
+
--col <id_column>
|
|
47178
|
+
--out <output_csv_path>
|
|
47179
|
+
--instruction "<template with {column} placeholders>"
|
|
47180
|
+
[--agent explore|verify|general] (default: verify)
|
|
47181
|
+
[--thoroughness quick|medium|deep] (default: medium)
|
|
47182
|
+
[--scope "<glob template>"] (repeatable)
|
|
47183
|
+
[--concurrency <n>] (default: ZELARI_KRAKEN_MAX_PARALLEL)
|
|
47184
|
+
[--max-runtime <seconds>]
|
|
47185
|
+
|
|
47186
|
+
Each row in <csv_path> becomes one sub-agent tentacle. The result CSV
|
|
47187
|
+
gets one row per input row, with extra columns \`status\`, \`result\`, \`error\`.`;
|
|
47188
|
+
async function handleKrakenFanout(ctx, raw) {
|
|
47189
|
+
const argv = splitArgs(stripPrefix(raw, "fanout"));
|
|
47190
|
+
const parsed = parseFanoutArgs(argv);
|
|
47191
|
+
if (!parsed.ok || !parsed.args) {
|
|
47192
|
+
appendSystem(ctx.setMessages, parsed.usage ?? `Usage: ${USAGE}`);
|
|
47193
|
+
if (parsed.error) {
|
|
47194
|
+
appendSystem(ctx.setMessages, `[kraken fanout] ${parsed.error}`);
|
|
47195
|
+
}
|
|
47196
|
+
return;
|
|
47197
|
+
}
|
|
47198
|
+
const absCsv = isAbsolute(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
|
|
47199
|
+
try {
|
|
47200
|
+
await fs21.access(absCsv);
|
|
47201
|
+
} catch {
|
|
47202
|
+
appendSystem(ctx.setMessages, `[kraken fanout] source CSV not found: ${absCsv}`);
|
|
47203
|
+
return;
|
|
47204
|
+
}
|
|
47205
|
+
appendSystem(
|
|
47206
|
+
ctx.setMessages,
|
|
47207
|
+
`[kraken fanout] starting: ${parsed.args.csv_path} \u2192 ${parsed.args.output_csv_path} (agent=${parsed.args.agent_kind})`
|
|
47208
|
+
);
|
|
47209
|
+
const audit = new AuditLogger();
|
|
47210
|
+
const taskToolDeps = {
|
|
47211
|
+
createSubAgentContext: createKrakenSubAgentContextFactory({
|
|
47212
|
+
root: ctx.cwd,
|
|
47213
|
+
audit,
|
|
47214
|
+
sessionId: ctx.sessionId
|
|
47215
|
+
})
|
|
47216
|
+
};
|
|
47217
|
+
try {
|
|
47218
|
+
const summary = await runCsvFanout(parsed.args, taskToolDeps, {
|
|
47219
|
+
parentCwd: ctx.cwd,
|
|
47220
|
+
sessionId: ctx.sessionId,
|
|
47221
|
+
onLog: (line) => appendSystem(ctx.setMessages, `[kraken fanout] ${line}`)
|
|
47222
|
+
});
|
|
47223
|
+
appendSystem(
|
|
47224
|
+
ctx.setMessages,
|
|
47225
|
+
`[kraken fanout] done: ${summary.completed}/${summary.rows} ok, ${summary.errored} error, ${summary.durationMs}ms \u2192 ${summary.output_csv_path}`
|
|
47226
|
+
);
|
|
47227
|
+
} catch (err) {
|
|
47228
|
+
appendSystem(
|
|
47229
|
+
ctx.setMessages,
|
|
47230
|
+
`[kraken fanout] failed: ${err instanceof Error ? err.message : String(err)}`
|
|
47231
|
+
);
|
|
47232
|
+
}
|
|
47233
|
+
}
|
|
47234
|
+
function isAbsolute(p3) {
|
|
47235
|
+
return /^([A-Za-z]:[\\/]|[\\/])/.test(p3);
|
|
47236
|
+
}
|
|
47237
|
+
function joinPath(a, b) {
|
|
47238
|
+
if (a.endsWith("\\") || a.endsWith("/")) return a + b;
|
|
47239
|
+
return `${a}\\${b}`;
|
|
47240
|
+
}
|
|
47241
|
+
function stripPrefix(s, prefix) {
|
|
47242
|
+
const trimmed = s.trim();
|
|
47243
|
+
if (trimmed.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
47244
|
+
return trimmed.slice(prefix.length).trim();
|
|
47245
|
+
}
|
|
47246
|
+
return trimmed;
|
|
47247
|
+
}
|
|
47248
|
+
function splitArgs(s) {
|
|
47249
|
+
const out = [];
|
|
47250
|
+
let cur = "";
|
|
47251
|
+
let inQuotes = false;
|
|
47252
|
+
for (let i = 0; i < s.length; i++) {
|
|
47253
|
+
const ch = s[i];
|
|
47254
|
+
if (ch === '"') {
|
|
47255
|
+
inQuotes = !inQuotes;
|
|
47256
|
+
continue;
|
|
47257
|
+
}
|
|
47258
|
+
if (!inQuotes && /\s/.test(ch)) {
|
|
47259
|
+
if (cur !== "") {
|
|
47260
|
+
out.push(cur);
|
|
47261
|
+
cur = "";
|
|
47262
|
+
}
|
|
47263
|
+
continue;
|
|
47264
|
+
}
|
|
47265
|
+
cur += ch;
|
|
47266
|
+
}
|
|
47267
|
+
if (cur !== "") out.push(cur);
|
|
47268
|
+
return out;
|
|
47269
|
+
}
|
|
47270
|
+
|
|
47271
|
+
// src/cli/slashHandlers/krakenWorkbench.ts
|
|
47272
|
+
import { promises as fs22 } from "node:fs";
|
|
47273
|
+
import path37 from "node:path";
|
|
47274
|
+
|
|
47275
|
+
// src/cli/kraken/workbenchView.ts
|
|
47276
|
+
var EMPTY = {
|
|
47277
|
+
goal: "",
|
|
47278
|
+
graphId: "",
|
|
47279
|
+
started: "",
|
|
47280
|
+
elapsed: "",
|
|
47281
|
+
progress: "",
|
|
47282
|
+
nodes: [],
|
|
47283
|
+
events: []
|
|
47284
|
+
};
|
|
47285
|
+
function parseWorkbench(content) {
|
|
47286
|
+
if (!content) return { ...EMPTY, nodes: [], events: [] };
|
|
47287
|
+
const lines = content.split(/\r?\n/);
|
|
47288
|
+
const out = { ...EMPTY, nodes: [], events: [] };
|
|
47289
|
+
let i = 0;
|
|
47290
|
+
while (i < lines.length) {
|
|
47291
|
+
const line = lines[i];
|
|
47292
|
+
const goalM = /^\*\*Goal:\*\*\s*(.*)$/.exec(line);
|
|
47293
|
+
if (goalM) {
|
|
47294
|
+
out.goal = goalM[1];
|
|
47295
|
+
i += 1;
|
|
47296
|
+
continue;
|
|
47297
|
+
}
|
|
47298
|
+
const gidM = /^\*\*Graph id:\*\*\s*`?([^`\s]*)`?/.exec(line);
|
|
47299
|
+
if (gidM) {
|
|
47300
|
+
out.graphId = gidM[1];
|
|
47301
|
+
i += 1;
|
|
47302
|
+
continue;
|
|
47303
|
+
}
|
|
47304
|
+
const startedM = /^\*\*Started:\*\*\s*(.+?)\s*·\s*\*\*Elapsed:\*\*\s*(\S+)/.exec(line);
|
|
47305
|
+
if (startedM) {
|
|
47306
|
+
out.started = startedM[1];
|
|
47307
|
+
out.elapsed = startedM[2];
|
|
47308
|
+
i += 1;
|
|
47309
|
+
continue;
|
|
47310
|
+
}
|
|
47311
|
+
const justStarted = /^\*\*Started:\*\*\s*(.*)$/.exec(line);
|
|
47312
|
+
if (justStarted) {
|
|
47313
|
+
out.started = justStarted[1];
|
|
47314
|
+
i += 1;
|
|
47315
|
+
continue;
|
|
47316
|
+
}
|
|
47317
|
+
const sectionM = /^##\s+([^:\s]+)(?::\s*(.*))?/.exec(line);
|
|
47318
|
+
if (!sectionM) {
|
|
47319
|
+
i += 1;
|
|
47320
|
+
continue;
|
|
47321
|
+
}
|
|
47322
|
+
const section = sectionM[1];
|
|
47323
|
+
const inline = sectionM[2] ?? "";
|
|
47324
|
+
if (section === "Progress") {
|
|
47325
|
+
if (inline) out.progress = inline;
|
|
47326
|
+
i += 1;
|
|
47327
|
+
continue;
|
|
47328
|
+
}
|
|
47329
|
+
if (section === "Wave") {
|
|
47330
|
+
while (i < lines.length && !lines[i].startsWith("| t")) i += 1;
|
|
47331
|
+
while (i < lines.length && lines[i].startsWith("| t")) {
|
|
47332
|
+
const cells = splitRow(lines[i]);
|
|
47333
|
+
if (cells.length >= 7) {
|
|
47334
|
+
out.nodes.push({
|
|
47335
|
+
id: cells[0],
|
|
47336
|
+
label: cells[1],
|
|
47337
|
+
kind: cells[2],
|
|
47338
|
+
scope: cells[3],
|
|
47339
|
+
status: cells[4],
|
|
47340
|
+
model: cells[5],
|
|
47341
|
+
duration: cells[6]
|
|
47342
|
+
});
|
|
47343
|
+
}
|
|
47344
|
+
i += 1;
|
|
47345
|
+
}
|
|
47346
|
+
continue;
|
|
47347
|
+
}
|
|
47348
|
+
if (section === "Events" || section.startsWith("Events")) {
|
|
47349
|
+
while (i < lines.length && !lines[i].startsWith("- ")) i += 1;
|
|
47350
|
+
while (i < lines.length && lines[i].startsWith("- ")) {
|
|
47351
|
+
const m = /^-\s+(\d{2}:\d{2}:\d{2})\s+(.*)$/.exec(lines[i]);
|
|
47352
|
+
if (m) out.events.push({ ts: m[1], text: m[2] });
|
|
47353
|
+
i += 1;
|
|
47354
|
+
}
|
|
47355
|
+
continue;
|
|
47356
|
+
}
|
|
47357
|
+
i += 1;
|
|
47358
|
+
}
|
|
47359
|
+
return out;
|
|
47360
|
+
}
|
|
47361
|
+
function splitRow(line) {
|
|
47362
|
+
return line.replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
|
|
47363
|
+
}
|
|
47364
|
+
function formatWorkbenchForTerminal(p3) {
|
|
47365
|
+
const lines = [];
|
|
47366
|
+
if (p3.goal) lines.push(`[kraken] ${p3.goal}`);
|
|
47367
|
+
if (p3.progress) lines.push(`[kraken] progress: ${p3.progress}`);
|
|
47368
|
+
if (p3.elapsed) lines.push(`[kraken] elapsed: ${p3.elapsed}`);
|
|
47369
|
+
lines.push("");
|
|
47370
|
+
if (p3.nodes.length > 0) {
|
|
47371
|
+
lines.push("[kraken] wave:");
|
|
47372
|
+
for (const n of p3.nodes) {
|
|
47373
|
+
const scope = n.scope ? ` (${n.scope})` : "";
|
|
47374
|
+
const dur = n.duration ? ` ${n.duration}` : "";
|
|
47375
|
+
lines.push(` ${n.status} ${n.id} ${n.label}${scope}${dur ? " " + dur : ""}`);
|
|
47376
|
+
}
|
|
47377
|
+
lines.push("");
|
|
47378
|
+
}
|
|
47379
|
+
if (p3.events.length > 0) {
|
|
47380
|
+
lines.push("[kraken] events (latest):");
|
|
47381
|
+
for (const e of p3.events.slice(-10)) {
|
|
47382
|
+
lines.push(` ${e.ts} ${e.text}`);
|
|
47383
|
+
}
|
|
47384
|
+
}
|
|
47385
|
+
return lines.join("\n");
|
|
47386
|
+
}
|
|
47387
|
+
|
|
47388
|
+
// src/cli/slashHandlers/krakenWorkbench.ts
|
|
47389
|
+
async function handleKrakenWorkbench(ctx) {
|
|
47390
|
+
const dir = path37.join(ctx.cwd, ".zelari", "radio");
|
|
47391
|
+
let latest = null;
|
|
47392
|
+
let latestMtime = 0;
|
|
47393
|
+
try {
|
|
47394
|
+
const files = await fs22.readdir(dir);
|
|
47395
|
+
for (const f of files) {
|
|
47396
|
+
if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
|
|
47397
|
+
const full = path37.join(dir, f);
|
|
47398
|
+
const stat = await fs22.stat(full);
|
|
47399
|
+
if (stat.mtimeMs > latestMtime) {
|
|
47400
|
+
latestMtime = stat.mtimeMs;
|
|
47401
|
+
latest = full;
|
|
47402
|
+
}
|
|
47403
|
+
}
|
|
47404
|
+
} catch {
|
|
47405
|
+
}
|
|
47406
|
+
if (!latest) {
|
|
47407
|
+
appendSystem(ctx.setMessages, "[kraken workbench] no workbench file found (.zelari/radio/workbench-*.md)");
|
|
47408
|
+
return;
|
|
47409
|
+
}
|
|
47410
|
+
const content = await fs22.readFile(latest, "utf8");
|
|
47411
|
+
const parsed = parseWorkbench(content);
|
|
47412
|
+
const rendered = formatWorkbenchForTerminal(parsed);
|
|
47413
|
+
if (!rendered.trim()) {
|
|
47414
|
+
appendSystem(ctx.setMessages, `[kraken workbench] ${path37.basename(latest)}: (no nodes / no events yet)`);
|
|
47415
|
+
return;
|
|
47416
|
+
}
|
|
47417
|
+
appendSystem(ctx.setMessages, `[kraken workbench] ${path37.basename(latest)}:
|
|
47418
|
+
${rendered}`);
|
|
47419
|
+
}
|
|
47420
|
+
|
|
44931
47421
|
// src/cli/compaction.ts
|
|
44932
47422
|
function compactTranscript(messages, options = {}) {
|
|
44933
47423
|
const threshold = options.threshold ?? 50;
|
|
@@ -45071,7 +47561,7 @@ ${output}`.toLowerCase();
|
|
|
45071
47561
|
}
|
|
45072
47562
|
|
|
45073
47563
|
// src/cli/slashHandlers/plugins.ts
|
|
45074
|
-
|
|
47564
|
+
init_registry3();
|
|
45075
47565
|
|
|
45076
47566
|
// src/cli/plugins/installer.ts
|
|
45077
47567
|
init_cmdline();
|
|
@@ -45225,17 +47715,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
45225
47715
|
}
|
|
45226
47716
|
|
|
45227
47717
|
// src/cli/slashHandlers/promoteMember.ts
|
|
45228
|
-
import { promises as
|
|
45229
|
-
import
|
|
47718
|
+
import { promises as fs23 } from "node:fs";
|
|
47719
|
+
import path40 from "node:path";
|
|
45230
47720
|
import os10 from "node:os";
|
|
45231
47721
|
async function handlePromoteMember(ctx, memberId) {
|
|
45232
47722
|
try {
|
|
45233
47723
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
45234
47724
|
const { skill, markdown } = promoteMember2(memberId);
|
|
45235
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
45236
|
-
await
|
|
45237
|
-
const filePath =
|
|
45238
|
-
await
|
|
47725
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path40.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
47726
|
+
await fs23.mkdir(skillDir, { recursive: true });
|
|
47727
|
+
const filePath = path40.join(skillDir, `${skill.id}.md`);
|
|
47728
|
+
await fs23.writeFile(filePath, markdown, "utf8");
|
|
45239
47729
|
appendSystem(
|
|
45240
47730
|
ctx.setMessages,
|
|
45241
47731
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -45251,25 +47741,25 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
45251
47741
|
}
|
|
45252
47742
|
|
|
45253
47743
|
// src/cli/branchManager.ts
|
|
45254
|
-
import { promises as
|
|
45255
|
-
import
|
|
47744
|
+
import { promises as fs24, existsSync as existsSync38, readFileSync as readFileSync30, writeFileSync as writeFileSync19, mkdirSync as mkdirSync16, statSync as statSync4, rmSync as rmSync3 } from "node:fs";
|
|
47745
|
+
import path41 from "node:path";
|
|
45256
47746
|
import os11 from "node:os";
|
|
45257
47747
|
var META_FILENAME = "meta.json";
|
|
45258
47748
|
var SESSIONS_SUBDIR = "sessions";
|
|
45259
47749
|
function getBranchesBaseDir() {
|
|
45260
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
47750
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path41.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
45261
47751
|
}
|
|
45262
47752
|
function getSessionsBaseDir() {
|
|
45263
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
47753
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path41.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
45264
47754
|
}
|
|
45265
47755
|
function branchPathFor(name, baseDir) {
|
|
45266
|
-
return
|
|
47756
|
+
return path41.join(baseDir, name);
|
|
45267
47757
|
}
|
|
45268
47758
|
function metaPathFor(name, baseDir) {
|
|
45269
|
-
return
|
|
47759
|
+
return path41.join(baseDir, name, META_FILENAME);
|
|
45270
47760
|
}
|
|
45271
47761
|
function sessionsPathFor(name, baseDir) {
|
|
45272
|
-
return
|
|
47762
|
+
return path41.join(baseDir, name, SESSIONS_SUBDIR);
|
|
45273
47763
|
}
|
|
45274
47764
|
function readBranchMeta(name, baseDir) {
|
|
45275
47765
|
const metaPath = metaPathFor(name, baseDir);
|
|
@@ -45294,13 +47784,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
45294
47784
|
}
|
|
45295
47785
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
45296
47786
|
const metaPath = metaPathFor(name, baseDir);
|
|
45297
|
-
mkdirSync16(
|
|
47787
|
+
mkdirSync16(path41.dirname(metaPath), { recursive: true });
|
|
45298
47788
|
writeFileSync19(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
45299
47789
|
}
|
|
45300
47790
|
async function countSessions(name, baseDir) {
|
|
45301
47791
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
45302
47792
|
try {
|
|
45303
|
-
const entries = await
|
|
47793
|
+
const entries = await fs24.readdir(sessionsPath);
|
|
45304
47794
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
45305
47795
|
} catch (err) {
|
|
45306
47796
|
if (err.code === "ENOENT") return 0;
|
|
@@ -45345,15 +47835,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
45345
47835
|
if (branchExists(name, baseDir)) {
|
|
45346
47836
|
throw new BranchAlreadyExistsError(name);
|
|
45347
47837
|
}
|
|
45348
|
-
const sourcePath =
|
|
47838
|
+
const sourcePath = path41.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
45349
47839
|
if (!existsSync38(sourcePath)) {
|
|
45350
47840
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
45351
47841
|
}
|
|
45352
47842
|
const branchPath = branchPathFor(name, baseDir);
|
|
45353
47843
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
45354
47844
|
mkdirSync16(branchSessionsPath, { recursive: true });
|
|
45355
|
-
const destPath =
|
|
45356
|
-
await
|
|
47845
|
+
const destPath = path41.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
47846
|
+
await fs24.copyFile(sourcePath, destPath);
|
|
45357
47847
|
const meta3 = {
|
|
45358
47848
|
name,
|
|
45359
47849
|
createdAt: Date.now(),
|
|
@@ -45371,7 +47861,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
45371
47861
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
45372
47862
|
let entries;
|
|
45373
47863
|
try {
|
|
45374
|
-
entries = await
|
|
47864
|
+
entries = await fs24.readdir(baseDir);
|
|
45375
47865
|
} catch (err) {
|
|
45376
47866
|
if (err.code === "ENOENT") return [];
|
|
45377
47867
|
throw err;
|
|
@@ -45452,26 +47942,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
45452
47942
|
}
|
|
45453
47943
|
|
|
45454
47944
|
// src/cli/slashHandlers/workspace.ts
|
|
45455
|
-
import { promises as
|
|
45456
|
-
import
|
|
47945
|
+
import { promises as fs25 } from "node:fs";
|
|
47946
|
+
import path42 from "node:path";
|
|
45457
47947
|
async function handleWorkspaceShow(ctx, what) {
|
|
45458
47948
|
try {
|
|
45459
|
-
const zelari =
|
|
47949
|
+
const zelari = path42.join(process.cwd(), ".zelari");
|
|
45460
47950
|
let content;
|
|
45461
47951
|
switch (what) {
|
|
45462
47952
|
case "plan": {
|
|
45463
|
-
const planPath =
|
|
47953
|
+
const planPath = path42.join(zelari, "plan.md");
|
|
45464
47954
|
try {
|
|
45465
|
-
content = await
|
|
47955
|
+
content = await fs25.readFile(planPath, "utf-8");
|
|
45466
47956
|
} catch {
|
|
45467
47957
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
45468
47958
|
}
|
|
45469
47959
|
break;
|
|
45470
47960
|
}
|
|
45471
47961
|
case "decisions": {
|
|
45472
|
-
const decisionsDir =
|
|
47962
|
+
const decisionsDir = path42.join(zelari, "decisions");
|
|
45473
47963
|
try {
|
|
45474
|
-
const files = (await
|
|
47964
|
+
const files = (await fs25.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
45475
47965
|
if (files.length === 0) {
|
|
45476
47966
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
45477
47967
|
} else {
|
|
@@ -45479,7 +47969,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
45479
47969
|
`];
|
|
45480
47970
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
45481
47971
|
for (const f of files) {
|
|
45482
|
-
const raw = await
|
|
47972
|
+
const raw = await fs25.readFile(path42.join(decisionsDir, f), "utf-8");
|
|
45483
47973
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
45484
47974
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
45485
47975
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -45492,27 +47982,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
45492
47982
|
break;
|
|
45493
47983
|
}
|
|
45494
47984
|
case "risks": {
|
|
45495
|
-
const risksPath =
|
|
47985
|
+
const risksPath = path42.join(zelari, "risks.md");
|
|
45496
47986
|
try {
|
|
45497
|
-
content = await
|
|
47987
|
+
content = await fs25.readFile(risksPath, "utf-8");
|
|
45498
47988
|
} catch {
|
|
45499
47989
|
content = "(no risks.md yet)";
|
|
45500
47990
|
}
|
|
45501
47991
|
break;
|
|
45502
47992
|
}
|
|
45503
47993
|
case "agents": {
|
|
45504
|
-
const agentsPath =
|
|
47994
|
+
const agentsPath = path42.join(process.cwd(), "AGENTS.MD");
|
|
45505
47995
|
try {
|
|
45506
|
-
content = await
|
|
47996
|
+
content = await fs25.readFile(agentsPath, "utf-8");
|
|
45507
47997
|
} catch {
|
|
45508
47998
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
45509
47999
|
}
|
|
45510
48000
|
break;
|
|
45511
48001
|
}
|
|
45512
48002
|
case "docs": {
|
|
45513
|
-
const docsDir =
|
|
48003
|
+
const docsDir = path42.join(zelari, "docs");
|
|
45514
48004
|
try {
|
|
45515
|
-
const files = (await
|
|
48005
|
+
const files = (await fs25.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
45516
48006
|
content = files.length ? `# Docs (${files.length})
|
|
45517
48007
|
|
|
45518
48008
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -45552,8 +48042,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
45552
48042
|
return;
|
|
45553
48043
|
}
|
|
45554
48044
|
try {
|
|
45555
|
-
const target =
|
|
45556
|
-
await
|
|
48045
|
+
const target = path42.join(process.cwd(), ".zelari");
|
|
48046
|
+
await fs25.rm(target, { recursive: true, force: true });
|
|
45557
48047
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
45558
48048
|
} catch (err) {
|
|
45559
48049
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -45912,16 +48402,16 @@ function handleModelsRefresh(ctx) {
|
|
|
45912
48402
|
}
|
|
45913
48403
|
|
|
45914
48404
|
// src/cli/slashHandlers/skills.ts
|
|
45915
|
-
import
|
|
48405
|
+
import path43 from "node:path";
|
|
45916
48406
|
import os12 from "node:os";
|
|
45917
48407
|
|
|
45918
48408
|
// src/cli/skillHistory.ts
|
|
45919
|
-
import { promises as
|
|
48409
|
+
import { promises as fs26, existsSync as existsSync39, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync4, mkdirSync as mkdirSync17 } from "node:fs";
|
|
45920
48410
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
45921
48411
|
async function readSkillHistory(file2) {
|
|
45922
48412
|
let raw = "";
|
|
45923
48413
|
try {
|
|
45924
|
-
raw = await
|
|
48414
|
+
raw = await fs26.readFile(file2, "utf-8");
|
|
45925
48415
|
} catch {
|
|
45926
48416
|
return [];
|
|
45927
48417
|
}
|
|
@@ -46038,7 +48528,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
|
|
|
46038
48528
|
});
|
|
46039
48529
|
}
|
|
46040
48530
|
async function handleSkillStats(ctx, skillId) {
|
|
46041
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
48531
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path43.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
46042
48532
|
try {
|
|
46043
48533
|
const records = await readSkillHistory(historyFile);
|
|
46044
48534
|
const stats = getSkillStats(records, skillId);
|
|
@@ -46054,7 +48544,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
46054
48544
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
46055
48545
|
return;
|
|
46056
48546
|
}
|
|
46057
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
48547
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path43.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
46058
48548
|
try {
|
|
46059
48549
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
46060
48550
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -46478,6 +48968,18 @@ function useSlashDispatch(params) {
|
|
|
46478
48968
|
await handleKrakenGraph({ setMessages, cwd: process.cwd(), sessionId: sid }, result.graphPrompt ?? "");
|
|
46479
48969
|
return;
|
|
46480
48970
|
}
|
|
48971
|
+
if (result.kind === "kraken_fanout") {
|
|
48972
|
+
const sid = (sessionId || "default").trim();
|
|
48973
|
+
await handleKrakenFanout(
|
|
48974
|
+
{ setMessages, cwd: process.cwd(), sessionId: sid },
|
|
48975
|
+
result.fanoutArgs ?? ""
|
|
48976
|
+
);
|
|
48977
|
+
return;
|
|
48978
|
+
}
|
|
48979
|
+
if (result.kind === "kraken_workbench") {
|
|
48980
|
+
await handleKrakenWorkbench({ setMessages, cwd: process.cwd() });
|
|
48981
|
+
return;
|
|
48982
|
+
}
|
|
46481
48983
|
if (result.kind === "phase_set" && result.phaseTarget) {
|
|
46482
48984
|
const { setPhase: setPhase2 } = await Promise.resolve().then(() => (init_phaseState(), phaseState_exports));
|
|
46483
48985
|
const { describePhase: describePhase2 } = await Promise.resolve().then(() => (init_phase(), phase_exports));
|
|
@@ -47061,7 +49563,7 @@ function SplashGate({
|
|
|
47061
49563
|
// src/cli/components/PluginGate.tsx
|
|
47062
49564
|
import React12, { useEffect as useEffect9, useState as useState10, useCallback as useCallback6 } from "react";
|
|
47063
49565
|
import { Box as Box11, Text as Text12, useStdin as useStdin4 } from "ink";
|
|
47064
|
-
|
|
49566
|
+
init_registry3();
|
|
47065
49567
|
init_prefs();
|
|
47066
49568
|
var CHOICE_INSTALL = "__install__";
|
|
47067
49569
|
var CHOICE_LATER = "__later__";
|
|
@@ -47501,6 +50003,8 @@ function parseHeadlessFlags(argv) {
|
|
|
47501
50003
|
let history2;
|
|
47502
50004
|
let once = false;
|
|
47503
50005
|
let krakenGraph;
|
|
50006
|
+
let planOnly = process.env.ZELARI_KRAKEN_PLAN_ONLY === "1" || process.env.ZELARI_KRAKEN_PLAN_ONLY === "true";
|
|
50007
|
+
let runPlan = process.env.ZELARI_KRAKEN_RUN_PLAN;
|
|
47504
50008
|
for (let i = 0; i < argv.length; i++) {
|
|
47505
50009
|
const arg = argv[i];
|
|
47506
50010
|
if (arg === "--headless") continue;
|
|
@@ -47594,6 +50098,11 @@ function parseHeadlessFlags(argv) {
|
|
|
47594
50098
|
} else if (arg === "--kraken-graph") {
|
|
47595
50099
|
krakenGraph = argv[i + 1];
|
|
47596
50100
|
i++;
|
|
50101
|
+
} else if (arg === "--plan-only") {
|
|
50102
|
+
planOnly = true;
|
|
50103
|
+
} else if (arg === "--run-plan") {
|
|
50104
|
+
runPlan = argv[i + 1];
|
|
50105
|
+
i++;
|
|
47597
50106
|
}
|
|
47598
50107
|
}
|
|
47599
50108
|
if (councilFlag && !modeExplicit) {
|
|
@@ -47621,7 +50130,9 @@ function parseHeadlessFlags(argv) {
|
|
|
47621
50130
|
model,
|
|
47622
50131
|
...history2 && history2.length > 0 ? { history: history2 } : {},
|
|
47623
50132
|
...once ? { once: true } : {},
|
|
47624
|
-
...krakenGraph ? { krakenGraph } : {}
|
|
50133
|
+
...krakenGraph ? { krakenGraph } : {},
|
|
50134
|
+
...planOnly ? { planOnly: true } : {},
|
|
50135
|
+
...runPlan ? { runPlan } : {}
|
|
47625
50136
|
}
|
|
47626
50137
|
};
|
|
47627
50138
|
}
|
|
@@ -47692,6 +50203,9 @@ function createStreamScrubber() {
|
|
|
47692
50203
|
|
|
47693
50204
|
// src/cli/runHeadless.ts
|
|
47694
50205
|
init_taskTool();
|
|
50206
|
+
import { promises as fs28 } from "node:fs";
|
|
50207
|
+
import path45 from "node:path";
|
|
50208
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
47695
50209
|
async function runHeadless(opts) {
|
|
47696
50210
|
resetTaskSpawnCount();
|
|
47697
50211
|
try {
|
|
@@ -47780,7 +50294,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
47780
50294
|
}
|
|
47781
50295
|
const { planTaskGraph: planTaskGraph2 } = await Promise.resolve().then(() => (init_planner(), planner_exports));
|
|
47782
50296
|
const { loadGraphSnapshot: loadGraphSnapshot2, formatSnapshotForPlanner: formatSnapshotForPlanner2 } = await Promise.resolve().then(() => (init_graphMemory(), graphMemory_exports));
|
|
47783
|
-
const { formatKrakenGraphAscii: formatKrakenGraphAscii2 } = await Promise.resolve().then(() => (init_graphStatus(), graphStatus_exports));
|
|
50297
|
+
const { formatKrakenGraphAscii: formatKrakenGraphAscii2, formatKrakenGraphDigest: formatKrakenGraphDigest2 } = await Promise.resolve().then(() => (init_graphStatus(), graphStatus_exports));
|
|
47784
50298
|
const { AuditLogger: AuditLogger2 } = await Promise.resolve().then(() => (init_auditLogger(), auditLogger_exports));
|
|
47785
50299
|
const { createKrakenSubAgentContextFactory: createKrakenSubAgentContextFactory2 } = await Promise.resolve().then(() => (init_toolRegistry(), toolRegistry_exports));
|
|
47786
50300
|
const cwd = process.cwd();
|
|
@@ -47793,18 +50307,69 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
47793
50307
|
`);
|
|
47794
50308
|
}
|
|
47795
50309
|
};
|
|
50310
|
+
const abort = new AbortController();
|
|
50311
|
+
const onSigint = () => {
|
|
50312
|
+
log("SIGINT \u2014 cancelling the graph; press Ctrl-C again to force quit");
|
|
50313
|
+
abort.abort();
|
|
50314
|
+
};
|
|
50315
|
+
process.once("SIGINT", onSigint);
|
|
47796
50316
|
try {
|
|
50317
|
+
let preflightGraph;
|
|
50318
|
+
if (opts.runPlan && opts.runPlan.trim() !== "") {
|
|
50319
|
+
const planPath = path45.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
|
|
50320
|
+
log(`loading pre-flight plan: ${planPath}`);
|
|
50321
|
+
let raw;
|
|
50322
|
+
try {
|
|
50323
|
+
raw = await fs28.readFile(planPath, "utf8");
|
|
50324
|
+
} catch (e) {
|
|
50325
|
+
log(`plan file not found: ${planPath} (${e.message})`);
|
|
50326
|
+
return 1;
|
|
50327
|
+
}
|
|
50328
|
+
let planJson;
|
|
50329
|
+
try {
|
|
50330
|
+
planJson = JSON.parse(raw);
|
|
50331
|
+
} catch (e) {
|
|
50332
|
+
log(`plan file is malformed JSON: ${e.message}`);
|
|
50333
|
+
return 1;
|
|
50334
|
+
}
|
|
50335
|
+
if (!planJson || !Array.isArray(planJson.nodes)) {
|
|
50336
|
+
log(`plan file is malformed: missing "nodes" array`);
|
|
50337
|
+
return 1;
|
|
50338
|
+
}
|
|
50339
|
+
const { createGraph: createGraph2, validateGraph: validateGraph2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
50340
|
+
const validated = createGraph2(planJson.graphId ?? opts.runPlan, planJson.nodes);
|
|
50341
|
+
validateGraph2(validated);
|
|
50342
|
+
preflightGraph = validated;
|
|
50343
|
+
log(`pre-flight plan loaded (${validated.nodes.size} nodes); executing`);
|
|
50344
|
+
}
|
|
47797
50345
|
log(`planning kraken graph: ${prompt}`);
|
|
47798
50346
|
const previous = await loadGraphSnapshot2(cwd);
|
|
47799
50347
|
const previousAttempt = formatSnapshotForPlanner2(previous);
|
|
47800
50348
|
if (previousAttempt) log("resuming from the previous unfinished graph");
|
|
47801
|
-
const graph = await planTaskGraph2({
|
|
50349
|
+
const graph = preflightGraph ?? await planTaskGraph2({
|
|
47802
50350
|
prompt,
|
|
47803
50351
|
provider,
|
|
47804
50352
|
model,
|
|
50353
|
+
cwd,
|
|
47805
50354
|
...previousAttempt ? { previousAttempt } : {}
|
|
47806
50355
|
});
|
|
47807
50356
|
log(formatKrakenGraphAscii2(graph));
|
|
50357
|
+
if (opts.planOnly) {
|
|
50358
|
+
const planId = randomUUID6();
|
|
50359
|
+
const planDir = path45.join(cwd, ".zelari", "radio");
|
|
50360
|
+
const planPath = path45.join(planDir, `plan-${planId}.json`);
|
|
50361
|
+
await fs28.mkdir(planDir, { recursive: true });
|
|
50362
|
+
await fs28.writeFile(planPath, JSON.stringify(graph, null, 2), "utf8");
|
|
50363
|
+
log(`plan-only: wrote ${planPath} (${graph.nodes.size} nodes)`);
|
|
50364
|
+
log(
|
|
50365
|
+
`re-run with ZELARI_KRAKEN_RUN_PLAN=${planId} to execute (or --run-plan <id> when the desktop wiring is in place)`
|
|
50366
|
+
);
|
|
50367
|
+
if (opts.output === "json") {
|
|
50368
|
+
emitEvent({ type: "log", message: `plan_only_id=${planId}` });
|
|
50369
|
+
emitEvent({ type: "log", message: `plan_only_path=${planPath}` });
|
|
50370
|
+
}
|
|
50371
|
+
return 0;
|
|
50372
|
+
}
|
|
47808
50373
|
const audit = new AuditLogger2();
|
|
47809
50374
|
const executor = new KrakenGraphExecutor2({
|
|
47810
50375
|
taskToolDeps: {
|
|
@@ -47823,10 +50388,20 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
47823
50388
|
},
|
|
47824
50389
|
parentCwd: cwd,
|
|
47825
50390
|
sessionId,
|
|
47826
|
-
goal: prompt
|
|
50391
|
+
goal: prompt,
|
|
50392
|
+
signal: abort.signal
|
|
47827
50393
|
});
|
|
47828
50394
|
const summary = await executor.execute(graph);
|
|
47829
|
-
|
|
50395
|
+
if (summary.cancelled) log("graph cancelled \u2014 partial results below");
|
|
50396
|
+
const finalAscii = `${formatKrakenGraphAscii2(summary.graph)}
|
|
50397
|
+
|
|
50398
|
+
${formatKrakenGraphDigest2(
|
|
50399
|
+
summary.graph,
|
|
50400
|
+
{
|
|
50401
|
+
durationsMs: summary.durationsMs,
|
|
50402
|
+
unresolvedFindings: summary.unresolvedFindings
|
|
50403
|
+
}
|
|
50404
|
+
)}`;
|
|
47830
50405
|
if (opts.output === "json") {
|
|
47831
50406
|
emitEvent({ type: "message_start" });
|
|
47832
50407
|
emitEvent({ type: "message_delta", delta: finalAscii });
|
|
@@ -47853,6 +50428,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
47853
50428
|
`);
|
|
47854
50429
|
}
|
|
47855
50430
|
return 2;
|
|
50431
|
+
} finally {
|
|
50432
|
+
process.off("SIGINT", onSigint);
|
|
47856
50433
|
}
|
|
47857
50434
|
}
|
|
47858
50435
|
function planModeFromOpts(opts) {
|
|
@@ -48668,7 +51245,7 @@ ${ragContext}` : slicePrompt;
|
|
|
48668
51245
|
init_desktopConfig();
|
|
48669
51246
|
|
|
48670
51247
|
// src/cli/plugins/cliFlags.ts
|
|
48671
|
-
|
|
51248
|
+
init_registry3();
|
|
48672
51249
|
function getArg(argv, flag) {
|
|
48673
51250
|
const i = argv.indexOf(flag);
|
|
48674
51251
|
if (i < 0) return void 0;
|
|
@@ -48968,7 +51545,7 @@ function upsertSkill(opts) {
|
|
|
48968
51545
|
}
|
|
48969
51546
|
dir = getProjectSkillsDir(root);
|
|
48970
51547
|
}
|
|
48971
|
-
const
|
|
51548
|
+
const path47 = skillFilePath(dir, name);
|
|
48972
51549
|
const content = serializeSkillMd({
|
|
48973
51550
|
name,
|
|
48974
51551
|
description,
|
|
@@ -48977,13 +51554,13 @@ function upsertSkill(opts) {
|
|
|
48977
51554
|
tools: opts.tools,
|
|
48978
51555
|
cost: opts.cost
|
|
48979
51556
|
});
|
|
48980
|
-
const parsed = parseSkillMd(content,
|
|
51557
|
+
const parsed = parseSkillMd(content, path47);
|
|
48981
51558
|
if (!parsed) {
|
|
48982
51559
|
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
48983
51560
|
}
|
|
48984
|
-
mkdirSync18(dirname9(
|
|
48985
|
-
writeFileSync20(
|
|
48986
|
-
return { ok: true, path:
|
|
51561
|
+
mkdirSync18(dirname9(path47), { recursive: true });
|
|
51562
|
+
writeFileSync20(path47, content, "utf8");
|
|
51563
|
+
return { ok: true, path: path47 };
|
|
48987
51564
|
}
|
|
48988
51565
|
function removeSkill(opts) {
|
|
48989
51566
|
const name = opts.name.trim().toLowerCase();
|
|
@@ -49001,8 +51578,8 @@ function removeSkill(opts) {
|
|
|
49001
51578
|
dir = getProjectSkillsDir(root);
|
|
49002
51579
|
}
|
|
49003
51580
|
const skillDir = join34(dir, name);
|
|
49004
|
-
const
|
|
49005
|
-
if (!existsSync42(
|
|
51581
|
+
const path47 = skillFilePath(dir, name);
|
|
51582
|
+
if (!existsSync42(path47) && !existsSync42(skillDir)) {
|
|
49006
51583
|
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
49007
51584
|
}
|
|
49008
51585
|
try {
|
|
@@ -49013,7 +51590,7 @@ function removeSkill(opts) {
|
|
|
49013
51590
|
error: err instanceof Error ? err.message : String(err)
|
|
49014
51591
|
};
|
|
49015
51592
|
}
|
|
49016
|
-
return { ok: true, path:
|
|
51593
|
+
return { ok: true, path: path47 };
|
|
49017
51594
|
}
|
|
49018
51595
|
|
|
49019
51596
|
// src/cli/generateSkillFromUrl.ts
|
|
@@ -49103,8 +51680,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
|
|
|
49103
51680
|
let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
49104
51681
|
if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
|
|
49105
51682
|
try {
|
|
49106
|
-
const
|
|
49107
|
-
name =
|
|
51683
|
+
const path47 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
51684
|
+
name = path47 && /^[a-z0-9]/.test(path47) ? path47 : "imported-skill";
|
|
49108
51685
|
} catch {
|
|
49109
51686
|
name = "imported-skill";
|
|
49110
51687
|
}
|