zelari-code 1.17.0 → 1.18.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/app.js +1 -0
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/browser/driver.js +5 -1
- package/dist/cli/browser/driver.js.map +1 -1
- package/dist/cli/hooks/chatStats.js +35 -28
- package/dist/cli/hooks/chatStats.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +48 -30
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/main.bundled.js +551 -95
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +13 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/plugins/cliFlags.js +91 -0
- package/dist/cli/plugins/cliFlags.js.map +1 -0
- package/dist/cli/plugins/installer.js +73 -11
- package/dist/cli/plugins/installer.js.map +1 -1
- package/dist/cli/runHeadless.js +21 -3
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/slashHandlers/cache.js +11 -4
- package/dist/cli/slashHandlers/cache.js.map +1 -1
- package/dist/cli/slashHandlers/state.js +19 -5
- package/dist/cli/slashHandlers/state.js.map +1 -1
- package/dist/cli/state/commitHelpers.js +13 -2
- package/dist/cli/state/commitHelpers.js.map +1 -1
- package/dist/cli/state/loadDurableContext.js +42 -0
- package/dist/cli/state/loadDurableContext.js.map +1 -0
- package/dist/cli/workspace/composeContext.js +7 -1
- package/dist/cli/workspace/composeContext.js.map +1 -1
- package/dist/cli/zelariMission.js +7 -14
- package/dist/cli/zelariMission.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -18415,6 +18415,7 @@ Earlier members' outputs are shared context. Build on them; do not re-derive or
|
|
|
18415
18415
|
- Pass complete, valid arguments. Required parameters must be present.
|
|
18416
18416
|
- Prefer tools over asking the user to paste file contents.
|
|
18417
18417
|
- After durable changes, briefly name what you created or modified.
|
|
18418
|
+
- **Act, don't narrate**: if you will edit/fix files, call the tools in this turn. Do not restate the same diagnosis or "I will fix\u2026" plan on a loop without tool calls.
|
|
18418
18419
|
- Text-only tool blocks (\`---TOOLS---\` JSON) are a legacy fallback \u2014 use them only if the runtime has no native tool channel.`
|
|
18419
18420
|
};
|
|
18420
18421
|
CODING_PRACTICES_MODULE = {
|
|
@@ -18429,7 +18430,8 @@ Earlier members' outputs are shared context. Build on them; do not re-derive or
|
|
|
18429
18430
|
- **Don't invent**: no fake APIs, deps, or config keys \u2014 discover from the tree or package manifests.
|
|
18430
18431
|
- **Verify**: after non-trivial edits, run the project's tests/typecheck/build when available; fix failures you introduced.
|
|
18431
18432
|
- **Use project scripts**: prefer package.json / Makefile / existing tooling over ad-hoc commands.
|
|
18432
|
-
- **Finish**: list the paths you wrote/edited and how to verify. If you wrote nothing, say so honestly \u2014 do not invent a done report
|
|
18433
|
+
- **Finish**: list the paths you wrote/edited and how to verify. If you wrote nothing, say so honestly \u2014 do not invent a done report.
|
|
18434
|
+
- **No spam**: never repeat the same paragraph or status line. One diagnosis, then tools (or one short final answer).`
|
|
18433
18435
|
};
|
|
18434
18436
|
CLARIFICATION_PROTOCOL_MODULE = {
|
|
18435
18437
|
type: "custom",
|
|
@@ -19124,6 +19126,159 @@ var init_events = __esm({
|
|
|
19124
19126
|
}
|
|
19125
19127
|
});
|
|
19126
19128
|
|
|
19129
|
+
// packages/core/dist/core/textLoopDetect.js
|
|
19130
|
+
function normalizeLoopUnit(s) {
|
|
19131
|
+
return s.replace(/<\/?small>/gi, "").replace(/<\/?(?:p|div|span|b|i|em|strong|br)\b[^>]*>/gi, "").replace(/\s+/g, " ").trim();
|
|
19132
|
+
}
|
|
19133
|
+
function detectChunkSequenceLoop(chunks, kind) {
|
|
19134
|
+
if (chunks.length < MIN_REPEATS)
|
|
19135
|
+
return { looping: false };
|
|
19136
|
+
const maxK = Math.min(12, Math.floor(chunks.length / MIN_REPEATS));
|
|
19137
|
+
for (let k = 1; k <= maxK; k++) {
|
|
19138
|
+
const unitParts = chunks.slice(chunks.length - k);
|
|
19139
|
+
const unitKey = unitParts.join("\n");
|
|
19140
|
+
if (normalizeLoopUnit(unitKey).length < MIN_UNIT)
|
|
19141
|
+
continue;
|
|
19142
|
+
let count = 1;
|
|
19143
|
+
let pos = chunks.length - k;
|
|
19144
|
+
while (pos - k >= 0) {
|
|
19145
|
+
const prev2 = chunks.slice(pos - k, pos);
|
|
19146
|
+
let same = true;
|
|
19147
|
+
for (let i = 0; i < k; i++) {
|
|
19148
|
+
if (prev2[i] !== unitParts[i]) {
|
|
19149
|
+
same = false;
|
|
19150
|
+
break;
|
|
19151
|
+
}
|
|
19152
|
+
}
|
|
19153
|
+
if (!same)
|
|
19154
|
+
break;
|
|
19155
|
+
count++;
|
|
19156
|
+
pos -= k;
|
|
19157
|
+
}
|
|
19158
|
+
if (count >= MIN_REPEATS) {
|
|
19159
|
+
return {
|
|
19160
|
+
looping: true,
|
|
19161
|
+
unit: unitKey,
|
|
19162
|
+
count,
|
|
19163
|
+
kind
|
|
19164
|
+
};
|
|
19165
|
+
}
|
|
19166
|
+
}
|
|
19167
|
+
return { looping: false };
|
|
19168
|
+
}
|
|
19169
|
+
function detectSuffixPeriod(text) {
|
|
19170
|
+
const compact = text.replace(/\s+/g, " ").trim();
|
|
19171
|
+
if (compact.length < MIN_UNIT * MIN_REPEATS)
|
|
19172
|
+
return { looping: false };
|
|
19173
|
+
const tail = compact.slice(-Math.min(compact.length, MAX_TAIL));
|
|
19174
|
+
const maxP = Math.min(MAX_PERIOD, Math.floor(tail.length / MIN_REPEATS));
|
|
19175
|
+
for (let period = MIN_UNIT; period <= maxP; period++) {
|
|
19176
|
+
const need = period * MIN_REPEATS;
|
|
19177
|
+
if (tail.length < need)
|
|
19178
|
+
continue;
|
|
19179
|
+
const unit = tail.slice(tail.length - period);
|
|
19180
|
+
if (normalizeLoopUnit(unit).length < MIN_UNIT * 0.55)
|
|
19181
|
+
continue;
|
|
19182
|
+
let ok = true;
|
|
19183
|
+
for (let r = 1; r < MIN_REPEATS; r++) {
|
|
19184
|
+
const start = tail.length - period * (r + 1);
|
|
19185
|
+
if (tail.slice(start, start + period) !== unit) {
|
|
19186
|
+
ok = false;
|
|
19187
|
+
break;
|
|
19188
|
+
}
|
|
19189
|
+
}
|
|
19190
|
+
if (!ok)
|
|
19191
|
+
continue;
|
|
19192
|
+
let count = MIN_REPEATS;
|
|
19193
|
+
let end = tail.length - period * MIN_REPEATS;
|
|
19194
|
+
while (end >= period && tail.slice(end - period, end) === unit) {
|
|
19195
|
+
count++;
|
|
19196
|
+
end -= period;
|
|
19197
|
+
}
|
|
19198
|
+
return {
|
|
19199
|
+
looping: true,
|
|
19200
|
+
unit,
|
|
19201
|
+
count,
|
|
19202
|
+
kind: "suffix"
|
|
19203
|
+
};
|
|
19204
|
+
}
|
|
19205
|
+
return { looping: false };
|
|
19206
|
+
}
|
|
19207
|
+
function detectAssistantTextLoop(text) {
|
|
19208
|
+
if (!text || text.length < MIN_UNIT * MIN_REPEATS) {
|
|
19209
|
+
return { looping: false };
|
|
19210
|
+
}
|
|
19211
|
+
const nl = text.replace(/\r\n/g, "\n");
|
|
19212
|
+
const paragraphs = nl.split(/\n\s*\n+/).map((p3) => normalizeLoopUnit(p3)).filter((p3) => p3.length > 0);
|
|
19213
|
+
const paraHit = detectChunkSequenceLoop(paragraphs, "paragraph");
|
|
19214
|
+
if (paraHit.looping)
|
|
19215
|
+
return paraHit;
|
|
19216
|
+
const lines = nl.split("\n").map((l) => normalizeLoopUnit(l)).filter((l) => l.length > 0);
|
|
19217
|
+
const lineHit = detectChunkSequenceLoop(lines, "line");
|
|
19218
|
+
if (lineHit.looping)
|
|
19219
|
+
return lineHit;
|
|
19220
|
+
return detectSuffixPeriod(nl);
|
|
19221
|
+
}
|
|
19222
|
+
function collapseLoopedAssistantText(text) {
|
|
19223
|
+
const hit = detectAssistantTextLoop(text);
|
|
19224
|
+
if (!hit.looping)
|
|
19225
|
+
return text;
|
|
19226
|
+
const note = `
|
|
19227
|
+
|
|
19228
|
+
[system: stopped repeating the same text \xD7${hit.count}; call tools or finish.]`;
|
|
19229
|
+
if (hit.kind === "paragraph" || hit.kind === "line") {
|
|
19230
|
+
const joinSep = hit.kind === "paragraph" ? "\n\n" : "\n";
|
|
19231
|
+
const unitParts = hit.unit.split("\n").map((p3) => normalizeLoopUnit(p3));
|
|
19232
|
+
const k = unitParts.length;
|
|
19233
|
+
if (k === 0)
|
|
19234
|
+
return text + note;
|
|
19235
|
+
const rawParts = text.replace(/\r\n/g, "\n").split(hit.kind === "paragraph" ? /\n\s*\n+/ : /\n/).filter((p3) => normalizeLoopUnit(p3).length > 0);
|
|
19236
|
+
if (rawParts.length < k * MIN_REPEATS) {
|
|
19237
|
+
return collapseByCharBudget(text, hit.unit, hit.count) + note;
|
|
19238
|
+
}
|
|
19239
|
+
let trailBlocks = 0;
|
|
19240
|
+
let pos = rawParts.length;
|
|
19241
|
+
while (pos >= k) {
|
|
19242
|
+
let same = true;
|
|
19243
|
+
for (let i = 0; i < k; i++) {
|
|
19244
|
+
if (normalizeLoopUnit(rawParts[pos - k + i]) !== unitParts[i]) {
|
|
19245
|
+
same = false;
|
|
19246
|
+
break;
|
|
19247
|
+
}
|
|
19248
|
+
}
|
|
19249
|
+
if (!same)
|
|
19250
|
+
break;
|
|
19251
|
+
trailBlocks++;
|
|
19252
|
+
pos -= k;
|
|
19253
|
+
}
|
|
19254
|
+
if (trailBlocks < MIN_REPEATS) {
|
|
19255
|
+
return collapseByCharBudget(text, hit.unit, hit.count) + note;
|
|
19256
|
+
}
|
|
19257
|
+
const keepBlocks = 2;
|
|
19258
|
+
const keepParts = rawParts.slice(0, rawParts.length - k * (trailBlocks - keepBlocks));
|
|
19259
|
+
return keepParts.join(joinSep).trimEnd() + note;
|
|
19260
|
+
}
|
|
19261
|
+
return collapseByCharBudget(text, hit.unit, hit.count) + note;
|
|
19262
|
+
}
|
|
19263
|
+
function collapseByCharBudget(text, unit, count) {
|
|
19264
|
+
const unitLen = Math.max(unit.length, MIN_UNIT);
|
|
19265
|
+
if (count < MIN_REPEATS)
|
|
19266
|
+
return text;
|
|
19267
|
+
const drop = unitLen * (count - 2);
|
|
19268
|
+
const cut = Math.max(0, text.length - drop);
|
|
19269
|
+
return text.slice(0, cut).trimEnd();
|
|
19270
|
+
}
|
|
19271
|
+
var MIN_UNIT, MIN_REPEATS, MAX_TAIL, MAX_PERIOD;
|
|
19272
|
+
var init_textLoopDetect = __esm({
|
|
19273
|
+
"packages/core/dist/core/textLoopDetect.js"() {
|
|
19274
|
+
"use strict";
|
|
19275
|
+
MIN_UNIT = 48;
|
|
19276
|
+
MIN_REPEATS = 3;
|
|
19277
|
+
MAX_TAIL = 6e3;
|
|
19278
|
+
MAX_PERIOD = 600;
|
|
19279
|
+
}
|
|
19280
|
+
});
|
|
19281
|
+
|
|
19127
19282
|
// packages/core/dist/core/AgentHarness.js
|
|
19128
19283
|
function hashToolCall(toolName, args) {
|
|
19129
19284
|
const canonical = stableStringify(args);
|
|
@@ -19362,6 +19517,8 @@ var init_AgentHarness = __esm({
|
|
|
19362
19517
|
"packages/core/dist/core/AgentHarness.js"() {
|
|
19363
19518
|
"use strict";
|
|
19364
19519
|
init_events();
|
|
19520
|
+
init_textLoopDetect();
|
|
19521
|
+
init_textLoopDetect();
|
|
19365
19522
|
AgentHarness = class {
|
|
19366
19523
|
config;
|
|
19367
19524
|
eventBus;
|
|
@@ -19811,6 +19968,7 @@ ${shared2.content}`,
|
|
|
19811
19968
|
const turnToolCalls = [];
|
|
19812
19969
|
const turnToolResults = [];
|
|
19813
19970
|
const pendingNativeTools = [];
|
|
19971
|
+
let lastLoopCheckLen = 0;
|
|
19814
19972
|
for await (const delta of stream) {
|
|
19815
19973
|
if (this.cancelled) {
|
|
19816
19974
|
const cancelEvent = createBrainEvent("error", this.sessionId, {
|
|
@@ -19827,6 +19985,29 @@ ${shared2.content}`,
|
|
|
19827
19985
|
const deltaEvent = createBrainEvent("message_delta", this.sessionId, { messageId, delta: delta.delta, ...this.memberFields() });
|
|
19828
19986
|
this.emit(deltaEvent);
|
|
19829
19987
|
yield deltaEvent;
|
|
19988
|
+
if (turnText.length >= 48 * 3 && turnText.length - lastLoopCheckLen >= 48) {
|
|
19989
|
+
lastLoopCheckLen = turnText.length;
|
|
19990
|
+
const loopHit = detectAssistantTextLoop(turnText);
|
|
19991
|
+
if (loopHit.looping) {
|
|
19992
|
+
const loopErr = createBrainEvent("error", this.sessionId, {
|
|
19993
|
+
severity: "recoverable",
|
|
19994
|
+
message: `Assistant text loop detected (same block \xD7${loopHit.count}). Stopped generation \u2014 model was repeating instead of calling tools or finishing. Retry or ask it to apply changes with tools.`,
|
|
19995
|
+
code: "assistant_text_loop"
|
|
19996
|
+
});
|
|
19997
|
+
this.emit(loopErr);
|
|
19998
|
+
yield loopErr;
|
|
19999
|
+
finishRef.value = "stop";
|
|
20000
|
+
const sealed = collapseLoopedAssistantText(turnText);
|
|
20001
|
+
if (sealed.length > 0 || turnReasoning.length > 0) {
|
|
20002
|
+
this.config.messages.push({
|
|
20003
|
+
role: "assistant",
|
|
20004
|
+
content: sealed,
|
|
20005
|
+
...turnReasoning.length > 0 ? { reasoningContent: turnReasoning } : {}
|
|
20006
|
+
});
|
|
20007
|
+
}
|
|
20008
|
+
break;
|
|
20009
|
+
}
|
|
20010
|
+
}
|
|
19830
20011
|
} else if (delta.kind === "thinking") {
|
|
19831
20012
|
turnReasoning += delta.delta;
|
|
19832
20013
|
const thinkEvent = createBrainEvent("thinking_delta", this.sessionId, {
|
|
@@ -20247,7 +20428,10 @@ var harness_exports = {};
|
|
|
20247
20428
|
__export(harness_exports, {
|
|
20248
20429
|
AgentHarness: () => AgentHarness,
|
|
20249
20430
|
SessionJsonlWriter: () => SessionJsonlWriter,
|
|
20431
|
+
collapseLoopedAssistantText: () => collapseLoopedAssistantText,
|
|
20432
|
+
detectAssistantTextLoop: () => detectAssistantTextLoop,
|
|
20250
20433
|
hashToolCall: () => hashToolCall,
|
|
20434
|
+
normalizeLoopUnit: () => normalizeLoopUnit,
|
|
20251
20435
|
normalizeTextToolArgs: () => normalizeTextToolArgs,
|
|
20252
20436
|
parseMinimaxStyleToolCalls: () => parseMinimaxStyleToolCalls,
|
|
20253
20437
|
parseTextToolCalls: () => parseTextToolCalls,
|
|
@@ -22449,7 +22633,7 @@ async function runBrowserCheck(options, loader) {
|
|
|
22449
22633
|
if (!pw) {
|
|
22450
22634
|
return {
|
|
22451
22635
|
...base,
|
|
22452
|
-
error: "browser automation unavailable \u2014 install
|
|
22636
|
+
error: "browser automation unavailable \u2014 Playwright is not installed in this workspace. Install it with: `zelari-code --plugins-install playwright --cwd .` (or Desktop banner \u201CInstall\u201D, or CLI `/plugins install playwright`, or `npm i -D playwright && npx playwright install chromium`). Then re-run browser_check."
|
|
22453
22637
|
};
|
|
22454
22638
|
}
|
|
22455
22639
|
const timeout = options.timeoutMs ?? 15e3;
|
|
@@ -28622,7 +28806,7 @@ function composeProjectContext(input) {
|
|
|
28622
28806
|
default: 3e3,
|
|
28623
28807
|
min: 200
|
|
28624
28808
|
});
|
|
28625
|
-
const wantDurable = input.includeDurableState ?? (input.mode === "council" || input.mode === "zelari");
|
|
28809
|
+
const wantDurable = input.includeDurableState ?? (process.env.ZELARI_STATE !== "0" && (input.mode === "council" || input.mode === "zelari" || input.mode === "agent"));
|
|
28626
28810
|
let durableRaw = input.durableState?.trim() ?? "";
|
|
28627
28811
|
if (!durableRaw && wantDurable) {
|
|
28628
28812
|
durableRaw = readDurableHeadSync(cwd);
|
|
@@ -28712,6 +28896,42 @@ var init_planDetect = __esm({
|
|
|
28712
28896
|
}
|
|
28713
28897
|
});
|
|
28714
28898
|
|
|
28899
|
+
// src/cli/state/loadDurableContext.ts
|
|
28900
|
+
var loadDurableContext_exports = {};
|
|
28901
|
+
__export(loadDurableContext_exports, {
|
|
28902
|
+
clearDurableContextCache: () => clearDurableContextCache,
|
|
28903
|
+
loadDurableContext: () => loadDurableContext
|
|
28904
|
+
});
|
|
28905
|
+
function clearDurableContextCache() {
|
|
28906
|
+
cache = null;
|
|
28907
|
+
}
|
|
28908
|
+
async function loadDurableContext(projectRoot, opts) {
|
|
28909
|
+
const env = opts?.env ?? process.env;
|
|
28910
|
+
if (!isStateEnabled(env)) return "";
|
|
28911
|
+
const cacheMs = opts?.cacheMs ?? DEFAULT_CACHE_MS;
|
|
28912
|
+
const now = Date.now();
|
|
28913
|
+
if (cache && cache.projectRoot === projectRoot && now - cache.at < cacheMs) {
|
|
28914
|
+
return cache.text;
|
|
28915
|
+
}
|
|
28916
|
+
try {
|
|
28917
|
+
const store = await getStateStore(projectRoot, env);
|
|
28918
|
+
const text = await store.materializeContext(void 0, opts?.maxChars);
|
|
28919
|
+
cache = { text: text || "", at: now, projectRoot };
|
|
28920
|
+
return cache.text;
|
|
28921
|
+
} catch {
|
|
28922
|
+
return "";
|
|
28923
|
+
}
|
|
28924
|
+
}
|
|
28925
|
+
var DEFAULT_CACHE_MS, cache;
|
|
28926
|
+
var init_loadDurableContext = __esm({
|
|
28927
|
+
"src/cli/state/loadDurableContext.ts"() {
|
|
28928
|
+
"use strict";
|
|
28929
|
+
init_fileStateStore();
|
|
28930
|
+
DEFAULT_CACHE_MS = 2e3;
|
|
28931
|
+
cache = null;
|
|
28932
|
+
}
|
|
28933
|
+
});
|
|
28934
|
+
|
|
28715
28935
|
// src/cli/workspace/storage.ts
|
|
28716
28936
|
var storage_exports = {};
|
|
28717
28937
|
__export(storage_exports, {
|
|
@@ -31539,8 +31759,8 @@ __export(commitHelpers_exports, {
|
|
|
31539
31759
|
async function tryStateCommit(args) {
|
|
31540
31760
|
try {
|
|
31541
31761
|
const store = args.store ?? await getStateStore(args.projectRoot, args.env);
|
|
31542
|
-
let workspaceCheckpointId;
|
|
31543
|
-
if (args.withCheckpoint && (args.env ?? process.env).ZELARI_CHECKPOINT !== "0") {
|
|
31762
|
+
let workspaceCheckpointId = args.workspaceCheckpointId;
|
|
31763
|
+
if (!workspaceCheckpointId && args.withCheckpoint && (args.env ?? process.env).ZELARI_CHECKPOINT !== "0") {
|
|
31544
31764
|
const cp = await createCheckpoint(
|
|
31545
31765
|
args.projectRoot,
|
|
31546
31766
|
`state ${args.layer ?? args.label}`.slice(0, 80)
|
|
@@ -31556,9 +31776,15 @@ async function tryStateCommit(args) {
|
|
|
31556
31776
|
verification: args.verification,
|
|
31557
31777
|
changedPaths: args.changedPaths,
|
|
31558
31778
|
discoveries: args.discoveries,
|
|
31779
|
+
stablePromptHash: args.stablePromptHash,
|
|
31559
31780
|
force: args.force
|
|
31560
31781
|
});
|
|
31561
31782
|
if (!meta3.id) return { ok: false, error: "noop store", checkpointId: workspaceCheckpointId };
|
|
31783
|
+
try {
|
|
31784
|
+
const { clearDurableContextCache: clearDurableContextCache2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
|
|
31785
|
+
clearDurableContextCache2();
|
|
31786
|
+
} catch {
|
|
31787
|
+
}
|
|
31562
31788
|
return { ok: true, meta: meta3, checkpointId: workspaceCheckpointId };
|
|
31563
31789
|
} catch (err) {
|
|
31564
31790
|
return {
|
|
@@ -31840,14 +32066,7 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
31840
32066
|
limit: 8,
|
|
31841
32067
|
metadataFilter: { projectRoot: deps.projectRoot }
|
|
31842
32068
|
});
|
|
31843
|
-
|
|
31844
|
-
try {
|
|
31845
|
-
durableBlock = await stateStore.materializeContext();
|
|
31846
|
-
} catch {
|
|
31847
|
-
durableBlock = "";
|
|
31848
|
-
}
|
|
31849
|
-
const memBlock = formatMemoryHits(hits);
|
|
31850
|
-
const ragContext = [durableBlock, memBlock].filter(Boolean).join("\n\n");
|
|
32069
|
+
const ragContext = formatMemoryHits(hits);
|
|
31851
32070
|
const promptIter = runMode === "implementation" ? implStep : 1;
|
|
31852
32071
|
const slicePrompt = buildSlicePrompt(brief, userMessage, runMode, promptIter);
|
|
31853
32072
|
const implementerRetry = runMode === "implementation" && implStep > 1;
|
|
@@ -31919,7 +32138,9 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
31919
32138
|
sessionId: missionId,
|
|
31920
32139
|
verification: { ok: hard, ran: result.ran },
|
|
31921
32140
|
force: !hard,
|
|
31922
|
-
|
|
32141
|
+
// Prefer linking the mission-start checkpoint (no explosion).
|
|
32142
|
+
workspaceCheckpointId: missionCheckpointId,
|
|
32143
|
+
withCheckpoint: hard && !missionCheckpointId,
|
|
31923
32144
|
discoveries: discoveriesFromOutcome({
|
|
31924
32145
|
stepId: `${missionId}-${step}`,
|
|
31925
32146
|
synthesis: result.synthesisText,
|
|
@@ -31930,7 +32151,7 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
31930
32151
|
if (commitRes.ok && commitRes.meta?.id) {
|
|
31931
32152
|
if (hard) {
|
|
31932
32153
|
state2.lastGoodCommitId = commitRes.meta.id;
|
|
31933
|
-
if (commitRes.checkpointId) {
|
|
32154
|
+
if (commitRes.checkpointId && !missionCheckpointId) {
|
|
31934
32155
|
missionCheckpointId = commitRes.checkpointId;
|
|
31935
32156
|
}
|
|
31936
32157
|
}
|
|
@@ -36673,6 +36894,48 @@ function finalizeStreamingAssistant(setMessages) {
|
|
|
36673
36894
|
// src/cli/hooks/useChatTurn.ts
|
|
36674
36895
|
init_conversationContext();
|
|
36675
36896
|
|
|
36897
|
+
// src/cli/state/promptCacheStats.ts
|
|
36898
|
+
function emptyPromptCacheStats() {
|
|
36899
|
+
return {
|
|
36900
|
+
promptTokens: 0,
|
|
36901
|
+
cachedTokens: 0,
|
|
36902
|
+
premiumTokens: 0,
|
|
36903
|
+
hitRate: 0,
|
|
36904
|
+
estimatedCostUsd: 0,
|
|
36905
|
+
stableBustCount: 0,
|
|
36906
|
+
turns: 0
|
|
36907
|
+
};
|
|
36908
|
+
}
|
|
36909
|
+
function accumulatePromptCacheStats(prev2, turn) {
|
|
36910
|
+
const promptTokens = prev2.promptTokens + Math.max(0, turn.promptTokens);
|
|
36911
|
+
const cachedTokens = prev2.cachedTokens + Math.max(0, turn.cachedTokens);
|
|
36912
|
+
const premiumDelta = Math.max(0, turn.promptTokens - turn.cachedTokens);
|
|
36913
|
+
const premiumTokens = prev2.premiumTokens + premiumDelta;
|
|
36914
|
+
const hitRate = promptTokens > 0 ? cachedTokens / promptTokens : 0;
|
|
36915
|
+
let stableBustCount = prev2.stableBustCount;
|
|
36916
|
+
let lastStableHash = prev2.lastStableHash;
|
|
36917
|
+
if (turn.stableHash) {
|
|
36918
|
+
if (lastStableHash && lastStableHash !== turn.stableHash) {
|
|
36919
|
+
stableBustCount += 1;
|
|
36920
|
+
}
|
|
36921
|
+
lastStableHash = turn.stableHash;
|
|
36922
|
+
}
|
|
36923
|
+
return {
|
|
36924
|
+
promptTokens,
|
|
36925
|
+
cachedTokens,
|
|
36926
|
+
premiumTokens,
|
|
36927
|
+
hitRate,
|
|
36928
|
+
estimatedCostUsd: prev2.estimatedCostUsd + (turn.costUsd ?? 0),
|
|
36929
|
+
lastStableHash,
|
|
36930
|
+
stableBustCount,
|
|
36931
|
+
turns: prev2.turns + 1
|
|
36932
|
+
};
|
|
36933
|
+
}
|
|
36934
|
+
function formatCacheStatsLine(stats) {
|
|
36935
|
+
const pct = stats.promptTokens > 0 ? Math.round(stats.hitRate * 100) : 0;
|
|
36936
|
+
return `cache hit ${pct}% \xB7 premium ${stats.premiumTokens} \xB7 cached ${stats.cachedTokens} \xB7 stable busts ${stats.stableBustCount} \xB7 turns ${stats.turns}`;
|
|
36937
|
+
}
|
|
36938
|
+
|
|
36676
36939
|
// src/cli/hooks/chatStats.ts
|
|
36677
36940
|
function computeSessionStatsDelta(realUsage, userText, assistantContent, model, prev2, opts) {
|
|
36678
36941
|
const promptTokens = realUsage ? realUsage.promptTokens : Math.ceil(userText.length / 4);
|
|
@@ -36680,31 +36943,41 @@ function computeSessionStatsDelta(realUsage, userText, assistantContent, model,
|
|
|
36680
36943
|
const cachedPromptTokens = realUsage?.cachedPromptTokens ?? 0;
|
|
36681
36944
|
const turnCost = calculateCost(model, promptTokens, completionTokens, cachedPromptTokens);
|
|
36682
36945
|
const contextTokens = realUsage ? realUsage.totalTokens || promptTokens + completionTokens : promptTokens + completionTokens;
|
|
36683
|
-
const
|
|
36684
|
-
|
|
36685
|
-
|
|
36686
|
-
|
|
36687
|
-
|
|
36688
|
-
|
|
36689
|
-
|
|
36690
|
-
|
|
36691
|
-
|
|
36692
|
-
|
|
36693
|
-
|
|
36694
|
-
|
|
36695
|
-
|
|
36946
|
+
const prevCache = {
|
|
36947
|
+
...emptyPromptCacheStats(),
|
|
36948
|
+
promptTokens: prev2.promptTokens ?? 0,
|
|
36949
|
+
cachedTokens: prev2.cachedTokens ?? 0,
|
|
36950
|
+
premiumTokens: prev2.premiumTokens ?? 0,
|
|
36951
|
+
hitRate: prev2.cacheHitRate ?? 0,
|
|
36952
|
+
estimatedCostUsd: prev2.totalCostUsd,
|
|
36953
|
+
lastStableHash: prev2.lastStableHash,
|
|
36954
|
+
stableBustCount: prev2.stableBustCount ?? 0,
|
|
36955
|
+
turns: 0
|
|
36956
|
+
};
|
|
36957
|
+
const nextCache = accumulatePromptCacheStats(prevCache, {
|
|
36958
|
+
promptTokens,
|
|
36959
|
+
cachedTokens: cachedPromptTokens,
|
|
36960
|
+
costUsd: turnCost,
|
|
36961
|
+
stableHash: opts?.stableHash
|
|
36962
|
+
});
|
|
36696
36963
|
return {
|
|
36697
36964
|
totalTokens: prev2.totalTokens + promptTokens + completionTokens,
|
|
36698
36965
|
totalCostUsd: prev2.totalCostUsd + turnCost,
|
|
36699
|
-
cachedTokens:
|
|
36966
|
+
cachedTokens: nextCache.cachedTokens,
|
|
36700
36967
|
contextTokens,
|
|
36701
|
-
premiumTokens:
|
|
36702
|
-
cacheHitRate,
|
|
36703
|
-
promptTokens:
|
|
36704
|
-
lastStableHash,
|
|
36705
|
-
stableBustCount
|
|
36968
|
+
premiumTokens: nextCache.premiumTokens,
|
|
36969
|
+
cacheHitRate: nextCache.hitRate,
|
|
36970
|
+
promptTokens: nextCache.promptTokens,
|
|
36971
|
+
lastStableHash: nextCache.lastStableHash,
|
|
36972
|
+
stableBustCount: nextCache.stableBustCount
|
|
36706
36973
|
};
|
|
36707
36974
|
}
|
|
36975
|
+
function resolvePromptCacheTtl(env = process.env) {
|
|
36976
|
+
const raw = (env.ZELARI_PROMPT_CACHE_TTL ?? "auto").toLowerCase().trim();
|
|
36977
|
+
if (raw === "1h" || raw === "1hour" || raw === "long") return "1h";
|
|
36978
|
+
if (raw === "5m" || raw === "5min" || raw === "short") return "5m";
|
|
36979
|
+
return "auto";
|
|
36980
|
+
}
|
|
36708
36981
|
|
|
36709
36982
|
// src/cli/hooks/useChatTurn.ts
|
|
36710
36983
|
init_envNumber();
|
|
@@ -36901,14 +37174,22 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
36901
37174
|
const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
|
|
36902
37175
|
const { hasWorkspacePlan: hasWorkspacePlan2 } = await Promise.resolve().then(() => (init_planDetect(), planDetect_exports));
|
|
36903
37176
|
hasPlan = hasWorkspacePlan2(cwd);
|
|
37177
|
+
const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
|
|
37178
|
+
const durableState = await loadDurableContext2(cwd);
|
|
36904
37179
|
const composed = composeProjectContext2({
|
|
36905
37180
|
mode: "agent",
|
|
36906
37181
|
cwd,
|
|
36907
37182
|
userMessage: userText,
|
|
36908
|
-
includeLessons: false
|
|
37183
|
+
includeLessons: false,
|
|
37184
|
+
durableState: durableState || void 0,
|
|
37185
|
+
// Pre-loaded async — skip sync fallback double-read.
|
|
37186
|
+
includeDurableState: false
|
|
36909
37187
|
});
|
|
36910
37188
|
composedWorkspace = composed.workspaceContext;
|
|
36911
37189
|
composedInstructions = composed.projectInstructions;
|
|
37190
|
+
if (composed.ragContext) {
|
|
37191
|
+
composedWorkspace = [composedWorkspace, composed.ragContext].filter(Boolean).join("\n\n");
|
|
37192
|
+
}
|
|
36912
37193
|
for (const w of composed.warnings) {
|
|
36913
37194
|
appendSystem(setMessages, w, Date.now());
|
|
36914
37195
|
}
|
|
@@ -37514,6 +37795,35 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
37514
37795
|
let sliceRan = false;
|
|
37515
37796
|
let sliceDegraded = false;
|
|
37516
37797
|
const PROVIDER_ERROR_ABORT_THRESHOLD = 2;
|
|
37798
|
+
let councilCompose = { workspaceContext: formatHistoryForCouncil(4) || "" };
|
|
37799
|
+
try {
|
|
37800
|
+
const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
|
|
37801
|
+
const durableState = await loadDurableContext2(process.cwd());
|
|
37802
|
+
const composed = composeProjectContext2({
|
|
37803
|
+
mode: overrides.ragContext ? "zelari" : "council",
|
|
37804
|
+
cwd: process.cwd(),
|
|
37805
|
+
userMessage: effectiveText,
|
|
37806
|
+
memoryHits: overrides.ragContext,
|
|
37807
|
+
durableState: durableState || void 0,
|
|
37808
|
+
historySnippet: formatHistoryForCouncil(4) || void 0,
|
|
37809
|
+
includeLessons: true,
|
|
37810
|
+
includeDurableState: false
|
|
37811
|
+
});
|
|
37812
|
+
for (const w of composed.warnings) {
|
|
37813
|
+
appendSystem(setMessages, w, Date.now());
|
|
37814
|
+
}
|
|
37815
|
+
councilCompose = {
|
|
37816
|
+
workspaceContext: composed.workspaceContext,
|
|
37817
|
+
...composed.ragContext ? { ragContext: composed.ragContext } : {}
|
|
37818
|
+
};
|
|
37819
|
+
} catch {
|
|
37820
|
+
if (overrides.ragContext) {
|
|
37821
|
+
councilCompose = {
|
|
37822
|
+
workspaceContext: formatHistoryForCouncil(4) || "",
|
|
37823
|
+
ragContext: overrides.ragContext
|
|
37824
|
+
};
|
|
37825
|
+
}
|
|
37826
|
+
}
|
|
37517
37827
|
try {
|
|
37518
37828
|
for await (const event of dispatchCouncil2(effectiveText, {
|
|
37519
37829
|
apiKey: envConfig.apiKey,
|
|
@@ -37523,33 +37833,8 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
37523
37833
|
sessionId,
|
|
37524
37834
|
tools: councilToolRegistry,
|
|
37525
37835
|
feedbackStore: councilFeedbackStore,
|
|
37526
|
-
|
|
37527
|
-
|
|
37528
|
-
// Fail-soft: never block a council run if compose throws in tests/odd cwd.
|
|
37529
|
-
...(() => {
|
|
37530
|
-
try {
|
|
37531
|
-
const composed = composeProjectContext2({
|
|
37532
|
-
mode: overrides.ragContext ? "zelari" : "council",
|
|
37533
|
-
cwd: process.cwd(),
|
|
37534
|
-
userMessage: effectiveText,
|
|
37535
|
-
memoryHits: overrides.ragContext,
|
|
37536
|
-
historySnippet: formatHistoryForCouncil(4) || void 0,
|
|
37537
|
-
includeLessons: true
|
|
37538
|
-
});
|
|
37539
|
-
for (const w of composed.warnings) {
|
|
37540
|
-
appendSystem(setMessages, w, Date.now());
|
|
37541
|
-
}
|
|
37542
|
-
return {
|
|
37543
|
-
workspaceContext: composed.workspaceContext,
|
|
37544
|
-
...composed.ragContext ? { ragContext: composed.ragContext } : {}
|
|
37545
|
-
};
|
|
37546
|
-
} catch {
|
|
37547
|
-
return {
|
|
37548
|
-
workspaceContext: formatHistoryForCouncil(4) || "",
|
|
37549
|
-
...overrides.ragContext ? { ragContext: overrides.ragContext } : {}
|
|
37550
|
-
};
|
|
37551
|
-
}
|
|
37552
|
-
})(),
|
|
37836
|
+
workspaceContext: councilCompose.workspaceContext,
|
|
37837
|
+
...councilCompose.ragContext ? { ragContext: councilCompose.ragContext } : {},
|
|
37553
37838
|
maxToolCallsPerTurn: councilMaxToolCalls,
|
|
37554
37839
|
maxToolLoopIterations: councilMaxToolLoop,
|
|
37555
37840
|
...councilMaxToolLoopHard > 0 ? { maxToolLoopHardCap: councilMaxToolLoopHard } : {},
|
|
@@ -38848,20 +39133,28 @@ async function handleStateStatus(ctx) {
|
|
|
38848
39133
|
if (!head) {
|
|
38849
39134
|
appendSystem(
|
|
38850
39135
|
ctx.setMessages,
|
|
38851
|
-
"[state] no durable commits yet. Run a verified Zelari slice or `/state commit [label]
|
|
39136
|
+
"[state] no durable commits yet. Run a verified Zelari slice or `/state commit [label]`.\n Kill switch: ZELARI_STATE=0 disables the store."
|
|
38852
39137
|
);
|
|
38853
39138
|
return;
|
|
38854
39139
|
}
|
|
38855
|
-
const
|
|
39140
|
+
const discoveries = await store.loadDiscoveries(head.id);
|
|
39141
|
+
const reusable = discoveries.filter((d) => d.reusable).length;
|
|
39142
|
+
const recent = await store.list(8);
|
|
38856
39143
|
const lines = recent.map((c, i) => {
|
|
38857
|
-
const
|
|
38858
|
-
return ` ${i === 0 ? "\u2192" : " "} ${c.id} ${ago2(c.createdAt)} ${c.label} ver=${
|
|
39144
|
+
const ver2 = c.verification.ran ? c.verification.ok ? "ok" : "fail" : "n/a";
|
|
39145
|
+
return ` ${i === 0 ? "\u2192" : " "} ${c.id} ${ago2(c.createdAt)} ${c.label} ver=${ver2}` + (c.layer ? ` [${c.layer}]` : "") + (c.stablePromptHash ? ` hash=${c.stablePromptHash.slice(0, 8)}` : "");
|
|
38859
39146
|
});
|
|
39147
|
+
const ver = head.verification;
|
|
38860
39148
|
appendSystem(
|
|
38861
39149
|
ctx.setMessages,
|
|
38862
|
-
`[state] HEAD ${head.id} \xB7 ${head.
|
|
38863
|
-
|
|
38864
|
-
` : "") +
|
|
39150
|
+
`[state] HEAD ${head.id} \xB7 mode ${head.mode}` + (head.layer ? ` \xB7 layer ${head.layer}` : "") + `
|
|
39151
|
+
discoveries: ${head.discoveryCount} total, ${reusable} reusable
|
|
39152
|
+
verification: ran=${ver.ran} ok=${ver.ok}` + (ver.reportPath ? ` report=${ver.reportPath}` : "") + (head.parentId ? `
|
|
39153
|
+
parent: ${head.parentId}` : "") + (head.workspaceCheckpointId ? `
|
|
39154
|
+
workspaceCheckpoint: ${head.workspaceCheckpointId}` : "") + (head.stablePromptHash ? `
|
|
39155
|
+
stablePromptHash: ${head.stablePromptHash}` : "") + `
|
|
39156
|
+
created: ${ago2(head.createdAt)}
|
|
39157
|
+
` + lines.join("\n")
|
|
38865
39158
|
);
|
|
38866
39159
|
}
|
|
38867
39160
|
async function handleStateCommit(ctx, label) {
|
|
@@ -38916,19 +39209,16 @@ async function handleStateRestore(ctx, id, opts) {
|
|
|
38916
39209
|
appendSystem(ctx.setMessages, res.message);
|
|
38917
39210
|
}
|
|
38918
39211
|
|
|
38919
|
-
// src/cli/state/promptCacheStats.ts
|
|
38920
|
-
function formatCacheStatsLine(stats) {
|
|
38921
|
-
const pct = stats.promptTokens > 0 ? Math.round(stats.hitRate * 100) : 0;
|
|
38922
|
-
return `cache hit ${pct}% \xB7 premium ${stats.premiumTokens} \xB7 cached ${stats.cachedTokens} \xB7 stable busts ${stats.stableBustCount} \xB7 turns ${stats.turns}`;
|
|
38923
|
-
}
|
|
38924
|
-
|
|
38925
39212
|
// src/cli/slashHandlers/cache.ts
|
|
38926
39213
|
function handleCacheStats(ctx) {
|
|
39214
|
+
const ttl = resolvePromptCacheTtl();
|
|
38927
39215
|
const s = ctx.sessionStats;
|
|
38928
39216
|
if (!s || !(s.promptTokens || s.cachedTokens || s.totalTokens)) {
|
|
38929
39217
|
appendSystem(
|
|
38930
39218
|
ctx.setMessages,
|
|
38931
|
-
|
|
39219
|
+
`[cache] no session usage yet. Complete a model turn to see hit rate.
|
|
39220
|
+
Tip: stable prompt (identity+tools) should stay byte-stable; workspace/plan is volatile.
|
|
39221
|
+
ZELARI_PROMPT_CACHE_TTL=${ttl} (OpenAI-compat: automatic prefix cache; TTL is a preference for future Anthropic markers).`
|
|
38932
39222
|
);
|
|
38933
39223
|
return;
|
|
38934
39224
|
}
|
|
@@ -38938,15 +39228,18 @@ function handleCacheStats(ctx) {
|
|
|
38938
39228
|
premiumTokens: s.premiumTokens ?? Math.max(0, (s.promptTokens ?? 0) - (s.cachedTokens ?? 0)),
|
|
38939
39229
|
hitRate: s.cacheHitRate ?? 0,
|
|
38940
39230
|
estimatedCostUsd: s.totalCostUsd ?? 0,
|
|
39231
|
+
lastStableHash: s.lastStableHash,
|
|
38941
39232
|
stableBustCount: s.stableBustCount ?? 0,
|
|
38942
39233
|
turns: 0
|
|
38943
39234
|
};
|
|
38944
39235
|
const pct = stats.promptTokens > 0 ? Math.round(stats.hitRate * 100) : 0;
|
|
39236
|
+
const hashShort = stats.lastStableHash ? stats.lastStableHash.slice(0, 8) : "\u2014";
|
|
38945
39237
|
appendSystem(
|
|
38946
39238
|
ctx.setMessages,
|
|
38947
39239
|
`[cache] ${formatCacheStatsLine(stats)}
|
|
38948
|
-
hit rate: ${pct}% \xB7
|
|
38949
|
-
(
|
|
39240
|
+
hit rate: ${pct}% \xB7 premium ${stats.premiumTokens} \xB7 cached ${stats.cachedTokens}
|
|
39241
|
+
cost ~$${stats.estimatedCostUsd.toFixed(4)} \xB7 stable hash ${hashShort} \xB7 busts ${stats.stableBustCount}
|
|
39242
|
+
TTL pref: ${ttl} (OpenAI-compat path uses automatic prefix caching; keep stable prefix free of plan/memory churn.)`
|
|
38950
39243
|
);
|
|
38951
39244
|
}
|
|
38952
39245
|
|
|
@@ -39168,18 +39461,65 @@ import { spawn as spawn10 } from "node:child_process";
|
|
|
39168
39461
|
async function installPlugin(spec, cwd, executor = spawn10) {
|
|
39169
39462
|
const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
|
|
39170
39463
|
const args = ["install", scopeFlag, spec.npmPackage];
|
|
39171
|
-
|
|
39172
|
-
if (
|
|
39173
|
-
|
|
39174
|
-
|
|
39175
|
-
|
|
39176
|
-
|
|
39177
|
-
|
|
39178
|
-
|
|
39464
|
+
let result = await runNpm2(executor, args, cwd, "shim");
|
|
39465
|
+
if (!result.ok) {
|
|
39466
|
+
const npmCli = resolveBundledNpmCli();
|
|
39467
|
+
if (npmCli && looksLikeBrokenShim(result.exitCode, result.output)) {
|
|
39468
|
+
const fallback = await runNpm2(executor, args, cwd, "bundled", npmCli);
|
|
39469
|
+
result = {
|
|
39470
|
+
...fallback,
|
|
39471
|
+
output: `[plugins] npm shim failed (${result.error ?? "exit " + result.exitCode}); retried via bundled npm (${npmCli}).
|
|
39179
39472
|
${fallback.output}`
|
|
39473
|
+
};
|
|
39474
|
+
}
|
|
39475
|
+
}
|
|
39476
|
+
if (!result.ok) return result;
|
|
39477
|
+
if (spec.id === "playwright") {
|
|
39478
|
+
const browsers = await runPlaywrightInstallChromium(cwd, executor);
|
|
39479
|
+
result = {
|
|
39480
|
+
...result,
|
|
39481
|
+
ok: browsers.ok ? true : result.ok,
|
|
39482
|
+
// Prefer package success even if browser download fails — surface both.
|
|
39483
|
+
output: (result.output ? result.output + "\n" : "") + (browsers.output ? `[playwright browsers]
|
|
39484
|
+
${browsers.output}` : "") + (browsers.ok ? "" : `
|
|
39485
|
+
[playwright] Chromium install failed: ${browsers.error ?? "unknown"}. Run: npx playwright install chromium`),
|
|
39486
|
+
error: browsers.ok ? result.error : result.error ?? `playwright package installed but Chromium download failed: ${browsers.error ?? "unknown"}`
|
|
39180
39487
|
};
|
|
39488
|
+
result = { ...result, ok: true };
|
|
39181
39489
|
}
|
|
39182
|
-
return
|
|
39490
|
+
return result;
|
|
39491
|
+
}
|
|
39492
|
+
function runPlaywrightInstallChromium(cwd, executor) {
|
|
39493
|
+
return new Promise((resolve) => {
|
|
39494
|
+
let stdout = "";
|
|
39495
|
+
let stderr = "";
|
|
39496
|
+
const stdio = ["ignore", "pipe", "pipe"];
|
|
39497
|
+
const args = ["playwright", "install", "chromium"];
|
|
39498
|
+
const child = process.platform === "win32" ? executor(buildCmdLine("npx", args), { stdio, shell: true, cwd }) : executor("npx", args, { stdio, cwd });
|
|
39499
|
+
child.stdout?.on("data", (chunk) => {
|
|
39500
|
+
stdout += chunk.toString();
|
|
39501
|
+
});
|
|
39502
|
+
child.stderr?.on("data", (chunk) => {
|
|
39503
|
+
stderr += chunk.toString();
|
|
39504
|
+
});
|
|
39505
|
+
child.on("error", (err) => {
|
|
39506
|
+
resolve({
|
|
39507
|
+
ok: false,
|
|
39508
|
+
output: stdout + stderr,
|
|
39509
|
+
error: err.message,
|
|
39510
|
+
exitCode: null
|
|
39511
|
+
});
|
|
39512
|
+
});
|
|
39513
|
+
child.on("close", (code) => {
|
|
39514
|
+
const ok = code === 0;
|
|
39515
|
+
resolve({
|
|
39516
|
+
ok,
|
|
39517
|
+
output: stdout + stderr,
|
|
39518
|
+
exitCode: code,
|
|
39519
|
+
error: ok ? void 0 : `npx playwright install chromium exited ${code}`
|
|
39520
|
+
});
|
|
39521
|
+
});
|
|
39522
|
+
});
|
|
39183
39523
|
}
|
|
39184
39524
|
function runNpm2(executor, args, cwd, mode, npmCliPath) {
|
|
39185
39525
|
return new Promise((resolve) => {
|
|
@@ -40702,7 +41042,8 @@ function App() {
|
|
|
40702
41042
|
premiumTokens: 0,
|
|
40703
41043
|
cacheHitRate: 0,
|
|
40704
41044
|
promptTokens: 0,
|
|
40705
|
-
stableBustCount: 0
|
|
41045
|
+
stableBustCount: 0,
|
|
41046
|
+
lastStableHash: void 0
|
|
40706
41047
|
});
|
|
40707
41048
|
const [clearEpoch, setClearEpoch] = useState8(0);
|
|
40708
41049
|
const [mode, setMode] = useState8("agent");
|
|
@@ -41816,11 +42157,16 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
41816
42157
|
].join("\n")
|
|
41817
42158
|
};
|
|
41818
42159
|
const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
|
|
42160
|
+
const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
|
|
42161
|
+
const cwd = process.cwd();
|
|
42162
|
+
const durableState = await loadDurableContext2(cwd);
|
|
41819
42163
|
const composed = composeProjectContext2({
|
|
41820
42164
|
mode: "agent",
|
|
41821
|
-
cwd
|
|
42165
|
+
cwd,
|
|
41822
42166
|
userMessage: opts.task,
|
|
41823
|
-
includeLessons: false
|
|
42167
|
+
includeLessons: false,
|
|
42168
|
+
durableState: durableState || void 0,
|
|
42169
|
+
includeDurableState: false
|
|
41824
42170
|
});
|
|
41825
42171
|
let sshBlock = "";
|
|
41826
42172
|
try {
|
|
@@ -41829,6 +42175,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
41829
42175
|
} catch {
|
|
41830
42176
|
}
|
|
41831
42177
|
const rolePrompt = [headlessRole.systemPrompt, sshBlock].filter(Boolean).join("\n\n");
|
|
42178
|
+
const agentWorkspace = [composed.workspaceContext, composed.ragContext].filter(Boolean).join("\n\n");
|
|
41832
42179
|
systemPrompt = buildSystemPrompt(
|
|
41833
42180
|
{ ...headlessRole, systemPrompt: rolePrompt },
|
|
41834
42181
|
{
|
|
@@ -41836,7 +42183,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
41836
42183
|
toolNames,
|
|
41837
42184
|
mode: "agent",
|
|
41838
42185
|
projectInstructions: composed.projectInstructions || void 0,
|
|
41839
|
-
workspaceContext:
|
|
42186
|
+
workspaceContext: agentWorkspace || void 0,
|
|
41840
42187
|
// Plan lives in workspaceContext as draft ops — never as RAG.
|
|
41841
42188
|
ragContext: void 0,
|
|
41842
42189
|
aiConfig: {
|
|
@@ -42104,11 +42451,16 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
42104
42451
|
let currentAssistantText = "";
|
|
42105
42452
|
try {
|
|
42106
42453
|
const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
|
|
42454
|
+
const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
|
|
42455
|
+
const cwd = process.cwd();
|
|
42456
|
+
const durableState = await loadDurableContext2(cwd);
|
|
42107
42457
|
const composed = composeProjectContext2({
|
|
42108
42458
|
mode: "council",
|
|
42109
|
-
cwd
|
|
42459
|
+
cwd,
|
|
42110
42460
|
userMessage: opts.task,
|
|
42111
|
-
includeLessons: true
|
|
42461
|
+
includeLessons: true,
|
|
42462
|
+
durableState: durableState || void 0,
|
|
42463
|
+
includeDurableState: false
|
|
42112
42464
|
});
|
|
42113
42465
|
for await (const event of dispatchCouncil2(effectiveTask, {
|
|
42114
42466
|
apiKey: "REDACTED",
|
|
@@ -42249,13 +42601,17 @@ ${ragContext}` : slicePrompt;
|
|
|
42249
42601
|
let membersCompleted = 0;
|
|
42250
42602
|
const scrub = createStreamScrubber();
|
|
42251
42603
|
const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
|
|
42604
|
+
const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
|
|
42252
42605
|
const memOnly = ragContext?.trim() ? ragContext : void 0;
|
|
42606
|
+
const durableState = await loadDurableContext2(projectRoot);
|
|
42253
42607
|
const composed = composeProjectContext2({
|
|
42254
42608
|
mode: "zelari",
|
|
42255
42609
|
cwd: projectRoot,
|
|
42256
42610
|
userMessage: slicePrompt,
|
|
42257
42611
|
memoryHits: memOnly,
|
|
42258
|
-
|
|
42612
|
+
durableState: durableState || void 0,
|
|
42613
|
+
includeLessons: true,
|
|
42614
|
+
includeDurableState: false
|
|
42259
42615
|
});
|
|
42260
42616
|
for await (const event of dispatchCouncil2(fullPrompt, {
|
|
42261
42617
|
apiKey: "REDACTED",
|
|
@@ -42625,6 +42981,98 @@ async function runDiscoverModels(providerArg) {
|
|
|
42625
42981
|
}
|
|
42626
42982
|
}
|
|
42627
42983
|
|
|
42984
|
+
// src/cli/plugins/cliFlags.ts
|
|
42985
|
+
init_registry2();
|
|
42986
|
+
function getArg(argv, flag) {
|
|
42987
|
+
const i = argv.indexOf(flag);
|
|
42988
|
+
if (i < 0) return void 0;
|
|
42989
|
+
return argv[i + 1];
|
|
42990
|
+
}
|
|
42991
|
+
function resolveCwd(argv) {
|
|
42992
|
+
return getArg(argv, "--cwd") ?? process.cwd();
|
|
42993
|
+
}
|
|
42994
|
+
function wantsPluginsStatus(argv) {
|
|
42995
|
+
return argv.includes("--plugins-status");
|
|
42996
|
+
}
|
|
42997
|
+
function wantsPluginsInstall(argv) {
|
|
42998
|
+
return argv.includes("--plugins-install");
|
|
42999
|
+
}
|
|
43000
|
+
async function runPluginsStatus(argv) {
|
|
43001
|
+
const cwd = resolveCwd(argv);
|
|
43002
|
+
const plugins = [];
|
|
43003
|
+
for (const spec of PLUGINS) {
|
|
43004
|
+
let present = false;
|
|
43005
|
+
try {
|
|
43006
|
+
present = await spec.detect(cwd);
|
|
43007
|
+
} catch {
|
|
43008
|
+
present = false;
|
|
43009
|
+
}
|
|
43010
|
+
plugins.push({
|
|
43011
|
+
id: spec.id,
|
|
43012
|
+
label: spec.label,
|
|
43013
|
+
present,
|
|
43014
|
+
description: spec.description,
|
|
43015
|
+
postInstallHint: spec.postInstallHint ?? null,
|
|
43016
|
+
npmPackage: spec.npmPackage,
|
|
43017
|
+
installScope: spec.installScope
|
|
43018
|
+
});
|
|
43019
|
+
}
|
|
43020
|
+
process.stdout.write(JSON.stringify({ cwd, plugins }, null, 2) + "\n");
|
|
43021
|
+
return 0;
|
|
43022
|
+
}
|
|
43023
|
+
async function runPluginsInstall(argv) {
|
|
43024
|
+
const cwd = resolveCwd(argv);
|
|
43025
|
+
const id = getArg(argv, "--plugins-install");
|
|
43026
|
+
if (!id) {
|
|
43027
|
+
process.stderr.write(
|
|
43028
|
+
"[plugins] usage: zelari-code --plugins-install <id> [--cwd <path>]\n"
|
|
43029
|
+
);
|
|
43030
|
+
process.stdout.write(
|
|
43031
|
+
JSON.stringify({
|
|
43032
|
+
ok: false,
|
|
43033
|
+
id: "",
|
|
43034
|
+
message: "missing plugin id"
|
|
43035
|
+
}) + "\n"
|
|
43036
|
+
);
|
|
43037
|
+
return 1;
|
|
43038
|
+
}
|
|
43039
|
+
const spec = findPlugin(id);
|
|
43040
|
+
if (!spec) {
|
|
43041
|
+
const msg = `unknown plugin id: ${id}. Available: ${PLUGINS.map((p3) => p3.id).join(", ")}`;
|
|
43042
|
+
process.stderr.write(`[plugins] ${msg}
|
|
43043
|
+
`);
|
|
43044
|
+
process.stdout.write(
|
|
43045
|
+
JSON.stringify({ ok: false, id, message: msg }) + "\n"
|
|
43046
|
+
);
|
|
43047
|
+
return 1;
|
|
43048
|
+
}
|
|
43049
|
+
process.stderr.write(
|
|
43050
|
+
`[plugins] installing ${spec.label} into ${cwd}\u2026
|
|
43051
|
+
`
|
|
43052
|
+
);
|
|
43053
|
+
const result = await installPlugin(spec, cwd);
|
|
43054
|
+
const payload = {
|
|
43055
|
+
ok: result.ok,
|
|
43056
|
+
id: spec.id,
|
|
43057
|
+
message: result.ok ? `Installed ${spec.label}` : result.error ?? `Install failed for ${spec.label}`,
|
|
43058
|
+
output: result.output?.slice(-4e3),
|
|
43059
|
+
postInstallHint: spec.postInstallHint ?? null
|
|
43060
|
+
};
|
|
43061
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
43062
|
+
if (!result.ok) {
|
|
43063
|
+
process.stderr.write(`[plugins] \u2717 ${payload.message}
|
|
43064
|
+
`);
|
|
43065
|
+
return 1;
|
|
43066
|
+
}
|
|
43067
|
+
process.stderr.write(`[plugins] \u2713 ${payload.message}
|
|
43068
|
+
`);
|
|
43069
|
+
if (spec.postInstallHint) {
|
|
43070
|
+
process.stderr.write(`[plugins] \u2192 ${spec.postInstallHint}
|
|
43071
|
+
`);
|
|
43072
|
+
}
|
|
43073
|
+
return 0;
|
|
43074
|
+
}
|
|
43075
|
+
|
|
42628
43076
|
// src/cli/skillsMd.ts
|
|
42629
43077
|
init_skills2();
|
|
42630
43078
|
import { existsSync as existsSync36, readdirSync as readdirSync5, readFileSync as readFileSync30 } from "node:fs";
|
|
@@ -42809,6 +43257,14 @@ function pickRootComponent() {
|
|
|
42809
43257
|
console.log(`zelari-code v${VERSION}`);
|
|
42810
43258
|
process.exit(0);
|
|
42811
43259
|
}
|
|
43260
|
+
if (wantsPluginsStatus(argv)) {
|
|
43261
|
+
void runPluginsStatus(argv).then((code) => process.exit(code));
|
|
43262
|
+
return { kind: "done" };
|
|
43263
|
+
}
|
|
43264
|
+
if (wantsPluginsInstall(argv)) {
|
|
43265
|
+
void runPluginsInstall(argv).then((code) => process.exit(code));
|
|
43266
|
+
return { kind: "done" };
|
|
43267
|
+
}
|
|
42812
43268
|
if (argv.includes("--doctor") || argv.includes("doctor")) {
|
|
42813
43269
|
const { runDoctor: runDoctor2 } = (init_doctor(), __toCommonJS(doctor_exports));
|
|
42814
43270
|
void runDoctor2().then((healthy) => process.exit(healthy ? 0 : 1));
|
|
@@ -42838,7 +43294,7 @@ function pickRootComponent() {
|
|
|
42838
43294
|
}
|
|
42839
43295
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
42840
43296
|
console.log(
|
|
42841
|
-
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode agent|council|zelari Dispatch mode (default: agent)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --print-config Print provider/model config as JSON (no secrets)\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
43297
|
+
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode agent|council|zelari Dispatch mode (default: agent)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --print-config Print provider/model config as JSON (no secrets)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
42842
43298
|
);
|
|
42843
43299
|
process.exit(0);
|
|
42844
43300
|
}
|