zelari-code 2.19.0 → 2.21.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/budget/retrievalPolicy.js +82 -0
- package/dist/cli/budget/retrievalPolicy.js.map +1 -0
- package/dist/cli/hooks/permissionPicker.js +9 -1
- package/dist/cli/hooks/permissionPicker.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +10 -2
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/main.bundled.js +1330 -859
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +22 -5
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/httpTransport.js +184 -0
- package/dist/cli/mcp/httpTransport.js.map +1 -0
- package/dist/cli/mcp/mcpClient.js +153 -54
- package/dist/cli/mcp/mcpClient.js.map +1 -1
- package/dist/cli/mcp/mcpConfigIo.js +26 -5
- package/dist/cli/mcp/mcpConfigIo.js.map +1 -1
- package/dist/cli/mcp/mcpManager.js +51 -3
- package/dist/cli/mcp/mcpManager.js.map +1 -1
- package/dist/cli/mcp/mcpPresets.js +42 -1
- package/dist/cli/mcp/mcpPresets.js.map +1 -1
- package/dist/cli/provider/capabilities.js +3 -0
- package/dist/cli/provider/capabilities.js.map +1 -1
- package/dist/cli/provider/openai-compatible.js +27 -1
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/runHeadless.js +7 -2
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/safety/resourceClaims.js +33 -2
- package/dist/cli/safety/resourceClaims.js.map +1 -1
- package/dist/cli/safety/toolPermissions.js +11 -4
- package/dist/cli/safety/toolPermissions.js.map +1 -1
- package/dist/cli/skillConfigIo.js +1 -0
- package/dist/cli/skillConfigIo.js.map +1 -1
- package/dist/cli/toolRegistry.js +16 -2
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/skillTool.js +20 -6
- package/dist/cli/tools/skillTool.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -29977,8 +29977,9 @@ var init_service = __esm({
|
|
|
29977
29977
|
const text = (raw.query ?? raw.text ?? "").slice(0, 64e3);
|
|
29978
29978
|
const limit = Math.max(1, Math.min(raw.limit ?? 8, 100));
|
|
29979
29979
|
this.emit({ type: "memory_recall_start" });
|
|
29980
|
+
const { weights, ...recallQuery } = raw;
|
|
29980
29981
|
const candidates = await this.backend.search({
|
|
29981
|
-
...
|
|
29982
|
+
...recallQuery,
|
|
29982
29983
|
text,
|
|
29983
29984
|
projectId: this.projectId,
|
|
29984
29985
|
statuses: raw.statuses ?? (raw.includeHistorical ? void 0 : ["active"]),
|
|
@@ -30025,7 +30026,7 @@ var init_service = __esm({
|
|
|
30025
30026
|
}
|
|
30026
30027
|
}
|
|
30027
30028
|
}
|
|
30028
|
-
const ranked = diverse(rankMemoryCandidates([...byId.values()], text), limit);
|
|
30029
|
+
const ranked = diverse(rankMemoryCandidates([...byId.values()], text, weights ? { weights } : {}), limit);
|
|
30029
30030
|
this.emit({
|
|
30030
30031
|
type: "memory_recall_end",
|
|
30031
30032
|
durationMs: Date.now() - started,
|
|
@@ -40721,25 +40722,883 @@ var init_skillsMd = __esm({
|
|
|
40721
40722
|
}
|
|
40722
40723
|
});
|
|
40723
40724
|
|
|
40725
|
+
// src/cli/budget/historySummary.ts
|
|
40726
|
+
function extractiveHistorySummary(dropped, opts) {
|
|
40727
|
+
const maxChars = opts?.maxChars ?? MAX_SUMMARY_CHARS;
|
|
40728
|
+
if (dropped.length === 0) return "No prior turns.";
|
|
40729
|
+
const userGoals = [];
|
|
40730
|
+
const assistantNotes = [];
|
|
40731
|
+
const userConstraints = [];
|
|
40732
|
+
const unresolved = [];
|
|
40733
|
+
const verification = [];
|
|
40734
|
+
const decisions = [];
|
|
40735
|
+
const tools = /* @__PURE__ */ new Map();
|
|
40736
|
+
const files = /* @__PURE__ */ new Set();
|
|
40737
|
+
let toolResults = 0;
|
|
40738
|
+
for (const m of dropped) {
|
|
40739
|
+
if (m.role === "user" && m.content.trim()) {
|
|
40740
|
+
const goal = oneLine(m.content, 220);
|
|
40741
|
+
userGoals.push(goal);
|
|
40742
|
+
if (/\b(must|never|required|only|do not|constraint|vincolo|deve|senza)\b/i.test(goal)) userConstraints.push(goal);
|
|
40743
|
+
} else if (m.role === "assistant") {
|
|
40744
|
+
if (m.content.trim()) {
|
|
40745
|
+
const note = oneLine(m.content, 220);
|
|
40746
|
+
assistantNotes.push(note);
|
|
40747
|
+
if (/\b(fail|failed|error|unresolved|remaining|todo|blocked|gap|errore|fallit|irrisolt|manca)\b/i.test(note)) unresolved.push(note);
|
|
40748
|
+
if (/\b(test|typecheck|build|verify|verification|passed|failed|green|red)\b/i.test(note)) verification.push(note);
|
|
40749
|
+
if (/\b(decid|decision|chosen|choose|scelt|adopt|implement)\b/i.test(note)) decisions.push(note);
|
|
40750
|
+
}
|
|
40751
|
+
if (m.toolCalls) {
|
|
40752
|
+
for (const tc of m.toolCalls) {
|
|
40753
|
+
tools.set(tc.name, (tools.get(tc.name) ?? 0) + 1);
|
|
40754
|
+
collectPaths3(tc.args, files);
|
|
40755
|
+
}
|
|
40756
|
+
}
|
|
40757
|
+
} else if (m.role === "tool") {
|
|
40758
|
+
toolResults += 1;
|
|
40759
|
+
collectPathsFromText(m.content, files);
|
|
40760
|
+
if (/\b(fail|failed|error|exception|blocked|errore|fallit)\b/i.test(m.content)) unresolved.push(oneLine(m.content, 220));
|
|
40761
|
+
if (/\b(test|typecheck|build|verify|passed|failed|success)\b/i.test(m.content)) verification.push(oneLine(m.content, 220));
|
|
40762
|
+
}
|
|
40763
|
+
}
|
|
40764
|
+
const parts = [
|
|
40765
|
+
"[history-summary] Earlier turns were compacted to stay within the context budget.",
|
|
40766
|
+
`Dropped ${dropped.length} message(s) (${userGoals.length} user, ${assistantNotes.length} assistant notes, ${toolResults} tool results).`
|
|
40767
|
+
];
|
|
40768
|
+
if (userGoals.length) {
|
|
40769
|
+
parts.push("## User goals / requests");
|
|
40770
|
+
for (const g of userGoals.slice(-6)) parts.push(`- ${g}`);
|
|
40771
|
+
}
|
|
40772
|
+
if (userConstraints.length) {
|
|
40773
|
+
parts.push("## User constraints (preserve exactly)");
|
|
40774
|
+
for (const item of [...new Set(userConstraints)].slice(-8)) parts.push(`- ${item}`);
|
|
40775
|
+
}
|
|
40776
|
+
if (unresolved.length) {
|
|
40777
|
+
parts.push("## Unresolved failures / pending repair");
|
|
40778
|
+
for (const item of [...new Set(unresolved)].slice(-8)) parts.push(`- ${item}`);
|
|
40779
|
+
}
|
|
40780
|
+
if (verification.length) {
|
|
40781
|
+
parts.push("## Latest verification state");
|
|
40782
|
+
for (const item of [...new Set(verification)].slice(-6)) parts.push(`- ${item}`);
|
|
40783
|
+
}
|
|
40784
|
+
if (decisions.length) {
|
|
40785
|
+
parts.push("## Recent active decisions");
|
|
40786
|
+
for (const item of [...new Set(decisions)].slice(-6)) parts.push(`- ${item}`);
|
|
40787
|
+
}
|
|
40788
|
+
if (assistantNotes.length) {
|
|
40789
|
+
parts.push("## Assistant conclusions (truncated)");
|
|
40790
|
+
for (const a of assistantNotes.slice(-5)) parts.push(`- ${a}`);
|
|
40791
|
+
}
|
|
40792
|
+
if (tools.size) {
|
|
40793
|
+
const ranked = [...tools.entries()].sort((a, b) => b[1] - a[1]).slice(0, 12).map(([n, c]) => `${n}\xD7${c}`);
|
|
40794
|
+
parts.push(`## Tools used: ${ranked.join(", ")}`);
|
|
40795
|
+
}
|
|
40796
|
+
if (files.size) {
|
|
40797
|
+
const list = [...files].slice(0, 24);
|
|
40798
|
+
parts.push(`## Paths mentioned: ${list.join(", ")}`);
|
|
40799
|
+
}
|
|
40800
|
+
parts.push(
|
|
40801
|
+
"Continue from the recent messages below; do not re-ask goals already answered above unless the user changes them."
|
|
40802
|
+
);
|
|
40803
|
+
let out = parts.join("\n");
|
|
40804
|
+
if (out.length > maxChars) {
|
|
40805
|
+
out = `${out.slice(0, maxChars - 1)}\u2026`;
|
|
40806
|
+
}
|
|
40807
|
+
return out;
|
|
40808
|
+
}
|
|
40809
|
+
function oneLine(s, max) {
|
|
40810
|
+
const t = s.replace(/\s+/g, " ").trim();
|
|
40811
|
+
if (t.length <= max) return t;
|
|
40812
|
+
return `${t.slice(0, max - 1)}\u2026`;
|
|
40813
|
+
}
|
|
40814
|
+
function collectPaths3(args, out) {
|
|
40815
|
+
if (!args || typeof args !== "object") return;
|
|
40816
|
+
const obj = args;
|
|
40817
|
+
for (const key of ["path", "file", "filepath", "filePath", "target", "cwd"]) {
|
|
40818
|
+
const v = obj[key];
|
|
40819
|
+
if (typeof v === "string" && v.length > 1 && v.length < 260) {
|
|
40820
|
+
out.add(v.replace(/\\/g, "/"));
|
|
40821
|
+
}
|
|
40822
|
+
}
|
|
40823
|
+
if (typeof obj.file_path === "string") out.add(String(obj.file_path));
|
|
40824
|
+
}
|
|
40825
|
+
function collectPathsFromText(text, out) {
|
|
40826
|
+
const re = /(?:^|[\s"'`])((?:[\w.-]+\/)+[\w.-]+\.\w{1,8})/g;
|
|
40827
|
+
let m;
|
|
40828
|
+
let n = 0;
|
|
40829
|
+
while ((m = re.exec(text)) !== null && n < 8) {
|
|
40830
|
+
out.add(m[1]);
|
|
40831
|
+
n += 1;
|
|
40832
|
+
}
|
|
40833
|
+
}
|
|
40834
|
+
var MAX_SUMMARY_CHARS;
|
|
40835
|
+
var init_historySummary = __esm({
|
|
40836
|
+
"src/cli/budget/historySummary.ts"() {
|
|
40837
|
+
"use strict";
|
|
40838
|
+
MAX_SUMMARY_CHARS = 3500;
|
|
40839
|
+
}
|
|
40840
|
+
});
|
|
40841
|
+
|
|
40842
|
+
// src/cli/budget/llmCompact.ts
|
|
40843
|
+
function isLlmCompactEnabled() {
|
|
40844
|
+
const v = process.env.ZELARI_LLM_COMPACT?.trim().toLowerCase();
|
|
40845
|
+
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
40846
|
+
return true;
|
|
40847
|
+
}
|
|
40848
|
+
function compactModelOverride() {
|
|
40849
|
+
const v = process.env.ZELARI_COMPACT_MODEL?.trim();
|
|
40850
|
+
return v ? v : void 0;
|
|
40851
|
+
}
|
|
40852
|
+
async function llmSummarizeHistoryReplay(input) {
|
|
40853
|
+
const override = input.overrideModel ?? compactModelOverride();
|
|
40854
|
+
const model = override ?? input.model;
|
|
40855
|
+
const cacheReuseExpected = !override;
|
|
40856
|
+
if (!isLlmCompactEnabled()) return { summary: null, model, cacheReuseExpected };
|
|
40857
|
+
if (input.droppedMessages.length === 0) {
|
|
40858
|
+
return { summary: null, model, cacheReuseExpected };
|
|
40859
|
+
}
|
|
40860
|
+
const messages = [
|
|
40861
|
+
...input.systemMessages,
|
|
40862
|
+
...input.droppedMessages,
|
|
40863
|
+
{
|
|
40864
|
+
role: "user",
|
|
40865
|
+
content: COMPACTION_INSTRUCTION
|
|
40866
|
+
}
|
|
40867
|
+
];
|
|
40868
|
+
const controller = new AbortController();
|
|
40869
|
+
const timeout = setTimeout(() => controller.abort(), REPLAY_TIMEOUT_MS);
|
|
40870
|
+
const onOuterAbort = () => controller.abort();
|
|
40871
|
+
input.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
40872
|
+
try {
|
|
40873
|
+
let text = "";
|
|
40874
|
+
let emittedToolCall = false;
|
|
40875
|
+
for await (const delta of input.providerStream({
|
|
40876
|
+
provider: input.provider,
|
|
40877
|
+
model,
|
|
40878
|
+
messages,
|
|
40879
|
+
// Tools stay advertised: dropping them would change the prefix token
|
|
40880
|
+
// sequence and destroy cache reuse (explicit DSH decision). They are
|
|
40881
|
+
// sorted canonically (same discipline as the live routed request and
|
|
40882
|
+
// the snapshot fingerprints) so the replay prefix is byte-identical.
|
|
40883
|
+
tools: [...input.tools].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
|
|
40884
|
+
signal: controller.signal,
|
|
40885
|
+
generation: {
|
|
40886
|
+
purpose: "compaction",
|
|
40887
|
+
temperature: 0.1,
|
|
40888
|
+
maxTokens: 900
|
|
40889
|
+
}
|
|
40890
|
+
})) {
|
|
40891
|
+
if (delta.kind === "text") text += delta.delta;
|
|
40892
|
+
if (delta.kind === "tool_call") emittedToolCall = true;
|
|
40893
|
+
}
|
|
40894
|
+
if (emittedToolCall) return { summary: null, model, cacheReuseExpected };
|
|
40895
|
+
if (!text.trim()) return { summary: null, model, cacheReuseExpected };
|
|
40896
|
+
return { summary: text.trim(), model, cacheReuseExpected };
|
|
40897
|
+
} catch {
|
|
40898
|
+
return { summary: null, model, cacheReuseExpected };
|
|
40899
|
+
} finally {
|
|
40900
|
+
clearTimeout(timeout);
|
|
40901
|
+
input.signal?.removeEventListener("abort", onOuterAbort);
|
|
40902
|
+
}
|
|
40903
|
+
}
|
|
40904
|
+
var COMPACTION_INSTRUCTION, REPLAY_TIMEOUT_MS;
|
|
40905
|
+
var init_llmCompact = __esm({
|
|
40906
|
+
"src/cli/budget/llmCompact.ts"() {
|
|
40907
|
+
"use strict";
|
|
40908
|
+
COMPACTION_INSTRUCTION = `
|
|
40909
|
+
You are now acting as a compaction engine for this coding-agent session.
|
|
40910
|
+
|
|
40911
|
+
Condense the conversation ABOVE into a compact checkpoint sufficient to continue the task.
|
|
40912
|
+
|
|
40913
|
+
Preserve:
|
|
40914
|
+
- user's goal and evolving intent
|
|
40915
|
+
- decisions already made
|
|
40916
|
+
- exact file paths and identifiers
|
|
40917
|
+
- code changes already completed
|
|
40918
|
+
- commands/errors that still matter
|
|
40919
|
+
- constraints
|
|
40920
|
+
- unfinished work
|
|
40921
|
+
- the single most likely next action
|
|
40922
|
+
|
|
40923
|
+
Do not call tools.
|
|
40924
|
+
Do not mention this summarization request.
|
|
40925
|
+
Output only the checkpoint.
|
|
40926
|
+
Be concise.
|
|
40927
|
+
`.trim();
|
|
40928
|
+
REPLAY_TIMEOUT_MS = 6e4;
|
|
40929
|
+
}
|
|
40930
|
+
});
|
|
40931
|
+
|
|
40932
|
+
// src/cli/hooks/historyCompaction.ts
|
|
40933
|
+
function compactedRangeFromDropped(dropped) {
|
|
40934
|
+
if (dropped.length === 0) return void 0;
|
|
40935
|
+
const seqs = [];
|
|
40936
|
+
const sources = [];
|
|
40937
|
+
for (const m of dropped) {
|
|
40938
|
+
const hasCompactRange = typeof m.compactedFromSeq === "number" && Number.isInteger(m.compactedFromSeq) && m.compactedFromSeq > 0 && typeof m.compactedToSeq === "number" && Number.isInteger(m.compactedToSeq) && m.compactedToSeq >= m.compactedFromSeq;
|
|
40939
|
+
if (hasCompactRange) {
|
|
40940
|
+
seqs.push(m.compactedFromSeq, m.compactedToSeq);
|
|
40941
|
+
sources.push(...m.sourceEventSeqs ?? []);
|
|
40942
|
+
if (typeof m.seq === "number" && Number.isInteger(m.seq) && m.seq > 0) {
|
|
40943
|
+
seqs.push(m.seq);
|
|
40944
|
+
sources.push(m.seq);
|
|
40945
|
+
}
|
|
40946
|
+
continue;
|
|
40947
|
+
}
|
|
40948
|
+
if (typeof m.seq !== "number" || !Number.isInteger(m.seq) || m.seq < 1) return void 0;
|
|
40949
|
+
seqs.push(m.seq);
|
|
40950
|
+
sources.push(m.seq);
|
|
40951
|
+
}
|
|
40952
|
+
return {
|
|
40953
|
+
fromSeq: Math.min(...seqs),
|
|
40954
|
+
toSeq: Math.max(...seqs),
|
|
40955
|
+
sourceEventSeqs: [...new Set(sources)]
|
|
40956
|
+
};
|
|
40957
|
+
}
|
|
40958
|
+
function withDroppedRange(result, dropped, strategy) {
|
|
40959
|
+
const range = compactedRangeFromDropped(dropped);
|
|
40960
|
+
if (!range) return { ...result, strategy };
|
|
40961
|
+
return { ...result, ...range, strategy };
|
|
40962
|
+
}
|
|
40963
|
+
function resolveMaxMessages(opts) {
|
|
40964
|
+
const envTurns = envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
|
|
40965
|
+
let turns = opts?.maxMessages ? Math.ceil(opts.maxMessages / 4) : envTurns;
|
|
40966
|
+
if (opts?.durableStatePresent && !opts?.maxMessages && process.env.ZELARI_HISTORY_TURNS === void 0) {
|
|
40967
|
+
turns = Math.min(turns, 3);
|
|
40968
|
+
}
|
|
40969
|
+
if (turns <= 0) return 0;
|
|
40970
|
+
if (opts?.force && opts?.maxMessages) return Math.max(1, opts.maxMessages);
|
|
40971
|
+
return turns * 4;
|
|
40972
|
+
}
|
|
40973
|
+
function findValidCutIndex(messages, naiveCut) {
|
|
40974
|
+
let cut = naiveCut;
|
|
40975
|
+
while (cut < messages.length) {
|
|
40976
|
+
const kept = messages.slice(cut);
|
|
40977
|
+
const declared = /* @__PURE__ */ new Set();
|
|
40978
|
+
for (const m of kept) {
|
|
40979
|
+
if (m.role === "assistant" && m.toolCalls) {
|
|
40980
|
+
for (const tc of m.toolCalls) declared.add(tc.id);
|
|
40981
|
+
}
|
|
40982
|
+
}
|
|
40983
|
+
let moved = false;
|
|
40984
|
+
for (let k = 0; k < kept.length; k++) {
|
|
40985
|
+
const m = kept[k];
|
|
40986
|
+
if (m.role === "tool" && m.toolCallId && !declared.has(m.toolCallId)) {
|
|
40987
|
+
for (let j = cut - 1; j >= 0; j--) {
|
|
40988
|
+
const prev2 = messages[j];
|
|
40989
|
+
if (prev2.role === "assistant" && prev2.toolCalls && prev2.toolCalls.some((tc) => tc.id === m.toolCallId)) {
|
|
40990
|
+
cut = j;
|
|
40991
|
+
moved = true;
|
|
40992
|
+
break;
|
|
40993
|
+
}
|
|
40994
|
+
}
|
|
40995
|
+
break;
|
|
40996
|
+
}
|
|
40997
|
+
}
|
|
40998
|
+
if (!moved) break;
|
|
40999
|
+
}
|
|
41000
|
+
return cut;
|
|
41001
|
+
}
|
|
41002
|
+
function resolvePruneLimits(opts) {
|
|
41003
|
+
const maxChars = opts?.maxChars ?? envNumber(process.env.ZELARI_TOOL_RESULT_MAX_CHARS, { default: 8e3, min: 256 });
|
|
41004
|
+
const rawTail = opts?.tailChars ?? envNumber(process.env.ZELARI_TOOL_RESULT_TAIL_CHARS, { default: 1e3, min: 0 });
|
|
41005
|
+
const tailChars = Math.min(rawTail, maxChars);
|
|
41006
|
+
return { maxChars, tailChars };
|
|
41007
|
+
}
|
|
41008
|
+
function pruneToolResultsDetailed(messages, opts) {
|
|
41009
|
+
const { maxChars, tailChars } = resolvePruneLimits(opts);
|
|
41010
|
+
const headChars = maxChars - tailChars;
|
|
41011
|
+
const stats = { pruned: 0, charsOmitted: 0 };
|
|
41012
|
+
let changed = false;
|
|
41013
|
+
const out = messages.map((m) => {
|
|
41014
|
+
if (m.role !== "tool") return m;
|
|
41015
|
+
const body = m.content ?? "";
|
|
41016
|
+
if (body.length <= maxChars) return m;
|
|
41017
|
+
const head = headChars > 0 ? body.slice(0, headChars) : "";
|
|
41018
|
+
const tail2 = tailChars > 0 ? body.slice(-tailChars) : "";
|
|
41019
|
+
const omitted = body.length - head.length - tail2.length;
|
|
41020
|
+
changed = true;
|
|
41021
|
+
stats.pruned += 1;
|
|
41022
|
+
stats.charsOmitted += omitted;
|
|
41023
|
+
return {
|
|
41024
|
+
...m,
|
|
41025
|
+
content: [head, "\u2026[pruned " + omitted + " chars]\u2026", tail2].join(String.fromCharCode(10))
|
|
41026
|
+
};
|
|
41027
|
+
});
|
|
41028
|
+
return {
|
|
41029
|
+
messages: changed ? out : messages,
|
|
41030
|
+
stats
|
|
41031
|
+
};
|
|
41032
|
+
}
|
|
41033
|
+
function compactHistory(messages, opts) {
|
|
41034
|
+
return compactHistoryDetailed(messages, opts).messages;
|
|
41035
|
+
}
|
|
41036
|
+
function buildCheckpointMessage(summaryText, range) {
|
|
41037
|
+
return {
|
|
41038
|
+
role: "user",
|
|
41039
|
+
content: CHECKPOINT_WRAPPER_PREFIX + "\n\n<compacted-summary>\n" + summaryText + "\n</compacted-summary>",
|
|
41040
|
+
...range ? {
|
|
41041
|
+
compactedFromSeq: range.fromSeq,
|
|
41042
|
+
compactedToSeq: range.toSeq,
|
|
41043
|
+
sourceEventSeqs: [...range.sourceEventSeqs]
|
|
41044
|
+
} : {}
|
|
41045
|
+
};
|
|
41046
|
+
}
|
|
41047
|
+
function compactHistoryDetailed(messages, opts) {
|
|
41048
|
+
const maxMessages = resolveMaxMessages(opts);
|
|
41049
|
+
if (maxMessages === 0) {
|
|
41050
|
+
return withDroppedRange(
|
|
41051
|
+
{ messages: [], compacted: true, messagesRemoved: messages.length, summary: "" },
|
|
41052
|
+
messages,
|
|
41053
|
+
"extractive"
|
|
41054
|
+
);
|
|
41055
|
+
}
|
|
41056
|
+
if (messages.length <= maxMessages * 2 && !opts?.force) {
|
|
41057
|
+
return {
|
|
41058
|
+
messages,
|
|
41059
|
+
compacted: false,
|
|
41060
|
+
messagesRemoved: 0,
|
|
41061
|
+
summary: ""
|
|
41062
|
+
};
|
|
41063
|
+
}
|
|
41064
|
+
const naiveCut = Math.max(0, messages.length - maxMessages);
|
|
41065
|
+
const cut = findValidCutIndex(messages, naiveCut);
|
|
41066
|
+
if (cut === 0) {
|
|
41067
|
+
return {
|
|
41068
|
+
messages,
|
|
41069
|
+
compacted: false,
|
|
41070
|
+
messagesRemoved: 0,
|
|
41071
|
+
summary: ""
|
|
41072
|
+
};
|
|
41073
|
+
}
|
|
41074
|
+
const droppedMsgs = messages.slice(0, cut);
|
|
41075
|
+
const droppedRange = compactedRangeFromDropped(droppedMsgs);
|
|
41076
|
+
const pruned = pruneToolResultsDetailed(messages.slice(cut));
|
|
41077
|
+
const kept = pruned.messages;
|
|
41078
|
+
const summaryText = extractiveHistorySummary(droppedMsgs);
|
|
41079
|
+
const summary = buildCheckpointMessage(
|
|
41080
|
+
summaryText || `${COMPACT_MARKER} ${cut} earlier message(s) dropped.`,
|
|
41081
|
+
droppedRange
|
|
41082
|
+
);
|
|
41083
|
+
return withDroppedRange(
|
|
41084
|
+
{
|
|
41085
|
+
messages: [summary, ...kept],
|
|
41086
|
+
compacted: true,
|
|
41087
|
+
messagesRemoved: cut,
|
|
41088
|
+
summary: summary.content,
|
|
41089
|
+
prunedToolResults: pruned.stats.pruned
|
|
41090
|
+
},
|
|
41091
|
+
droppedMsgs,
|
|
41092
|
+
"extractive"
|
|
41093
|
+
);
|
|
41094
|
+
}
|
|
41095
|
+
async function compactHistoryAsync(messages, opts) {
|
|
41096
|
+
const base2 = compactHistoryDetailed(messages, opts);
|
|
41097
|
+
if (!base2.compacted || base2.messagesRemoved === 0) return base2;
|
|
41098
|
+
const cut = base2.messagesRemoved;
|
|
41099
|
+
const droppedMsgs = messages.slice(0, cut);
|
|
41100
|
+
const extractive = extractiveHistorySummary(droppedMsgs);
|
|
41101
|
+
let summaryText = extractive;
|
|
41102
|
+
let cacheReuseExpected;
|
|
41103
|
+
let replayExactPrefix;
|
|
41104
|
+
const canReplay = !!(opts?.providerStream && opts?.requestSnapshot);
|
|
41105
|
+
if (canReplay) {
|
|
41106
|
+
try {
|
|
41107
|
+
const replay = await llmSummarizeHistoryReplay({
|
|
41108
|
+
providerStream: opts.providerStream,
|
|
41109
|
+
provider: opts.requestSnapshot.provider,
|
|
41110
|
+
model: opts.requestSnapshot.model,
|
|
41111
|
+
systemMessages: opts.requestSnapshot.systemMessages,
|
|
41112
|
+
tools: opts.requestSnapshot.tools,
|
|
41113
|
+
droppedMessages: droppedMsgs,
|
|
41114
|
+
signal: opts?.signal
|
|
41115
|
+
});
|
|
41116
|
+
cacheReuseExpected = replay.cacheReuseExpected;
|
|
41117
|
+
if (replay.summary && replay.summary.trim().length > 40) {
|
|
41118
|
+
const sourceTokens = roughTokens(droppedMsgs);
|
|
41119
|
+
const summaryTok = Math.ceil(replay.summary.length / 4);
|
|
41120
|
+
if (summaryTok < sourceTokens) {
|
|
41121
|
+
summaryText = replay.summary.trim();
|
|
41122
|
+
}
|
|
41123
|
+
}
|
|
41124
|
+
} catch {
|
|
41125
|
+
}
|
|
41126
|
+
}
|
|
41127
|
+
const pruned = pruneToolResultsDetailed(messages.slice(cut));
|
|
41128
|
+
const kept = pruned.messages;
|
|
41129
|
+
const summary = buildCheckpointMessage(summaryText, compactedRangeFromDropped(droppedMsgs));
|
|
41130
|
+
const usedLlm = summaryText !== extractive && summaryText.trim().length > 40;
|
|
41131
|
+
return withDroppedRange(
|
|
41132
|
+
{
|
|
41133
|
+
messages: [summary, ...kept],
|
|
41134
|
+
compacted: true,
|
|
41135
|
+
messagesRemoved: cut,
|
|
41136
|
+
summary: summaryText,
|
|
41137
|
+
prunedToolResults: pruned.stats.pruned,
|
|
41138
|
+
cacheReuseExpected,
|
|
41139
|
+
replayExactPrefix
|
|
41140
|
+
},
|
|
41141
|
+
droppedMsgs,
|
|
41142
|
+
usedLlm ? "llm" : "extractive"
|
|
41143
|
+
);
|
|
41144
|
+
}
|
|
41145
|
+
function roughTokens(msgs) {
|
|
41146
|
+
let n = 0;
|
|
41147
|
+
for (const m of msgs) {
|
|
41148
|
+
n += Math.ceil((m.content ?? "").length / 4);
|
|
41149
|
+
if (m.toolCalls) {
|
|
41150
|
+
for (const tc of m.toolCalls) {
|
|
41151
|
+
n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
|
|
41152
|
+
}
|
|
41153
|
+
}
|
|
41154
|
+
}
|
|
41155
|
+
return Math.max(1, n);
|
|
41156
|
+
}
|
|
41157
|
+
var COMPACT_MARKER, CHECKPOINT_WRAPPER_PREFIX;
|
|
41158
|
+
var init_historyCompaction = __esm({
|
|
41159
|
+
"src/cli/hooks/historyCompaction.ts"() {
|
|
41160
|
+
"use strict";
|
|
41161
|
+
init_historySummary();
|
|
41162
|
+
init_llmCompact();
|
|
41163
|
+
init_envNumber();
|
|
41164
|
+
COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
41165
|
+
CHECKPOINT_WRAPPER_PREFIX = "This is an automatically generated checkpoint of earlier conversation. Treat it as established context and continue directly.";
|
|
41166
|
+
}
|
|
41167
|
+
});
|
|
41168
|
+
|
|
41169
|
+
// src/cli/provider/capabilities.ts
|
|
41170
|
+
function frozenProfile(input) {
|
|
41171
|
+
if (input.reasoning.levels) Object.freeze(input.reasoning.levels);
|
|
41172
|
+
Object.freeze(input.reasoning);
|
|
41173
|
+
Object.freeze(input.promptCache);
|
|
41174
|
+
Object.freeze(input.toolCalling);
|
|
41175
|
+
Object.freeze(input.buildRecovery);
|
|
41176
|
+
Object.freeze(input.sampling);
|
|
41177
|
+
Object.freeze(input.compaction);
|
|
41178
|
+
return Object.freeze(input);
|
|
41179
|
+
}
|
|
41180
|
+
function resolveHarnessProfile(model, providerId) {
|
|
41181
|
+
const provider = providerId?.trim().toLowerCase();
|
|
41182
|
+
if (provider === "deepseek") return "deepseek-v4";
|
|
41183
|
+
if (provider === "grok") return "grok";
|
|
41184
|
+
if (provider === "minimax") return "minimax";
|
|
41185
|
+
if (provider === "glm") return "glm";
|
|
41186
|
+
if (model && DEEPSEEK_RE.test(model)) return "deepseek-v4";
|
|
41187
|
+
if (model && GROK_RE.test(model)) return "grok";
|
|
41188
|
+
if (model && MINIMAX_RE.test(model)) return "minimax";
|
|
41189
|
+
if (model && GLM_RE.test(model)) return "glm";
|
|
41190
|
+
return "default";
|
|
41191
|
+
}
|
|
41192
|
+
function capabilitiesFor(model, providerId) {
|
|
41193
|
+
switch (resolveHarnessProfile(model, providerId)) {
|
|
41194
|
+
case "deepseek-v4":
|
|
41195
|
+
return DEEPSEEK_V4_CAPS;
|
|
41196
|
+
case "grok":
|
|
41197
|
+
return GROK_CAPS;
|
|
41198
|
+
case "minimax":
|
|
41199
|
+
return model && MINIMAX_M3_RE.test(model) ? MINIMAX_M3_CAPS : MINIMAX_M2_CAPS;
|
|
41200
|
+
case "glm":
|
|
41201
|
+
return GLM_CAPS;
|
|
41202
|
+
default:
|
|
41203
|
+
return DEFAULT_CAPS;
|
|
41204
|
+
}
|
|
41205
|
+
}
|
|
41206
|
+
var SHARED_COMPACTION, DEFAULT_CAPS, DEEPSEEK_V4_CAPS, GROK_CAPS, MINIMAX_M3_CAPS, MINIMAX_M2_CAPS, GLM_CAPS, DEEPSEEK_RE, GROK_RE, MINIMAX_RE, MINIMAX_M3_RE, GLM_RE;
|
|
41207
|
+
var init_capabilities = __esm({
|
|
41208
|
+
"src/cli/provider/capabilities.ts"() {
|
|
41209
|
+
"use strict";
|
|
41210
|
+
SHARED_COMPACTION = { warnAt: 0.7, compactAt: 0.85, hardAt: 0.95 };
|
|
41211
|
+
DEFAULT_CAPS = frozenProfile({
|
|
41212
|
+
contextWindow: 4e5,
|
|
41213
|
+
reasoning: { supported: true, levels: ["low", "medium", "high"], replayReasoning: true },
|
|
41214
|
+
promptCache: { supported: true, pricedCacheRead: false },
|
|
41215
|
+
toolCalling: { parallel: true },
|
|
41216
|
+
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
41217
|
+
sampling: { temperature: 0.7 },
|
|
41218
|
+
compaction: { ...SHARED_COMPACTION },
|
|
41219
|
+
profile: "default"
|
|
41220
|
+
});
|
|
41221
|
+
DEEPSEEK_V4_CAPS = frozenProfile({
|
|
41222
|
+
contextWindow: 1e6,
|
|
41223
|
+
reasoning: { supported: true, levels: ["high", "max"], replayReasoning: true },
|
|
41224
|
+
promptCache: { supported: true, pricedCacheRead: true },
|
|
41225
|
+
toolCalling: { parallel: true },
|
|
41226
|
+
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
41227
|
+
sampling: { temperature: 0.7 },
|
|
41228
|
+
compaction: { ...SHARED_COMPACTION },
|
|
41229
|
+
profile: "deepseek-v4"
|
|
41230
|
+
});
|
|
41231
|
+
GROK_CAPS = frozenProfile({
|
|
41232
|
+
contextWindow: 5e5,
|
|
41233
|
+
// grok-4.x truncation fix: explicit conversation ceiling (see interface
|
|
41234
|
+
// doc). Override at runtime via ZELARI_MAX_OUTPUT_TOKENS.
|
|
41235
|
+
maxOutputTokens: 32768,
|
|
41236
|
+
reasoning: {
|
|
41237
|
+
supported: true,
|
|
41238
|
+
levels: ["low", "medium", "high", "xhigh"],
|
|
41239
|
+
replayReasoning: false
|
|
41240
|
+
},
|
|
41241
|
+
promptCache: {
|
|
41242
|
+
supported: true,
|
|
41243
|
+
pricedCacheRead: true,
|
|
41244
|
+
conversationAffinityHeader: "x-grok-conv-id"
|
|
41245
|
+
},
|
|
41246
|
+
toolCalling: { parallel: true },
|
|
41247
|
+
buildRecovery: { forceToolChoice: true, maxForcedTurns: 1 },
|
|
41248
|
+
sampling: { temperature: 0.7 },
|
|
41249
|
+
compaction: { ...SHARED_COMPACTION },
|
|
41250
|
+
profile: "grok"
|
|
41251
|
+
});
|
|
41252
|
+
MINIMAX_M3_CAPS = frozenProfile({
|
|
41253
|
+
contextWindow: 1e6,
|
|
41254
|
+
reasoning: { supported: true, replayReasoning: true },
|
|
41255
|
+
promptCache: { supported: false, pricedCacheRead: false },
|
|
41256
|
+
toolCalling: { parallel: true },
|
|
41257
|
+
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
41258
|
+
sampling: { temperature: 0.7 },
|
|
41259
|
+
compaction: { ...SHARED_COMPACTION },
|
|
41260
|
+
profile: "minimax"
|
|
41261
|
+
});
|
|
41262
|
+
MINIMAX_M2_CAPS = frozenProfile({
|
|
41263
|
+
contextWindow: 204800,
|
|
41264
|
+
reasoning: { supported: true, replayReasoning: true },
|
|
41265
|
+
promptCache: { supported: false, pricedCacheRead: false },
|
|
41266
|
+
toolCalling: { parallel: true },
|
|
41267
|
+
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
41268
|
+
sampling: { temperature: 0.7 },
|
|
41269
|
+
compaction: { ...SHARED_COMPACTION },
|
|
41270
|
+
profile: "minimax"
|
|
41271
|
+
});
|
|
41272
|
+
GLM_CAPS = frozenProfile({
|
|
41273
|
+
contextWindow: 2e5,
|
|
41274
|
+
reasoning: { supported: true, replayReasoning: true },
|
|
41275
|
+
promptCache: { supported: true, pricedCacheRead: false },
|
|
41276
|
+
toolCalling: { parallel: true },
|
|
41277
|
+
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
41278
|
+
sampling: { temperature: 0.7 },
|
|
41279
|
+
compaction: { ...SHARED_COMPACTION },
|
|
41280
|
+
profile: "glm"
|
|
41281
|
+
});
|
|
41282
|
+
DEEPSEEK_RE = /^deepseek-(?:v4(?:\.|-|$)|chat$|reasoner$)/i;
|
|
41283
|
+
GROK_RE = /^grok(?:\.|-|$)/i;
|
|
41284
|
+
MINIMAX_RE = /^minimax(?:\.|-|$)/i;
|
|
41285
|
+
MINIMAX_M3_RE = /^minimax-m3(?:\.|-|$)/i;
|
|
41286
|
+
GLM_RE = /^glm(?:\.|-|$)/i;
|
|
41287
|
+
}
|
|
41288
|
+
});
|
|
41289
|
+
|
|
41290
|
+
// src/cli/budget/tokenBudget.ts
|
|
41291
|
+
function mergeCompactRange(into, r) {
|
|
41292
|
+
if (r.fromSeq !== void 0 && r.toSeq !== void 0) {
|
|
41293
|
+
into.fromSeq = into.fromSeq === void 0 ? r.fromSeq : Math.min(into.fromSeq, r.fromSeq);
|
|
41294
|
+
into.toSeq = into.toSeq === void 0 ? r.toSeq : Math.max(into.toSeq, r.toSeq);
|
|
41295
|
+
if (r.sourceEventSeqs) into.sourceSeqs.push(...r.sourceEventSeqs);
|
|
41296
|
+
}
|
|
41297
|
+
if (r.strategy === "llm") into.strategy = "llm";
|
|
41298
|
+
else if (r.strategy && !into.strategy) into.strategy = r.strategy;
|
|
41299
|
+
}
|
|
41300
|
+
function estimateTokens2(text) {
|
|
41301
|
+
if (!text) return 0;
|
|
41302
|
+
return Math.max(1, Math.ceil(text.length / 4));
|
|
41303
|
+
}
|
|
41304
|
+
function estimateHistoryTokens(messages) {
|
|
41305
|
+
let n = 0;
|
|
41306
|
+
for (const m of messages) {
|
|
41307
|
+
n += estimateTokens2(m.content);
|
|
41308
|
+
if (m.toolCalls) {
|
|
41309
|
+
for (const tc of m.toolCalls) {
|
|
41310
|
+
n += estimateTokens2(tc.name) + estimateTokens2(JSON.stringify(tc.args ?? {}));
|
|
41311
|
+
}
|
|
41312
|
+
}
|
|
41313
|
+
}
|
|
41314
|
+
return n;
|
|
41315
|
+
}
|
|
41316
|
+
function defaultContextLimitForModel(model, provider) {
|
|
41317
|
+
return capabilitiesFor(model, provider).contextWindow;
|
|
41318
|
+
}
|
|
41319
|
+
function resolveContextLimit(model, provider) {
|
|
41320
|
+
return envNumber(process.env.ZELARI_CONTEXT_LIMIT, {
|
|
41321
|
+
default: defaultContextLimitForModel(model, provider),
|
|
41322
|
+
min: 4e3,
|
|
41323
|
+
max: 2e6
|
|
41324
|
+
});
|
|
41325
|
+
}
|
|
41326
|
+
function phaseKnobs(phase2) {
|
|
41327
|
+
return {
|
|
41328
|
+
historyTurns: phase2 === "plan" ? envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 8, min: 0 }) : envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 }),
|
|
41329
|
+
maxToolLoopIterations: phase2 === "plan" ? envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 60, min: 1 }) : envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
|
|
41330
|
+
default: 120,
|
|
41331
|
+
min: 1
|
|
41332
|
+
})
|
|
41333
|
+
};
|
|
41334
|
+
}
|
|
41335
|
+
async function applyBudgetPolicyAsync(history2, phase2, opts) {
|
|
41336
|
+
const contextLimit = resolveContextLimit(opts?.model, opts?.provider);
|
|
41337
|
+
const compact3 = capabilitiesFor(opts?.model, opts?.provider).compaction;
|
|
41338
|
+
const sessionExtra = opts?.sessionTokens ?? 0;
|
|
41339
|
+
const warnings = [];
|
|
41340
|
+
let { historyTurns, maxToolLoopIterations } = phaseKnobs(phase2);
|
|
41341
|
+
const envelope = opts?.requestSnapshot ?? null;
|
|
41342
|
+
const surface = envelope?.snapshot ?? opts?.requestSurface ?? null;
|
|
41343
|
+
const replayBase = surface ? {
|
|
41344
|
+
provider: surface.provider,
|
|
41345
|
+
model: surface.model,
|
|
41346
|
+
systemMessages: surface.systemMessages,
|
|
41347
|
+
tools: surface.tools
|
|
41348
|
+
} : opts?.providerStream ? { provider: "local", model: opts?.model ?? "unknown", systemMessages: [], tools: [] } : null;
|
|
41349
|
+
const headerTokens = surface ? estimateSystemTokensLite(surface.systemMessages) + estimateToolSchemaTokensLite(surface.tools) : 0;
|
|
41350
|
+
const convTokensOf = (h) => estimateConversationTokensLite(h);
|
|
41351
|
+
let hist = history2;
|
|
41352
|
+
let estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
41353
|
+
let occupancy = Math.min(1, estimated / contextLimit);
|
|
41354
|
+
let compactSummary = "";
|
|
41355
|
+
let messagesRemoved = 0;
|
|
41356
|
+
let cacheReuseExpected;
|
|
41357
|
+
let prunedTotal = 0;
|
|
41358
|
+
const compactRange = { sourceSeqs: [] };
|
|
41359
|
+
if (occupancy >= compact3.warnAt && occupancy < compact3.compactAt) {
|
|
41360
|
+
warnings.push(
|
|
41361
|
+
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated}/${contextLimit} tok full-request est.) \u2014 consider /compact or shorter replies.`
|
|
41362
|
+
);
|
|
41363
|
+
}
|
|
41364
|
+
if (occupancy >= 0.8) {
|
|
41365
|
+
const pruned = pruneToolResultsDetailed(hist);
|
|
41366
|
+
if (pruned.stats.pruned > 0) {
|
|
41367
|
+
hist = pruned.messages;
|
|
41368
|
+
prunedTotal += pruned.stats.pruned;
|
|
41369
|
+
estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
41370
|
+
occupancy = Math.min(1, estimated / contextLimit);
|
|
41371
|
+
warnings.push(
|
|
41372
|
+
`[budget] pruned ${pruned.stats.pruned} oversized tool result(s) \u2192 ${Math.round(occupancy * 100)}% (${estimated} tok).`
|
|
41373
|
+
);
|
|
41374
|
+
}
|
|
41375
|
+
}
|
|
41376
|
+
const fold = (r, label, forcedTurns) => {
|
|
41377
|
+
hist = applySessionSurface(r.messages);
|
|
41378
|
+
if (r.compacted) {
|
|
41379
|
+
messagesRemoved += r.messagesRemoved;
|
|
41380
|
+
if (r.summary) compactSummary = r.summary;
|
|
41381
|
+
mergeCompactRange(compactRange, r);
|
|
41382
|
+
if (r.cacheReuseExpected !== void 0) {
|
|
41383
|
+
cacheReuseExpected = r.cacheReuseExpected;
|
|
41384
|
+
}
|
|
41385
|
+
}
|
|
41386
|
+
estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
41387
|
+
occupancy = Math.min(1, estimated / contextLimit);
|
|
41388
|
+
warnings.push(
|
|
41389
|
+
`[budget] ${label} \u2014 kept ~${forcedTurns} turns (${estimated} tok est.` + (r.messagesRemoved ? `, removed ${r.messagesRemoved} msgs` : "") + (r.cacheReuseExpected === false ? ", cache reuse NOT expected (model override)" : "") + ")."
|
|
41390
|
+
);
|
|
41391
|
+
};
|
|
41392
|
+
if (occupancy >= compact3.compactAt) {
|
|
41393
|
+
const forcedTurns = Math.max(1, Math.floor(historyTurns / 2));
|
|
41394
|
+
historyTurns = forcedTurns;
|
|
41395
|
+
maxToolLoopIterations = Math.min(
|
|
41396
|
+
maxToolLoopIterations,
|
|
41397
|
+
phase2 === "plan" ? 24 : 40
|
|
41398
|
+
);
|
|
41399
|
+
let r = await compactHistoryAsync(hist, {
|
|
41400
|
+
maxMessages: Math.max(2, forcedTurns * 4),
|
|
41401
|
+
force: true,
|
|
41402
|
+
signal: opts?.signal,
|
|
41403
|
+
...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
|
|
41404
|
+
});
|
|
41405
|
+
if (!r.compacted) {
|
|
41406
|
+
r = await compactHistoryAsync(hist, {
|
|
41407
|
+
maxMessages: 2,
|
|
41408
|
+
force: true,
|
|
41409
|
+
signal: opts?.signal,
|
|
41410
|
+
...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
|
|
41411
|
+
});
|
|
41412
|
+
}
|
|
41413
|
+
const label = r.cacheReuseExpected === false ? "llm-compact (model override)" : "auto-compact";
|
|
41414
|
+
fold(r, label, forcedTurns);
|
|
41415
|
+
}
|
|
41416
|
+
if (occupancy >= compact3.hardAt) {
|
|
41417
|
+
const hard = await compactHistoryAsync(hist, {
|
|
41418
|
+
maxMessages: 2,
|
|
41419
|
+
force: true,
|
|
41420
|
+
signal: opts?.signal
|
|
41421
|
+
});
|
|
41422
|
+
fold(hard, hard.summary.includes("\xB7 llm") ? "llm-compact" : "HARD trim", 2);
|
|
41423
|
+
historyTurns = 2;
|
|
41424
|
+
maxToolLoopIterations = Math.min(maxToolLoopIterations, 16);
|
|
41425
|
+
warnings.push(
|
|
41426
|
+
"[budget] HARD context pressure (\u226595%) \u2014 prefer /clear or a new session if quality drops."
|
|
41427
|
+
);
|
|
41428
|
+
}
|
|
41429
|
+
const cacheMetricsLine = envelope ? [
|
|
41430
|
+
"compaction meter:",
|
|
41431
|
+
`provider/model: ${envelope.snapshot.provider}/${envelope.snapshot.model}`,
|
|
41432
|
+
`headerFingerprint: ${envelope.snapshot.headerFingerprint.slice(0, 12)}`,
|
|
41433
|
+
`occupancy: ${Math.round(occupancy * 100)}% (${estimated}/${contextLimit})`,
|
|
41434
|
+
...envelope.usage?.cachedPromptTokens !== void 0 ? [`cachedPromptTokens: ${envelope.usage.cachedPromptTokens}`] : [],
|
|
41435
|
+
...cacheReuseExpected !== void 0 ? [`cacheReuseExpected: ${cacheReuseExpected}`] : []
|
|
41436
|
+
].join(" | ") : void 0;
|
|
41437
|
+
return {
|
|
41438
|
+
history: hist,
|
|
41439
|
+
warnings,
|
|
41440
|
+
maxToolLoopIterations,
|
|
41441
|
+
historyTurns,
|
|
41442
|
+
estimatedHistoryTokens: surface ? convTokensOf(hist) : estimated,
|
|
41443
|
+
contextLimit,
|
|
41444
|
+
occupancy,
|
|
41445
|
+
compactSummary: compactSummary || void 0,
|
|
41446
|
+
messagesRemoved: messagesRemoved || void 0,
|
|
41447
|
+
...compactRange.fromSeq !== void 0 && compactRange.toSeq !== void 0 ? {
|
|
41448
|
+
compactedFromSeq: compactRange.fromSeq,
|
|
41449
|
+
compactedToSeq: compactRange.toSeq,
|
|
41450
|
+
compactSourceSeqs: compactRange.sourceSeqs,
|
|
41451
|
+
compactStrategy: compactRange.strategy
|
|
41452
|
+
} : {},
|
|
41453
|
+
...surface ? { contextPressureTokens: estimated } : {},
|
|
41454
|
+
...cacheReuseExpected !== void 0 ? { cacheReuseExpected } : {},
|
|
41455
|
+
...cacheMetricsLine ? { cacheMetricsLine } : {}
|
|
41456
|
+
};
|
|
41457
|
+
}
|
|
41458
|
+
function estimateSystemTokensLite(systemMessages) {
|
|
41459
|
+
let n = 0;
|
|
41460
|
+
for (const m of systemMessages) n += 4 + Math.ceil((m.content ?? "").length / 4);
|
|
41461
|
+
return n;
|
|
41462
|
+
}
|
|
41463
|
+
function estimateToolSchemaTokensLite(tools) {
|
|
41464
|
+
let n = 0;
|
|
41465
|
+
for (const t of tools) {
|
|
41466
|
+
n += Math.ceil((t.name ?? "").length / 4);
|
|
41467
|
+
n += Math.ceil((t.description ?? "").length / 4);
|
|
41468
|
+
n += Math.ceil(JSON.stringify(t.parameters ?? {}).length / 4);
|
|
41469
|
+
}
|
|
41470
|
+
return n + tools.length * 4;
|
|
41471
|
+
}
|
|
41472
|
+
function estimateConversationTokensLite(messages) {
|
|
41473
|
+
let n = 0;
|
|
41474
|
+
for (const m of messages) {
|
|
41475
|
+
n += 4 + Math.ceil((m.content ?? "").length / 4);
|
|
41476
|
+
if (m.toolCalls) {
|
|
41477
|
+
for (const tc of m.toolCalls) {
|
|
41478
|
+
n += Math.ceil(tc.name.length / 4) + Math.ceil(tc.id.length / 4);
|
|
41479
|
+
n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
|
|
41480
|
+
}
|
|
41481
|
+
}
|
|
41482
|
+
if (m.reasoningContent) n += Math.ceil(m.reasoningContent.length / 4);
|
|
41483
|
+
if (m.toolCallId) n += Math.ceil(m.toolCallId.length / 4);
|
|
41484
|
+
}
|
|
41485
|
+
return n;
|
|
41486
|
+
}
|
|
41487
|
+
var RESERVED_OUTPUT_TOKENS;
|
|
41488
|
+
var init_tokenBudget = __esm({
|
|
41489
|
+
"src/cli/budget/tokenBudget.ts"() {
|
|
41490
|
+
"use strict";
|
|
41491
|
+
init_envNumber();
|
|
41492
|
+
init_historyCompaction();
|
|
41493
|
+
init_observationStore();
|
|
41494
|
+
init_capabilities();
|
|
41495
|
+
RESERVED_OUTPUT_TOKENS = 8192;
|
|
41496
|
+
}
|
|
41497
|
+
});
|
|
41498
|
+
|
|
41499
|
+
// src/cli/budget/retrievalPolicy.ts
|
|
41500
|
+
var retrievalPolicy_exports = {};
|
|
41501
|
+
__export(retrievalPolicy_exports, {
|
|
41502
|
+
BASELINE_RETRIEVAL_POLICY: () => BASELINE_RETRIEVAL_POLICY,
|
|
41503
|
+
RETRIEVAL_PRESSURE_THRESHOLDS: () => RETRIEVAL_PRESSURE_THRESHOLDS,
|
|
41504
|
+
estimateHistoryOccupancy: () => estimateHistoryOccupancy,
|
|
41505
|
+
filterSkillCatalogByBand: () => filterSkillCatalogByBand,
|
|
41506
|
+
resolveRetrievalPolicy: () => resolveRetrievalPolicy,
|
|
41507
|
+
retrievalBand: () => retrievalBand
|
|
41508
|
+
});
|
|
41509
|
+
function retrievalBand(occupancy) {
|
|
41510
|
+
const occ = Number.isFinite(occupancy) ? Math.max(0, Math.min(1, occupancy)) : 0;
|
|
41511
|
+
if (occ >= RETRIEVAL_PRESSURE_THRESHOLDS.high) return "high";
|
|
41512
|
+
if (occ >= RETRIEVAL_PRESSURE_THRESHOLDS.medium) return "medium";
|
|
41513
|
+
return "low";
|
|
41514
|
+
}
|
|
41515
|
+
function resolveRetrievalPolicy(occupancy) {
|
|
41516
|
+
const band = retrievalBand(occupancy);
|
|
41517
|
+
const packing = PRESSURE_PACKING[band];
|
|
41518
|
+
const weights = PRESSURE_WEIGHTS[band];
|
|
41519
|
+
return {
|
|
41520
|
+
band,
|
|
41521
|
+
...packing,
|
|
41522
|
+
...weights ? { weights } : {}
|
|
41523
|
+
};
|
|
41524
|
+
}
|
|
41525
|
+
function estimateHistoryOccupancy(history2, opts) {
|
|
41526
|
+
const limit = resolveContextLimit(opts?.model, opts?.provider);
|
|
41527
|
+
return Math.min(1, estimateHistoryTokens(history2) / limit);
|
|
41528
|
+
}
|
|
41529
|
+
function filterSkillCatalogByBand(skills, band) {
|
|
41530
|
+
if (band !== "high") return [...skills];
|
|
41531
|
+
return skills.filter((s) => s.estimatedCost !== "high");
|
|
41532
|
+
}
|
|
41533
|
+
var RETRIEVAL_PRESSURE_THRESHOLDS, BASELINE_RETRIEVAL_POLICY, PRESSURE_WEIGHTS, PRESSURE_PACKING;
|
|
41534
|
+
var init_retrievalPolicy = __esm({
|
|
41535
|
+
"src/cli/budget/retrievalPolicy.ts"() {
|
|
41536
|
+
"use strict";
|
|
41537
|
+
init_tokenBudget();
|
|
41538
|
+
RETRIEVAL_PRESSURE_THRESHOLDS = { medium: 0.5, high: 0.8 };
|
|
41539
|
+
BASELINE_RETRIEVAL_POLICY = {
|
|
41540
|
+
band: "low",
|
|
41541
|
+
maxChars: 2e3,
|
|
41542
|
+
maxMemories: 8
|
|
41543
|
+
};
|
|
41544
|
+
PRESSURE_WEIGHTS = {
|
|
41545
|
+
low: void 0,
|
|
41546
|
+
medium: {
|
|
41547
|
+
semanticRelevance: 0.35,
|
|
41548
|
+
lexicalRelevance: 0.2,
|
|
41549
|
+
importance: 0.15,
|
|
41550
|
+
confidence: 0.1,
|
|
41551
|
+
recency: 0.05,
|
|
41552
|
+
graphProximity: 0.1,
|
|
41553
|
+
verificationBonus: 0.05
|
|
41554
|
+
},
|
|
41555
|
+
high: {
|
|
41556
|
+
semanticRelevance: 0.4,
|
|
41557
|
+
lexicalRelevance: 0.25,
|
|
41558
|
+
importance: 0.15,
|
|
41559
|
+
confidence: 0.1,
|
|
41560
|
+
recency: 0,
|
|
41561
|
+
graphProximity: 0.05,
|
|
41562
|
+
verificationBonus: 0.05
|
|
41563
|
+
}
|
|
41564
|
+
};
|
|
41565
|
+
PRESSURE_PACKING = {
|
|
41566
|
+
low: { maxChars: 2e3, maxMemories: 8 },
|
|
41567
|
+
medium: { maxChars: 1200, maxMemories: 6 },
|
|
41568
|
+
high: { maxChars: 600, maxMemories: 4 }
|
|
41569
|
+
};
|
|
41570
|
+
}
|
|
41571
|
+
});
|
|
41572
|
+
|
|
40724
41573
|
// src/cli/tools/skillTool.ts
|
|
40725
|
-
function formatAvailableSkillsCatalog(cwd = process.cwd()) {
|
|
41574
|
+
function formatAvailableSkillsCatalog(cwd = process.cwd(), opts) {
|
|
40726
41575
|
try {
|
|
40727
41576
|
loadSkillMdSkills(cwd);
|
|
40728
41577
|
} catch {
|
|
40729
41578
|
}
|
|
40730
|
-
const
|
|
40731
|
-
|
|
41579
|
+
const band = opts?.pressureBand;
|
|
41580
|
+
const all = listCodingSkills();
|
|
41581
|
+
if (all.length === 0) {
|
|
40732
41582
|
return "No skills registered.";
|
|
40733
41583
|
}
|
|
40734
|
-
const
|
|
41584
|
+
const gated = band ? filterSkillCatalogByBand(all, band) : all;
|
|
41585
|
+
const hidden = all.length - gated.length;
|
|
41586
|
+
const lines = gated.slice(0, 80).map((s) => {
|
|
40735
41587
|
const desc = (s.description || s.id).replace(/\s+/g, " ").trim().slice(0, 160);
|
|
40736
41588
|
return `- ${s.id}: ${desc}`;
|
|
40737
41589
|
});
|
|
41590
|
+
if (hidden > 0) {
|
|
41591
|
+
lines.push(
|
|
41592
|
+
`(plus ${hidden} high-cost skill(s) hidden under context pressure \u2014 still loadable by name if truly needed)`
|
|
41593
|
+
);
|
|
41594
|
+
}
|
|
40738
41595
|
return lines.join("\n");
|
|
40739
41596
|
}
|
|
40740
41597
|
function createSkillTool(opts) {
|
|
40741
41598
|
const cwd = opts?.cwd ?? process.cwd();
|
|
40742
|
-
const catalog = formatAvailableSkillsCatalog(cwd
|
|
41599
|
+
const catalog = formatAvailableSkillsCatalog(cwd, {
|
|
41600
|
+
pressureBand: opts?.pressureBand
|
|
41601
|
+
});
|
|
40743
41602
|
return {
|
|
40744
41603
|
name: "skill",
|
|
40745
41604
|
description: "Load the full instructions for a named coding skill into this turn. Call when a skill matches the current task. Available skills:\n" + catalog,
|
|
@@ -40781,6 +41640,7 @@ var init_skillTool = __esm({
|
|
|
40781
41640
|
init_skills2();
|
|
40782
41641
|
init_toolTypes();
|
|
40783
41642
|
init_skillsMd();
|
|
41643
|
+
init_retrievalPolicy();
|
|
40784
41644
|
SkillArgsSchema = external_exports.object({
|
|
40785
41645
|
name: external_exports.string().min(1).describe("Skill id/name to load (from the available skills list).")
|
|
40786
41646
|
});
|
|
@@ -43862,124 +44722,6 @@ var init_semantic2 = __esm({
|
|
|
43862
44722
|
}
|
|
43863
44723
|
});
|
|
43864
44724
|
|
|
43865
|
-
// src/cli/provider/capabilities.ts
|
|
43866
|
-
function frozenProfile(input) {
|
|
43867
|
-
if (input.reasoning.levels) Object.freeze(input.reasoning.levels);
|
|
43868
|
-
Object.freeze(input.reasoning);
|
|
43869
|
-
Object.freeze(input.promptCache);
|
|
43870
|
-
Object.freeze(input.toolCalling);
|
|
43871
|
-
Object.freeze(input.buildRecovery);
|
|
43872
|
-
Object.freeze(input.sampling);
|
|
43873
|
-
Object.freeze(input.compaction);
|
|
43874
|
-
return Object.freeze(input);
|
|
43875
|
-
}
|
|
43876
|
-
function resolveHarnessProfile(model, providerId) {
|
|
43877
|
-
const provider = providerId?.trim().toLowerCase();
|
|
43878
|
-
if (provider === "deepseek") return "deepseek-v4";
|
|
43879
|
-
if (provider === "grok") return "grok";
|
|
43880
|
-
if (provider === "minimax") return "minimax";
|
|
43881
|
-
if (provider === "glm") return "glm";
|
|
43882
|
-
if (model && DEEPSEEK_RE.test(model)) return "deepseek-v4";
|
|
43883
|
-
if (model && GROK_RE.test(model)) return "grok";
|
|
43884
|
-
if (model && MINIMAX_RE.test(model)) return "minimax";
|
|
43885
|
-
if (model && GLM_RE.test(model)) return "glm";
|
|
43886
|
-
return "default";
|
|
43887
|
-
}
|
|
43888
|
-
function capabilitiesFor(model, providerId) {
|
|
43889
|
-
switch (resolveHarnessProfile(model, providerId)) {
|
|
43890
|
-
case "deepseek-v4":
|
|
43891
|
-
return DEEPSEEK_V4_CAPS;
|
|
43892
|
-
case "grok":
|
|
43893
|
-
return GROK_CAPS;
|
|
43894
|
-
case "minimax":
|
|
43895
|
-
return model && MINIMAX_M3_RE.test(model) ? MINIMAX_M3_CAPS : MINIMAX_M2_CAPS;
|
|
43896
|
-
case "glm":
|
|
43897
|
-
return GLM_CAPS;
|
|
43898
|
-
default:
|
|
43899
|
-
return DEFAULT_CAPS;
|
|
43900
|
-
}
|
|
43901
|
-
}
|
|
43902
|
-
var SHARED_COMPACTION, DEFAULT_CAPS, DEEPSEEK_V4_CAPS, GROK_CAPS, MINIMAX_M3_CAPS, MINIMAX_M2_CAPS, GLM_CAPS, DEEPSEEK_RE, GROK_RE, MINIMAX_RE, MINIMAX_M3_RE, GLM_RE;
|
|
43903
|
-
var init_capabilities = __esm({
|
|
43904
|
-
"src/cli/provider/capabilities.ts"() {
|
|
43905
|
-
"use strict";
|
|
43906
|
-
SHARED_COMPACTION = { warnAt: 0.7, compactAt: 0.85, hardAt: 0.95 };
|
|
43907
|
-
DEFAULT_CAPS = frozenProfile({
|
|
43908
|
-
contextWindow: 4e5,
|
|
43909
|
-
reasoning: { supported: true, levels: ["low", "medium", "high"], replayReasoning: true },
|
|
43910
|
-
promptCache: { supported: true, pricedCacheRead: false },
|
|
43911
|
-
toolCalling: { parallel: true },
|
|
43912
|
-
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
43913
|
-
sampling: { temperature: 0.7 },
|
|
43914
|
-
compaction: { ...SHARED_COMPACTION },
|
|
43915
|
-
profile: "default"
|
|
43916
|
-
});
|
|
43917
|
-
DEEPSEEK_V4_CAPS = frozenProfile({
|
|
43918
|
-
contextWindow: 1e6,
|
|
43919
|
-
reasoning: { supported: true, levels: ["high", "max"], replayReasoning: true },
|
|
43920
|
-
promptCache: { supported: true, pricedCacheRead: true },
|
|
43921
|
-
toolCalling: { parallel: true },
|
|
43922
|
-
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
43923
|
-
sampling: { temperature: 0.7 },
|
|
43924
|
-
compaction: { ...SHARED_COMPACTION },
|
|
43925
|
-
profile: "deepseek-v4"
|
|
43926
|
-
});
|
|
43927
|
-
GROK_CAPS = frozenProfile({
|
|
43928
|
-
contextWindow: 5e5,
|
|
43929
|
-
reasoning: {
|
|
43930
|
-
supported: true,
|
|
43931
|
-
levels: ["low", "medium", "high", "xhigh"],
|
|
43932
|
-
replayReasoning: false
|
|
43933
|
-
},
|
|
43934
|
-
promptCache: {
|
|
43935
|
-
supported: true,
|
|
43936
|
-
pricedCacheRead: true,
|
|
43937
|
-
conversationAffinityHeader: "x-grok-conv-id"
|
|
43938
|
-
},
|
|
43939
|
-
toolCalling: { parallel: true },
|
|
43940
|
-
buildRecovery: { forceToolChoice: true, maxForcedTurns: 1 },
|
|
43941
|
-
sampling: { temperature: 0.7 },
|
|
43942
|
-
compaction: { ...SHARED_COMPACTION },
|
|
43943
|
-
profile: "grok"
|
|
43944
|
-
});
|
|
43945
|
-
MINIMAX_M3_CAPS = frozenProfile({
|
|
43946
|
-
contextWindow: 1e6,
|
|
43947
|
-
reasoning: { supported: true, replayReasoning: true },
|
|
43948
|
-
promptCache: { supported: false, pricedCacheRead: false },
|
|
43949
|
-
toolCalling: { parallel: true },
|
|
43950
|
-
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
43951
|
-
sampling: { temperature: 0.7 },
|
|
43952
|
-
compaction: { ...SHARED_COMPACTION },
|
|
43953
|
-
profile: "minimax"
|
|
43954
|
-
});
|
|
43955
|
-
MINIMAX_M2_CAPS = frozenProfile({
|
|
43956
|
-
contextWindow: 204800,
|
|
43957
|
-
reasoning: { supported: true, replayReasoning: true },
|
|
43958
|
-
promptCache: { supported: false, pricedCacheRead: false },
|
|
43959
|
-
toolCalling: { parallel: true },
|
|
43960
|
-
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
43961
|
-
sampling: { temperature: 0.7 },
|
|
43962
|
-
compaction: { ...SHARED_COMPACTION },
|
|
43963
|
-
profile: "minimax"
|
|
43964
|
-
});
|
|
43965
|
-
GLM_CAPS = frozenProfile({
|
|
43966
|
-
contextWindow: 2e5,
|
|
43967
|
-
reasoning: { supported: true, replayReasoning: true },
|
|
43968
|
-
promptCache: { supported: true, pricedCacheRead: false },
|
|
43969
|
-
toolCalling: { parallel: true },
|
|
43970
|
-
buildRecovery: { forceToolChoice: false, maxForcedTurns: 0 },
|
|
43971
|
-
sampling: { temperature: 0.7 },
|
|
43972
|
-
compaction: { ...SHARED_COMPACTION },
|
|
43973
|
-
profile: "glm"
|
|
43974
|
-
});
|
|
43975
|
-
DEEPSEEK_RE = /^deepseek-(?:v4(?:\.|-|$)|chat$|reasoner$)/i;
|
|
43976
|
-
GROK_RE = /^grok(?:\.|-|$)/i;
|
|
43977
|
-
MINIMAX_RE = /^minimax(?:\.|-|$)/i;
|
|
43978
|
-
MINIMAX_M3_RE = /^minimax-m3(?:\.|-|$)/i;
|
|
43979
|
-
GLM_RE = /^glm(?:\.|-|$)/i;
|
|
43980
|
-
}
|
|
43981
|
-
});
|
|
43982
|
-
|
|
43983
44725
|
// src/cli/provider/openai-compatible.ts
|
|
43984
44726
|
var openai_compatible_exports = {};
|
|
43985
44727
|
__export(openai_compatible_exports, {
|
|
@@ -44163,6 +44905,12 @@ ${notes}
|
|
|
44163
44905
|
}
|
|
44164
44906
|
return { role: m.role, content: m.content };
|
|
44165
44907
|
}
|
|
44908
|
+
function positiveEnvInt(name) {
|
|
44909
|
+
const raw = process.env[name];
|
|
44910
|
+
if (!raw) return void 0;
|
|
44911
|
+
const n = Number.parseInt(raw, 10);
|
|
44912
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
44913
|
+
}
|
|
44166
44914
|
function openaiCompatibleProvider(config2) {
|
|
44167
44915
|
return async function* (params) {
|
|
44168
44916
|
const capabilities = capabilitiesFor(params.model, config2.providerId);
|
|
@@ -44195,6 +44943,8 @@ function openaiCompatibleProvider(config2) {
|
|
|
44195
44943
|
};
|
|
44196
44944
|
if (typeof generation?.maxTokens === "number" && generation.maxTokens > 0) {
|
|
44197
44945
|
body.max_tokens = generation.maxTokens;
|
|
44946
|
+
} else if (typeof capabilities.maxOutputTokens === "number" && capabilities.maxOutputTokens > 0) {
|
|
44947
|
+
body.max_tokens = positiveEnvInt("ZELARI_MAX_OUTPUT_TOKENS") ?? capabilities.maxOutputTokens;
|
|
44198
44948
|
}
|
|
44199
44949
|
const thinkingSpec = config2.thinking ?? "auto";
|
|
44200
44950
|
if (config2.providerId === "deepseek" && thinkingSpec === "auto") {
|
|
@@ -46386,8 +47136,8 @@ function defaultPermissionPolicy(overrides) {
|
|
|
46386
47136
|
return {
|
|
46387
47137
|
read: parseAction(process.env.ZELARI_PERMISSION_READ, "allow"),
|
|
46388
47138
|
write: parseAction(process.env.ZELARI_PERMISSION_WRITE, "allow"),
|
|
46389
|
-
execute: parseAction(process.env.ZELARI_PERMISSION_EXECUTE, "
|
|
46390
|
-
network: parseAction(process.env.ZELARI_PERMISSION_NETWORK, "
|
|
47139
|
+
execute: parseAction(process.env.ZELARI_PERMISSION_EXECUTE, "ask"),
|
|
47140
|
+
network: parseAction(process.env.ZELARI_PERMISSION_NETWORK, "ask"),
|
|
46391
47141
|
ui: "allow",
|
|
46392
47142
|
auto: isAutoPermissions(),
|
|
46393
47143
|
...overrides
|
|
@@ -47353,7 +48103,7 @@ function matchResourceClaimLayered(layers, precedence, claim, root) {
|
|
|
47353
48103
|
function resolveClaimsVerdict(layers, precedence, toolName, args, root) {
|
|
47354
48104
|
const claims = resourceClaimsFor(toolName, args);
|
|
47355
48105
|
const matchedRules = [];
|
|
47356
|
-
if (!layers || claims.length === 0) return { matchedRules };
|
|
48106
|
+
if (!layers || claims.length === 0) return { claims, matchedRules };
|
|
47357
48107
|
const effects = [];
|
|
47358
48108
|
for (const claim of claims) {
|
|
47359
48109
|
const hit = matchResourceClaimLayered(layers, precedence, claim, root);
|
|
@@ -47362,7 +48112,26 @@ function resolveClaimsVerdict(layers, precedence, toolName, args, root) {
|
|
|
47362
48112
|
matchedRules.push(hit);
|
|
47363
48113
|
}
|
|
47364
48114
|
}
|
|
47365
|
-
return effects.length > 0 ? { effect: intersectEffects(...effects), matchedRules } : { matchedRules };
|
|
48115
|
+
return effects.length > 0 ? { effect: intersectEffects(...effects), claims, matchedRules } : { claims, matchedRules };
|
|
48116
|
+
}
|
|
48117
|
+
function describeResourceClaim(claim) {
|
|
48118
|
+
const cap3 = (s) => s.length > 160 ? `${s.slice(0, 157)}\u2026` : s;
|
|
48119
|
+
switch (claim.kind) {
|
|
48120
|
+
case "path":
|
|
48121
|
+
return cap3(`path ${claim.operation}: ${claim.path}`);
|
|
48122
|
+
case "process":
|
|
48123
|
+
return cap3(`process: ${[claim.executable, ...claim.argv ?? []].join(" ")}`);
|
|
48124
|
+
case "network":
|
|
48125
|
+
return cap3(`network: ${claim.host}${claim.port !== void 0 ? `:${claim.port}` : ""}`);
|
|
48126
|
+
case "mcp":
|
|
48127
|
+
return cap3(`mcp: ${claim.server}/${claim.tool}`);
|
|
48128
|
+
case "ssh":
|
|
48129
|
+
return cap3(`ssh: ${claim.target}${claim.command ? ` (${claim.command})` : ""}`);
|
|
48130
|
+
case "ui":
|
|
48131
|
+
return cap3(`ui: ${claim.action}`);
|
|
48132
|
+
case "agent":
|
|
48133
|
+
return cap3(`agent: ${claim.role}`);
|
|
48134
|
+
}
|
|
47366
48135
|
}
|
|
47367
48136
|
var MAX_RAW_SHELL_STRIP_DEPTH, RAW_SHELL_WRAPPERS, RAW_SHELL_INTERPRETERS;
|
|
47368
48137
|
var init_resourceClaims = __esm({
|
|
@@ -47819,7 +48588,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
47819
48588
|
registry4.register(withPerm(askUserTool));
|
|
47820
48589
|
}
|
|
47821
48590
|
const enableSkill = options.enableSkill !== false && options.readOnly !== true && !gauntletParent && profile !== "explore" && profile !== "verify";
|
|
47822
|
-
const skillTool = enableSkill ? withPerm(createSkillTool({ cwd: root })) : null;
|
|
48591
|
+
const skillTool = enableSkill ? withPerm(createSkillTool({ cwd: root, pressureBand: options.pressureBand })) : null;
|
|
47823
48592
|
if (skillTool) {
|
|
47824
48593
|
registry4.register(skillTool);
|
|
47825
48594
|
}
|
|
@@ -48113,11 +48882,17 @@ function wrapWithPermissions(original, policy, onAsk, agentLayers, precedence =
|
|
|
48113
48882
|
);
|
|
48114
48883
|
}
|
|
48115
48884
|
try {
|
|
48885
|
+
const expanded = claims?.claims ?? resourceClaimsFor(original.name, input ?? {});
|
|
48116
48886
|
const ok = await onAsk({
|
|
48117
48887
|
toolName: original.name,
|
|
48118
48888
|
reason: decision.reason,
|
|
48119
48889
|
categories: decision.categories,
|
|
48120
|
-
args: input
|
|
48890
|
+
args: input,
|
|
48891
|
+
policyNote: rulePrefix || void 0,
|
|
48892
|
+
claims: expanded.slice(0, 6).map((c) => ({
|
|
48893
|
+
kind: c.kind,
|
|
48894
|
+
summary: describeResourceClaim(c)
|
|
48895
|
+
}))
|
|
48121
48896
|
});
|
|
48122
48897
|
if (!ok) {
|
|
48123
48898
|
return typedErr(
|
|
@@ -50500,450 +51275,6 @@ var init_messageHelpers = __esm({
|
|
|
50500
51275
|
}
|
|
50501
51276
|
});
|
|
50502
51277
|
|
|
50503
|
-
// src/cli/budget/historySummary.ts
|
|
50504
|
-
function extractiveHistorySummary(dropped, opts) {
|
|
50505
|
-
const maxChars = opts?.maxChars ?? MAX_SUMMARY_CHARS;
|
|
50506
|
-
if (dropped.length === 0) return "No prior turns.";
|
|
50507
|
-
const userGoals = [];
|
|
50508
|
-
const assistantNotes = [];
|
|
50509
|
-
const userConstraints = [];
|
|
50510
|
-
const unresolved = [];
|
|
50511
|
-
const verification = [];
|
|
50512
|
-
const decisions = [];
|
|
50513
|
-
const tools = /* @__PURE__ */ new Map();
|
|
50514
|
-
const files = /* @__PURE__ */ new Set();
|
|
50515
|
-
let toolResults = 0;
|
|
50516
|
-
for (const m of dropped) {
|
|
50517
|
-
if (m.role === "user" && m.content.trim()) {
|
|
50518
|
-
const goal = oneLine(m.content, 220);
|
|
50519
|
-
userGoals.push(goal);
|
|
50520
|
-
if (/\b(must|never|required|only|do not|constraint|vincolo|deve|senza)\b/i.test(goal)) userConstraints.push(goal);
|
|
50521
|
-
} else if (m.role === "assistant") {
|
|
50522
|
-
if (m.content.trim()) {
|
|
50523
|
-
const note = oneLine(m.content, 220);
|
|
50524
|
-
assistantNotes.push(note);
|
|
50525
|
-
if (/\b(fail|failed|error|unresolved|remaining|todo|blocked|gap|errore|fallit|irrisolt|manca)\b/i.test(note)) unresolved.push(note);
|
|
50526
|
-
if (/\b(test|typecheck|build|verify|verification|passed|failed|green|red)\b/i.test(note)) verification.push(note);
|
|
50527
|
-
if (/\b(decid|decision|chosen|choose|scelt|adopt|implement)\b/i.test(note)) decisions.push(note);
|
|
50528
|
-
}
|
|
50529
|
-
if (m.toolCalls) {
|
|
50530
|
-
for (const tc of m.toolCalls) {
|
|
50531
|
-
tools.set(tc.name, (tools.get(tc.name) ?? 0) + 1);
|
|
50532
|
-
collectPaths3(tc.args, files);
|
|
50533
|
-
}
|
|
50534
|
-
}
|
|
50535
|
-
} else if (m.role === "tool") {
|
|
50536
|
-
toolResults += 1;
|
|
50537
|
-
collectPathsFromText(m.content, files);
|
|
50538
|
-
if (/\b(fail|failed|error|exception|blocked|errore|fallit)\b/i.test(m.content)) unresolved.push(oneLine(m.content, 220));
|
|
50539
|
-
if (/\b(test|typecheck|build|verify|passed|failed|success)\b/i.test(m.content)) verification.push(oneLine(m.content, 220));
|
|
50540
|
-
}
|
|
50541
|
-
}
|
|
50542
|
-
const parts = [
|
|
50543
|
-
"[history-summary] Earlier turns were compacted to stay within the context budget.",
|
|
50544
|
-
`Dropped ${dropped.length} message(s) (${userGoals.length} user, ${assistantNotes.length} assistant notes, ${toolResults} tool results).`
|
|
50545
|
-
];
|
|
50546
|
-
if (userGoals.length) {
|
|
50547
|
-
parts.push("## User goals / requests");
|
|
50548
|
-
for (const g of userGoals.slice(-6)) parts.push(`- ${g}`);
|
|
50549
|
-
}
|
|
50550
|
-
if (userConstraints.length) {
|
|
50551
|
-
parts.push("## User constraints (preserve exactly)");
|
|
50552
|
-
for (const item of [...new Set(userConstraints)].slice(-8)) parts.push(`- ${item}`);
|
|
50553
|
-
}
|
|
50554
|
-
if (unresolved.length) {
|
|
50555
|
-
parts.push("## Unresolved failures / pending repair");
|
|
50556
|
-
for (const item of [...new Set(unresolved)].slice(-8)) parts.push(`- ${item}`);
|
|
50557
|
-
}
|
|
50558
|
-
if (verification.length) {
|
|
50559
|
-
parts.push("## Latest verification state");
|
|
50560
|
-
for (const item of [...new Set(verification)].slice(-6)) parts.push(`- ${item}`);
|
|
50561
|
-
}
|
|
50562
|
-
if (decisions.length) {
|
|
50563
|
-
parts.push("## Recent active decisions");
|
|
50564
|
-
for (const item of [...new Set(decisions)].slice(-6)) parts.push(`- ${item}`);
|
|
50565
|
-
}
|
|
50566
|
-
if (assistantNotes.length) {
|
|
50567
|
-
parts.push("## Assistant conclusions (truncated)");
|
|
50568
|
-
for (const a of assistantNotes.slice(-5)) parts.push(`- ${a}`);
|
|
50569
|
-
}
|
|
50570
|
-
if (tools.size) {
|
|
50571
|
-
const ranked = [...tools.entries()].sort((a, b) => b[1] - a[1]).slice(0, 12).map(([n, c]) => `${n}\xD7${c}`);
|
|
50572
|
-
parts.push(`## Tools used: ${ranked.join(", ")}`);
|
|
50573
|
-
}
|
|
50574
|
-
if (files.size) {
|
|
50575
|
-
const list = [...files].slice(0, 24);
|
|
50576
|
-
parts.push(`## Paths mentioned: ${list.join(", ")}`);
|
|
50577
|
-
}
|
|
50578
|
-
parts.push(
|
|
50579
|
-
"Continue from the recent messages below; do not re-ask goals already answered above unless the user changes them."
|
|
50580
|
-
);
|
|
50581
|
-
let out = parts.join("\n");
|
|
50582
|
-
if (out.length > maxChars) {
|
|
50583
|
-
out = `${out.slice(0, maxChars - 1)}\u2026`;
|
|
50584
|
-
}
|
|
50585
|
-
return out;
|
|
50586
|
-
}
|
|
50587
|
-
function oneLine(s, max) {
|
|
50588
|
-
const t = s.replace(/\s+/g, " ").trim();
|
|
50589
|
-
if (t.length <= max) return t;
|
|
50590
|
-
return `${t.slice(0, max - 1)}\u2026`;
|
|
50591
|
-
}
|
|
50592
|
-
function collectPaths3(args, out) {
|
|
50593
|
-
if (!args || typeof args !== "object") return;
|
|
50594
|
-
const obj = args;
|
|
50595
|
-
for (const key of ["path", "file", "filepath", "filePath", "target", "cwd"]) {
|
|
50596
|
-
const v = obj[key];
|
|
50597
|
-
if (typeof v === "string" && v.length > 1 && v.length < 260) {
|
|
50598
|
-
out.add(v.replace(/\\/g, "/"));
|
|
50599
|
-
}
|
|
50600
|
-
}
|
|
50601
|
-
if (typeof obj.file_path === "string") out.add(String(obj.file_path));
|
|
50602
|
-
}
|
|
50603
|
-
function collectPathsFromText(text, out) {
|
|
50604
|
-
const re = /(?:^|[\s"'`])((?:[\w.-]+\/)+[\w.-]+\.\w{1,8})/g;
|
|
50605
|
-
let m;
|
|
50606
|
-
let n = 0;
|
|
50607
|
-
while ((m = re.exec(text)) !== null && n < 8) {
|
|
50608
|
-
out.add(m[1]);
|
|
50609
|
-
n += 1;
|
|
50610
|
-
}
|
|
50611
|
-
}
|
|
50612
|
-
var MAX_SUMMARY_CHARS;
|
|
50613
|
-
var init_historySummary = __esm({
|
|
50614
|
-
"src/cli/budget/historySummary.ts"() {
|
|
50615
|
-
"use strict";
|
|
50616
|
-
MAX_SUMMARY_CHARS = 3500;
|
|
50617
|
-
}
|
|
50618
|
-
});
|
|
50619
|
-
|
|
50620
|
-
// src/cli/budget/llmCompact.ts
|
|
50621
|
-
function isLlmCompactEnabled() {
|
|
50622
|
-
const v = process.env.ZELARI_LLM_COMPACT?.trim().toLowerCase();
|
|
50623
|
-
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
50624
|
-
return true;
|
|
50625
|
-
}
|
|
50626
|
-
function compactModelOverride() {
|
|
50627
|
-
const v = process.env.ZELARI_COMPACT_MODEL?.trim();
|
|
50628
|
-
return v ? v : void 0;
|
|
50629
|
-
}
|
|
50630
|
-
async function llmSummarizeHistoryReplay(input) {
|
|
50631
|
-
const override = input.overrideModel ?? compactModelOverride();
|
|
50632
|
-
const model = override ?? input.model;
|
|
50633
|
-
const cacheReuseExpected = !override;
|
|
50634
|
-
if (!isLlmCompactEnabled()) return { summary: null, model, cacheReuseExpected };
|
|
50635
|
-
if (input.droppedMessages.length === 0) {
|
|
50636
|
-
return { summary: null, model, cacheReuseExpected };
|
|
50637
|
-
}
|
|
50638
|
-
const messages = [
|
|
50639
|
-
...input.systemMessages,
|
|
50640
|
-
...input.droppedMessages,
|
|
50641
|
-
{
|
|
50642
|
-
role: "user",
|
|
50643
|
-
content: COMPACTION_INSTRUCTION
|
|
50644
|
-
}
|
|
50645
|
-
];
|
|
50646
|
-
const controller = new AbortController();
|
|
50647
|
-
const timeout = setTimeout(() => controller.abort(), REPLAY_TIMEOUT_MS);
|
|
50648
|
-
const onOuterAbort = () => controller.abort();
|
|
50649
|
-
input.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
50650
|
-
try {
|
|
50651
|
-
let text = "";
|
|
50652
|
-
let emittedToolCall = false;
|
|
50653
|
-
for await (const delta of input.providerStream({
|
|
50654
|
-
provider: input.provider,
|
|
50655
|
-
model,
|
|
50656
|
-
messages,
|
|
50657
|
-
// Tools stay advertised: dropping them would change the prefix token
|
|
50658
|
-
// sequence and destroy cache reuse (explicit DSH decision). They are
|
|
50659
|
-
// sorted canonically (same discipline as the live routed request and
|
|
50660
|
-
// the snapshot fingerprints) so the replay prefix is byte-identical.
|
|
50661
|
-
tools: [...input.tools].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
|
|
50662
|
-
signal: controller.signal,
|
|
50663
|
-
generation: {
|
|
50664
|
-
purpose: "compaction",
|
|
50665
|
-
temperature: 0.1,
|
|
50666
|
-
maxTokens: 900
|
|
50667
|
-
}
|
|
50668
|
-
})) {
|
|
50669
|
-
if (delta.kind === "text") text += delta.delta;
|
|
50670
|
-
if (delta.kind === "tool_call") emittedToolCall = true;
|
|
50671
|
-
}
|
|
50672
|
-
if (emittedToolCall) return { summary: null, model, cacheReuseExpected };
|
|
50673
|
-
if (!text.trim()) return { summary: null, model, cacheReuseExpected };
|
|
50674
|
-
return { summary: text.trim(), model, cacheReuseExpected };
|
|
50675
|
-
} catch {
|
|
50676
|
-
return { summary: null, model, cacheReuseExpected };
|
|
50677
|
-
} finally {
|
|
50678
|
-
clearTimeout(timeout);
|
|
50679
|
-
input.signal?.removeEventListener("abort", onOuterAbort);
|
|
50680
|
-
}
|
|
50681
|
-
}
|
|
50682
|
-
var COMPACTION_INSTRUCTION, REPLAY_TIMEOUT_MS;
|
|
50683
|
-
var init_llmCompact = __esm({
|
|
50684
|
-
"src/cli/budget/llmCompact.ts"() {
|
|
50685
|
-
"use strict";
|
|
50686
|
-
COMPACTION_INSTRUCTION = `
|
|
50687
|
-
You are now acting as a compaction engine for this coding-agent session.
|
|
50688
|
-
|
|
50689
|
-
Condense the conversation ABOVE into a compact checkpoint sufficient to continue the task.
|
|
50690
|
-
|
|
50691
|
-
Preserve:
|
|
50692
|
-
- user's goal and evolving intent
|
|
50693
|
-
- decisions already made
|
|
50694
|
-
- exact file paths and identifiers
|
|
50695
|
-
- code changes already completed
|
|
50696
|
-
- commands/errors that still matter
|
|
50697
|
-
- constraints
|
|
50698
|
-
- unfinished work
|
|
50699
|
-
- the single most likely next action
|
|
50700
|
-
|
|
50701
|
-
Do not call tools.
|
|
50702
|
-
Do not mention this summarization request.
|
|
50703
|
-
Output only the checkpoint.
|
|
50704
|
-
Be concise.
|
|
50705
|
-
`.trim();
|
|
50706
|
-
REPLAY_TIMEOUT_MS = 6e4;
|
|
50707
|
-
}
|
|
50708
|
-
});
|
|
50709
|
-
|
|
50710
|
-
// src/cli/hooks/historyCompaction.ts
|
|
50711
|
-
function compactedRangeFromDropped(dropped) {
|
|
50712
|
-
if (dropped.length === 0) return void 0;
|
|
50713
|
-
const seqs = [];
|
|
50714
|
-
const sources = [];
|
|
50715
|
-
for (const m of dropped) {
|
|
50716
|
-
const hasCompactRange = typeof m.compactedFromSeq === "number" && Number.isInteger(m.compactedFromSeq) && m.compactedFromSeq > 0 && typeof m.compactedToSeq === "number" && Number.isInteger(m.compactedToSeq) && m.compactedToSeq >= m.compactedFromSeq;
|
|
50717
|
-
if (hasCompactRange) {
|
|
50718
|
-
seqs.push(m.compactedFromSeq, m.compactedToSeq);
|
|
50719
|
-
sources.push(...m.sourceEventSeqs ?? []);
|
|
50720
|
-
if (typeof m.seq === "number" && Number.isInteger(m.seq) && m.seq > 0) {
|
|
50721
|
-
seqs.push(m.seq);
|
|
50722
|
-
sources.push(m.seq);
|
|
50723
|
-
}
|
|
50724
|
-
continue;
|
|
50725
|
-
}
|
|
50726
|
-
if (typeof m.seq !== "number" || !Number.isInteger(m.seq) || m.seq < 1) return void 0;
|
|
50727
|
-
seqs.push(m.seq);
|
|
50728
|
-
sources.push(m.seq);
|
|
50729
|
-
}
|
|
50730
|
-
return {
|
|
50731
|
-
fromSeq: Math.min(...seqs),
|
|
50732
|
-
toSeq: Math.max(...seqs),
|
|
50733
|
-
sourceEventSeqs: [...new Set(sources)]
|
|
50734
|
-
};
|
|
50735
|
-
}
|
|
50736
|
-
function withDroppedRange(result, dropped, strategy) {
|
|
50737
|
-
const range = compactedRangeFromDropped(dropped);
|
|
50738
|
-
if (!range) return { ...result, strategy };
|
|
50739
|
-
return { ...result, ...range, strategy };
|
|
50740
|
-
}
|
|
50741
|
-
function resolveMaxMessages(opts) {
|
|
50742
|
-
const envTurns = envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
|
|
50743
|
-
let turns = opts?.maxMessages ? Math.ceil(opts.maxMessages / 4) : envTurns;
|
|
50744
|
-
if (opts?.durableStatePresent && !opts?.maxMessages && process.env.ZELARI_HISTORY_TURNS === void 0) {
|
|
50745
|
-
turns = Math.min(turns, 3);
|
|
50746
|
-
}
|
|
50747
|
-
if (turns <= 0) return 0;
|
|
50748
|
-
if (opts?.force && opts?.maxMessages) return Math.max(1, opts.maxMessages);
|
|
50749
|
-
return turns * 4;
|
|
50750
|
-
}
|
|
50751
|
-
function findValidCutIndex(messages, naiveCut) {
|
|
50752
|
-
let cut = naiveCut;
|
|
50753
|
-
while (cut < messages.length) {
|
|
50754
|
-
const kept = messages.slice(cut);
|
|
50755
|
-
const declared = /* @__PURE__ */ new Set();
|
|
50756
|
-
for (const m of kept) {
|
|
50757
|
-
if (m.role === "assistant" && m.toolCalls) {
|
|
50758
|
-
for (const tc of m.toolCalls) declared.add(tc.id);
|
|
50759
|
-
}
|
|
50760
|
-
}
|
|
50761
|
-
let moved = false;
|
|
50762
|
-
for (let k = 0; k < kept.length; k++) {
|
|
50763
|
-
const m = kept[k];
|
|
50764
|
-
if (m.role === "tool" && m.toolCallId && !declared.has(m.toolCallId)) {
|
|
50765
|
-
for (let j = cut - 1; j >= 0; j--) {
|
|
50766
|
-
const prev2 = messages[j];
|
|
50767
|
-
if (prev2.role === "assistant" && prev2.toolCalls && prev2.toolCalls.some((tc) => tc.id === m.toolCallId)) {
|
|
50768
|
-
cut = j;
|
|
50769
|
-
moved = true;
|
|
50770
|
-
break;
|
|
50771
|
-
}
|
|
50772
|
-
}
|
|
50773
|
-
break;
|
|
50774
|
-
}
|
|
50775
|
-
}
|
|
50776
|
-
if (!moved) break;
|
|
50777
|
-
}
|
|
50778
|
-
return cut;
|
|
50779
|
-
}
|
|
50780
|
-
function resolvePruneLimits(opts) {
|
|
50781
|
-
const maxChars = opts?.maxChars ?? envNumber(process.env.ZELARI_TOOL_RESULT_MAX_CHARS, { default: 8e3, min: 256 });
|
|
50782
|
-
const rawTail = opts?.tailChars ?? envNumber(process.env.ZELARI_TOOL_RESULT_TAIL_CHARS, { default: 1e3, min: 0 });
|
|
50783
|
-
const tailChars = Math.min(rawTail, maxChars);
|
|
50784
|
-
return { maxChars, tailChars };
|
|
50785
|
-
}
|
|
50786
|
-
function pruneToolResultsDetailed(messages, opts) {
|
|
50787
|
-
const { maxChars, tailChars } = resolvePruneLimits(opts);
|
|
50788
|
-
const headChars = maxChars - tailChars;
|
|
50789
|
-
const stats = { pruned: 0, charsOmitted: 0 };
|
|
50790
|
-
let changed = false;
|
|
50791
|
-
const out = messages.map((m) => {
|
|
50792
|
-
if (m.role !== "tool") return m;
|
|
50793
|
-
const body = m.content ?? "";
|
|
50794
|
-
if (body.length <= maxChars) return m;
|
|
50795
|
-
const head = headChars > 0 ? body.slice(0, headChars) : "";
|
|
50796
|
-
const tail2 = tailChars > 0 ? body.slice(-tailChars) : "";
|
|
50797
|
-
const omitted = body.length - head.length - tail2.length;
|
|
50798
|
-
changed = true;
|
|
50799
|
-
stats.pruned += 1;
|
|
50800
|
-
stats.charsOmitted += omitted;
|
|
50801
|
-
return {
|
|
50802
|
-
...m,
|
|
50803
|
-
content: [head, "\u2026[pruned " + omitted + " chars]\u2026", tail2].join(String.fromCharCode(10))
|
|
50804
|
-
};
|
|
50805
|
-
});
|
|
50806
|
-
return {
|
|
50807
|
-
messages: changed ? out : messages,
|
|
50808
|
-
stats
|
|
50809
|
-
};
|
|
50810
|
-
}
|
|
50811
|
-
function compactHistory(messages, opts) {
|
|
50812
|
-
return compactHistoryDetailed(messages, opts).messages;
|
|
50813
|
-
}
|
|
50814
|
-
function buildCheckpointMessage(summaryText, range) {
|
|
50815
|
-
return {
|
|
50816
|
-
role: "user",
|
|
50817
|
-
content: CHECKPOINT_WRAPPER_PREFIX + "\n\n<compacted-summary>\n" + summaryText + "\n</compacted-summary>",
|
|
50818
|
-
...range ? {
|
|
50819
|
-
compactedFromSeq: range.fromSeq,
|
|
50820
|
-
compactedToSeq: range.toSeq,
|
|
50821
|
-
sourceEventSeqs: [...range.sourceEventSeqs]
|
|
50822
|
-
} : {}
|
|
50823
|
-
};
|
|
50824
|
-
}
|
|
50825
|
-
function compactHistoryDetailed(messages, opts) {
|
|
50826
|
-
const maxMessages = resolveMaxMessages(opts);
|
|
50827
|
-
if (maxMessages === 0) {
|
|
50828
|
-
return withDroppedRange(
|
|
50829
|
-
{ messages: [], compacted: true, messagesRemoved: messages.length, summary: "" },
|
|
50830
|
-
messages,
|
|
50831
|
-
"extractive"
|
|
50832
|
-
);
|
|
50833
|
-
}
|
|
50834
|
-
if (messages.length <= maxMessages * 2 && !opts?.force) {
|
|
50835
|
-
return {
|
|
50836
|
-
messages,
|
|
50837
|
-
compacted: false,
|
|
50838
|
-
messagesRemoved: 0,
|
|
50839
|
-
summary: ""
|
|
50840
|
-
};
|
|
50841
|
-
}
|
|
50842
|
-
const naiveCut = Math.max(0, messages.length - maxMessages);
|
|
50843
|
-
const cut = findValidCutIndex(messages, naiveCut);
|
|
50844
|
-
if (cut === 0) {
|
|
50845
|
-
return {
|
|
50846
|
-
messages,
|
|
50847
|
-
compacted: false,
|
|
50848
|
-
messagesRemoved: 0,
|
|
50849
|
-
summary: ""
|
|
50850
|
-
};
|
|
50851
|
-
}
|
|
50852
|
-
const droppedMsgs = messages.slice(0, cut);
|
|
50853
|
-
const droppedRange = compactedRangeFromDropped(droppedMsgs);
|
|
50854
|
-
const pruned = pruneToolResultsDetailed(messages.slice(cut));
|
|
50855
|
-
const kept = pruned.messages;
|
|
50856
|
-
const summaryText = extractiveHistorySummary(droppedMsgs);
|
|
50857
|
-
const summary = buildCheckpointMessage(
|
|
50858
|
-
summaryText || `${COMPACT_MARKER} ${cut} earlier message(s) dropped.`,
|
|
50859
|
-
droppedRange
|
|
50860
|
-
);
|
|
50861
|
-
return withDroppedRange(
|
|
50862
|
-
{
|
|
50863
|
-
messages: [summary, ...kept],
|
|
50864
|
-
compacted: true,
|
|
50865
|
-
messagesRemoved: cut,
|
|
50866
|
-
summary: summary.content,
|
|
50867
|
-
prunedToolResults: pruned.stats.pruned
|
|
50868
|
-
},
|
|
50869
|
-
droppedMsgs,
|
|
50870
|
-
"extractive"
|
|
50871
|
-
);
|
|
50872
|
-
}
|
|
50873
|
-
async function compactHistoryAsync(messages, opts) {
|
|
50874
|
-
const base2 = compactHistoryDetailed(messages, opts);
|
|
50875
|
-
if (!base2.compacted || base2.messagesRemoved === 0) return base2;
|
|
50876
|
-
const cut = base2.messagesRemoved;
|
|
50877
|
-
const droppedMsgs = messages.slice(0, cut);
|
|
50878
|
-
const extractive = extractiveHistorySummary(droppedMsgs);
|
|
50879
|
-
let summaryText = extractive;
|
|
50880
|
-
let cacheReuseExpected;
|
|
50881
|
-
let replayExactPrefix;
|
|
50882
|
-
const canReplay = !!(opts?.providerStream && opts?.requestSnapshot);
|
|
50883
|
-
if (canReplay) {
|
|
50884
|
-
try {
|
|
50885
|
-
const replay = await llmSummarizeHistoryReplay({
|
|
50886
|
-
providerStream: opts.providerStream,
|
|
50887
|
-
provider: opts.requestSnapshot.provider,
|
|
50888
|
-
model: opts.requestSnapshot.model,
|
|
50889
|
-
systemMessages: opts.requestSnapshot.systemMessages,
|
|
50890
|
-
tools: opts.requestSnapshot.tools,
|
|
50891
|
-
droppedMessages: droppedMsgs,
|
|
50892
|
-
signal: opts?.signal
|
|
50893
|
-
});
|
|
50894
|
-
cacheReuseExpected = replay.cacheReuseExpected;
|
|
50895
|
-
if (replay.summary && replay.summary.trim().length > 40) {
|
|
50896
|
-
const sourceTokens = roughTokens(droppedMsgs);
|
|
50897
|
-
const summaryTok = Math.ceil(replay.summary.length / 4);
|
|
50898
|
-
if (summaryTok < sourceTokens) {
|
|
50899
|
-
summaryText = replay.summary.trim();
|
|
50900
|
-
}
|
|
50901
|
-
}
|
|
50902
|
-
} catch {
|
|
50903
|
-
}
|
|
50904
|
-
}
|
|
50905
|
-
const pruned = pruneToolResultsDetailed(messages.slice(cut));
|
|
50906
|
-
const kept = pruned.messages;
|
|
50907
|
-
const summary = buildCheckpointMessage(summaryText, compactedRangeFromDropped(droppedMsgs));
|
|
50908
|
-
const usedLlm = summaryText !== extractive && summaryText.trim().length > 40;
|
|
50909
|
-
return withDroppedRange(
|
|
50910
|
-
{
|
|
50911
|
-
messages: [summary, ...kept],
|
|
50912
|
-
compacted: true,
|
|
50913
|
-
messagesRemoved: cut,
|
|
50914
|
-
summary: summaryText,
|
|
50915
|
-
prunedToolResults: pruned.stats.pruned,
|
|
50916
|
-
cacheReuseExpected,
|
|
50917
|
-
replayExactPrefix
|
|
50918
|
-
},
|
|
50919
|
-
droppedMsgs,
|
|
50920
|
-
usedLlm ? "llm" : "extractive"
|
|
50921
|
-
);
|
|
50922
|
-
}
|
|
50923
|
-
function roughTokens(msgs) {
|
|
50924
|
-
let n = 0;
|
|
50925
|
-
for (const m of msgs) {
|
|
50926
|
-
n += Math.ceil((m.content ?? "").length / 4);
|
|
50927
|
-
if (m.toolCalls) {
|
|
50928
|
-
for (const tc of m.toolCalls) {
|
|
50929
|
-
n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
|
|
50930
|
-
}
|
|
50931
|
-
}
|
|
50932
|
-
}
|
|
50933
|
-
return Math.max(1, n);
|
|
50934
|
-
}
|
|
50935
|
-
var COMPACT_MARKER, CHECKPOINT_WRAPPER_PREFIX;
|
|
50936
|
-
var init_historyCompaction = __esm({
|
|
50937
|
-
"src/cli/hooks/historyCompaction.ts"() {
|
|
50938
|
-
"use strict";
|
|
50939
|
-
init_historySummary();
|
|
50940
|
-
init_llmCompact();
|
|
50941
|
-
init_envNumber();
|
|
50942
|
-
COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
50943
|
-
CHECKPOINT_WRAPPER_PREFIX = "This is an automatically generated checkpoint of earlier conversation. Treat it as established context and continue directly.";
|
|
50944
|
-
}
|
|
50945
|
-
});
|
|
50946
|
-
|
|
50947
51278
|
// src/cli/budget/requestSnapshotStore.ts
|
|
50948
51279
|
function recordRequestSnapshot(sessionId2, snapshot) {
|
|
50949
51280
|
store5.set(sessionId2, { snapshot });
|
|
@@ -51253,215 +51584,6 @@ var init_phase = __esm({
|
|
|
51253
51584
|
}
|
|
51254
51585
|
});
|
|
51255
51586
|
|
|
51256
|
-
// src/cli/budget/tokenBudget.ts
|
|
51257
|
-
function mergeCompactRange(into, r) {
|
|
51258
|
-
if (r.fromSeq !== void 0 && r.toSeq !== void 0) {
|
|
51259
|
-
into.fromSeq = into.fromSeq === void 0 ? r.fromSeq : Math.min(into.fromSeq, r.fromSeq);
|
|
51260
|
-
into.toSeq = into.toSeq === void 0 ? r.toSeq : Math.max(into.toSeq, r.toSeq);
|
|
51261
|
-
if (r.sourceEventSeqs) into.sourceSeqs.push(...r.sourceEventSeqs);
|
|
51262
|
-
}
|
|
51263
|
-
if (r.strategy === "llm") into.strategy = "llm";
|
|
51264
|
-
else if (r.strategy && !into.strategy) into.strategy = r.strategy;
|
|
51265
|
-
}
|
|
51266
|
-
function estimateTokens2(text) {
|
|
51267
|
-
if (!text) return 0;
|
|
51268
|
-
return Math.max(1, Math.ceil(text.length / 4));
|
|
51269
|
-
}
|
|
51270
|
-
function estimateHistoryTokens(messages) {
|
|
51271
|
-
let n = 0;
|
|
51272
|
-
for (const m of messages) {
|
|
51273
|
-
n += estimateTokens2(m.content);
|
|
51274
|
-
if (m.toolCalls) {
|
|
51275
|
-
for (const tc of m.toolCalls) {
|
|
51276
|
-
n += estimateTokens2(tc.name) + estimateTokens2(JSON.stringify(tc.args ?? {}));
|
|
51277
|
-
}
|
|
51278
|
-
}
|
|
51279
|
-
}
|
|
51280
|
-
return n;
|
|
51281
|
-
}
|
|
51282
|
-
function defaultContextLimitForModel(model, provider) {
|
|
51283
|
-
return capabilitiesFor(model, provider).contextWindow;
|
|
51284
|
-
}
|
|
51285
|
-
function resolveContextLimit(model, provider) {
|
|
51286
|
-
return envNumber(process.env.ZELARI_CONTEXT_LIMIT, {
|
|
51287
|
-
default: defaultContextLimitForModel(model, provider),
|
|
51288
|
-
min: 4e3,
|
|
51289
|
-
max: 2e6
|
|
51290
|
-
});
|
|
51291
|
-
}
|
|
51292
|
-
function phaseKnobs(phase2) {
|
|
51293
|
-
return {
|
|
51294
|
-
historyTurns: phase2 === "plan" ? envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 8, min: 0 }) : envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 }),
|
|
51295
|
-
maxToolLoopIterations: phase2 === "plan" ? envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 60, min: 1 }) : envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
|
|
51296
|
-
default: 120,
|
|
51297
|
-
min: 1
|
|
51298
|
-
})
|
|
51299
|
-
};
|
|
51300
|
-
}
|
|
51301
|
-
async function applyBudgetPolicyAsync(history2, phase2, opts) {
|
|
51302
|
-
const contextLimit = resolveContextLimit(opts?.model, opts?.provider);
|
|
51303
|
-
const compact3 = capabilitiesFor(opts?.model, opts?.provider).compaction;
|
|
51304
|
-
const sessionExtra = opts?.sessionTokens ?? 0;
|
|
51305
|
-
const warnings = [];
|
|
51306
|
-
let { historyTurns, maxToolLoopIterations } = phaseKnobs(phase2);
|
|
51307
|
-
const envelope = opts?.requestSnapshot ?? null;
|
|
51308
|
-
const surface = envelope?.snapshot ?? opts?.requestSurface ?? null;
|
|
51309
|
-
const replayBase = surface ? {
|
|
51310
|
-
provider: surface.provider,
|
|
51311
|
-
model: surface.model,
|
|
51312
|
-
systemMessages: surface.systemMessages,
|
|
51313
|
-
tools: surface.tools
|
|
51314
|
-
} : opts?.providerStream ? { provider: "local", model: opts?.model ?? "unknown", systemMessages: [], tools: [] } : null;
|
|
51315
|
-
const headerTokens = surface ? estimateSystemTokensLite(surface.systemMessages) + estimateToolSchemaTokensLite(surface.tools) : 0;
|
|
51316
|
-
const convTokensOf = (h) => estimateConversationTokensLite(h);
|
|
51317
|
-
let hist = history2;
|
|
51318
|
-
let estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
51319
|
-
let occupancy = Math.min(1, estimated / contextLimit);
|
|
51320
|
-
let compactSummary = "";
|
|
51321
|
-
let messagesRemoved = 0;
|
|
51322
|
-
let cacheReuseExpected;
|
|
51323
|
-
let prunedTotal = 0;
|
|
51324
|
-
const compactRange = { sourceSeqs: [] };
|
|
51325
|
-
if (occupancy >= compact3.warnAt && occupancy < compact3.compactAt) {
|
|
51326
|
-
warnings.push(
|
|
51327
|
-
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated}/${contextLimit} tok full-request est.) \u2014 consider /compact or shorter replies.`
|
|
51328
|
-
);
|
|
51329
|
-
}
|
|
51330
|
-
if (occupancy >= 0.8) {
|
|
51331
|
-
const pruned = pruneToolResultsDetailed(hist);
|
|
51332
|
-
if (pruned.stats.pruned > 0) {
|
|
51333
|
-
hist = pruned.messages;
|
|
51334
|
-
prunedTotal += pruned.stats.pruned;
|
|
51335
|
-
estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
51336
|
-
occupancy = Math.min(1, estimated / contextLimit);
|
|
51337
|
-
warnings.push(
|
|
51338
|
-
`[budget] pruned ${pruned.stats.pruned} oversized tool result(s) \u2192 ${Math.round(occupancy * 100)}% (${estimated} tok).`
|
|
51339
|
-
);
|
|
51340
|
-
}
|
|
51341
|
-
}
|
|
51342
|
-
const fold = (r, label, forcedTurns) => {
|
|
51343
|
-
hist = applySessionSurface(r.messages);
|
|
51344
|
-
if (r.compacted) {
|
|
51345
|
-
messagesRemoved += r.messagesRemoved;
|
|
51346
|
-
if (r.summary) compactSummary = r.summary;
|
|
51347
|
-
mergeCompactRange(compactRange, r);
|
|
51348
|
-
if (r.cacheReuseExpected !== void 0) {
|
|
51349
|
-
cacheReuseExpected = r.cacheReuseExpected;
|
|
51350
|
-
}
|
|
51351
|
-
}
|
|
51352
|
-
estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
51353
|
-
occupancy = Math.min(1, estimated / contextLimit);
|
|
51354
|
-
warnings.push(
|
|
51355
|
-
`[budget] ${label} \u2014 kept ~${forcedTurns} turns (${estimated} tok est.` + (r.messagesRemoved ? `, removed ${r.messagesRemoved} msgs` : "") + (r.cacheReuseExpected === false ? ", cache reuse NOT expected (model override)" : "") + ")."
|
|
51356
|
-
);
|
|
51357
|
-
};
|
|
51358
|
-
if (occupancy >= compact3.compactAt) {
|
|
51359
|
-
const forcedTurns = Math.max(1, Math.floor(historyTurns / 2));
|
|
51360
|
-
historyTurns = forcedTurns;
|
|
51361
|
-
maxToolLoopIterations = Math.min(
|
|
51362
|
-
maxToolLoopIterations,
|
|
51363
|
-
phase2 === "plan" ? 24 : 40
|
|
51364
|
-
);
|
|
51365
|
-
let r = await compactHistoryAsync(hist, {
|
|
51366
|
-
maxMessages: Math.max(2, forcedTurns * 4),
|
|
51367
|
-
force: true,
|
|
51368
|
-
signal: opts?.signal,
|
|
51369
|
-
...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
|
|
51370
|
-
});
|
|
51371
|
-
if (!r.compacted) {
|
|
51372
|
-
r = await compactHistoryAsync(hist, {
|
|
51373
|
-
maxMessages: 2,
|
|
51374
|
-
force: true,
|
|
51375
|
-
signal: opts?.signal,
|
|
51376
|
-
...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
|
|
51377
|
-
});
|
|
51378
|
-
}
|
|
51379
|
-
const label = r.cacheReuseExpected === false ? "llm-compact (model override)" : "auto-compact";
|
|
51380
|
-
fold(r, label, forcedTurns);
|
|
51381
|
-
}
|
|
51382
|
-
if (occupancy >= compact3.hardAt) {
|
|
51383
|
-
const hard = await compactHistoryAsync(hist, {
|
|
51384
|
-
maxMessages: 2,
|
|
51385
|
-
force: true,
|
|
51386
|
-
signal: opts?.signal
|
|
51387
|
-
});
|
|
51388
|
-
fold(hard, hard.summary.includes("\xB7 llm") ? "llm-compact" : "HARD trim", 2);
|
|
51389
|
-
historyTurns = 2;
|
|
51390
|
-
maxToolLoopIterations = Math.min(maxToolLoopIterations, 16);
|
|
51391
|
-
warnings.push(
|
|
51392
|
-
"[budget] HARD context pressure (\u226595%) \u2014 prefer /clear or a new session if quality drops."
|
|
51393
|
-
);
|
|
51394
|
-
}
|
|
51395
|
-
const cacheMetricsLine = envelope ? [
|
|
51396
|
-
"compaction meter:",
|
|
51397
|
-
`provider/model: ${envelope.snapshot.provider}/${envelope.snapshot.model}`,
|
|
51398
|
-
`headerFingerprint: ${envelope.snapshot.headerFingerprint.slice(0, 12)}`,
|
|
51399
|
-
`occupancy: ${Math.round(occupancy * 100)}% (${estimated}/${contextLimit})`,
|
|
51400
|
-
...envelope.usage?.cachedPromptTokens !== void 0 ? [`cachedPromptTokens: ${envelope.usage.cachedPromptTokens}`] : [],
|
|
51401
|
-
...cacheReuseExpected !== void 0 ? [`cacheReuseExpected: ${cacheReuseExpected}`] : []
|
|
51402
|
-
].join(" | ") : void 0;
|
|
51403
|
-
return {
|
|
51404
|
-
history: hist,
|
|
51405
|
-
warnings,
|
|
51406
|
-
maxToolLoopIterations,
|
|
51407
|
-
historyTurns,
|
|
51408
|
-
estimatedHistoryTokens: surface ? convTokensOf(hist) : estimated,
|
|
51409
|
-
contextLimit,
|
|
51410
|
-
occupancy,
|
|
51411
|
-
compactSummary: compactSummary || void 0,
|
|
51412
|
-
messagesRemoved: messagesRemoved || void 0,
|
|
51413
|
-
...compactRange.fromSeq !== void 0 && compactRange.toSeq !== void 0 ? {
|
|
51414
|
-
compactedFromSeq: compactRange.fromSeq,
|
|
51415
|
-
compactedToSeq: compactRange.toSeq,
|
|
51416
|
-
compactSourceSeqs: compactRange.sourceSeqs,
|
|
51417
|
-
compactStrategy: compactRange.strategy
|
|
51418
|
-
} : {},
|
|
51419
|
-
...surface ? { contextPressureTokens: estimated } : {},
|
|
51420
|
-
...cacheReuseExpected !== void 0 ? { cacheReuseExpected } : {},
|
|
51421
|
-
...cacheMetricsLine ? { cacheMetricsLine } : {}
|
|
51422
|
-
};
|
|
51423
|
-
}
|
|
51424
|
-
function estimateSystemTokensLite(systemMessages) {
|
|
51425
|
-
let n = 0;
|
|
51426
|
-
for (const m of systemMessages) n += 4 + Math.ceil((m.content ?? "").length / 4);
|
|
51427
|
-
return n;
|
|
51428
|
-
}
|
|
51429
|
-
function estimateToolSchemaTokensLite(tools) {
|
|
51430
|
-
let n = 0;
|
|
51431
|
-
for (const t of tools) {
|
|
51432
|
-
n += Math.ceil((t.name ?? "").length / 4);
|
|
51433
|
-
n += Math.ceil((t.description ?? "").length / 4);
|
|
51434
|
-
n += Math.ceil(JSON.stringify(t.parameters ?? {}).length / 4);
|
|
51435
|
-
}
|
|
51436
|
-
return n + tools.length * 4;
|
|
51437
|
-
}
|
|
51438
|
-
function estimateConversationTokensLite(messages) {
|
|
51439
|
-
let n = 0;
|
|
51440
|
-
for (const m of messages) {
|
|
51441
|
-
n += 4 + Math.ceil((m.content ?? "").length / 4);
|
|
51442
|
-
if (m.toolCalls) {
|
|
51443
|
-
for (const tc of m.toolCalls) {
|
|
51444
|
-
n += Math.ceil(tc.name.length / 4) + Math.ceil(tc.id.length / 4);
|
|
51445
|
-
n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
|
|
51446
|
-
}
|
|
51447
|
-
}
|
|
51448
|
-
if (m.reasoningContent) n += Math.ceil(m.reasoningContent.length / 4);
|
|
51449
|
-
if (m.toolCallId) n += Math.ceil(m.toolCallId.length / 4);
|
|
51450
|
-
}
|
|
51451
|
-
return n;
|
|
51452
|
-
}
|
|
51453
|
-
var RESERVED_OUTPUT_TOKENS;
|
|
51454
|
-
var init_tokenBudget = __esm({
|
|
51455
|
-
"src/cli/budget/tokenBudget.ts"() {
|
|
51456
|
-
"use strict";
|
|
51457
|
-
init_envNumber();
|
|
51458
|
-
init_historyCompaction();
|
|
51459
|
-
init_observationStore();
|
|
51460
|
-
init_capabilities();
|
|
51461
|
-
RESERVED_OUTPUT_TOKENS = 8192;
|
|
51462
|
-
}
|
|
51463
|
-
});
|
|
51464
|
-
|
|
51465
51587
|
// src/cli/budget/requestMeter.ts
|
|
51466
51588
|
function estimateTokensLocal(text) {
|
|
51467
51589
|
if (!text) return 0;
|
|
@@ -55182,39 +55304,224 @@ var init_toolRegistry2 = __esm({
|
|
|
55182
55304
|
}
|
|
55183
55305
|
});
|
|
55184
55306
|
|
|
55307
|
+
// src/cli/mcp/httpTransport.ts
|
|
55308
|
+
var SSE_DATA_RE, ABORT_GRACE_MS, McpHttpTransport;
|
|
55309
|
+
var init_httpTransport = __esm({
|
|
55310
|
+
"src/cli/mcp/httpTransport.ts"() {
|
|
55311
|
+
"use strict";
|
|
55312
|
+
SSE_DATA_RE = /^data:\s?(.*)$/;
|
|
55313
|
+
ABORT_GRACE_MS = 250;
|
|
55314
|
+
McpHttpTransport = class {
|
|
55315
|
+
constructor(opts) {
|
|
55316
|
+
this.opts = opts;
|
|
55317
|
+
}
|
|
55318
|
+
sessionId = null;
|
|
55319
|
+
closed = false;
|
|
55320
|
+
reinit = null;
|
|
55321
|
+
controllers = /* @__PURE__ */ new Set();
|
|
55322
|
+
get hasSession() {
|
|
55323
|
+
return this.sessionId !== null;
|
|
55324
|
+
}
|
|
55325
|
+
/**
|
|
55326
|
+
* Deliver one JSON-RPC message. Resolves once the response (if any) has
|
|
55327
|
+
* been fed to onMessage. Transport-level failures are converted into
|
|
55328
|
+
* JSON-RPC error responses so the client's pending map rejects cleanly
|
|
55329
|
+
* through the same pump used for stdio.
|
|
55330
|
+
*/
|
|
55331
|
+
async send(msg, timeoutMs2) {
|
|
55332
|
+
if (this.closed) throw new Error(`[mcp:${this.opts.serverName}] transport closed`);
|
|
55333
|
+
try {
|
|
55334
|
+
await this.post(msg, timeoutMs2, false);
|
|
55335
|
+
} catch (err) {
|
|
55336
|
+
if (msg.id !== void 0) {
|
|
55337
|
+
this.opts.onMessage({
|
|
55338
|
+
jsonrpc: "2.0",
|
|
55339
|
+
id: msg.id,
|
|
55340
|
+
error: {
|
|
55341
|
+
code: -32e3,
|
|
55342
|
+
message: err instanceof Error ? err.message : String(err)
|
|
55343
|
+
}
|
|
55344
|
+
});
|
|
55345
|
+
}
|
|
55346
|
+
}
|
|
55347
|
+
}
|
|
55348
|
+
/** Best-effort session teardown (HTTP DELETE), then abort in-flight POSTs. */
|
|
55349
|
+
close() {
|
|
55350
|
+
this.closed = true;
|
|
55351
|
+
for (const ac of this.controllers) ac.abort();
|
|
55352
|
+
this.controllers.clear();
|
|
55353
|
+
const sid = this.sessionId;
|
|
55354
|
+
this.sessionId = null;
|
|
55355
|
+
if (!sid) return;
|
|
55356
|
+
const headers2 = {
|
|
55357
|
+
...this.opts.headers ?? {},
|
|
55358
|
+
"mcp-session-id": sid
|
|
55359
|
+
};
|
|
55360
|
+
void fetch(this.opts.url, { method: "DELETE", headers: headers2 }).catch(() => {
|
|
55361
|
+
});
|
|
55362
|
+
}
|
|
55363
|
+
// ── internals ────────────────────────────────────────────────────────
|
|
55364
|
+
async post(msg, timeoutMs2, replayed) {
|
|
55365
|
+
const ac = new AbortController();
|
|
55366
|
+
this.controllers.add(ac);
|
|
55367
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs2 + ABORT_GRACE_MS);
|
|
55368
|
+
const hadSession = this.sessionId !== null;
|
|
55369
|
+
try {
|
|
55370
|
+
const headers2 = {
|
|
55371
|
+
"content-type": "application/json",
|
|
55372
|
+
accept: "application/json, text/event-stream",
|
|
55373
|
+
...this.opts.headers ?? {}
|
|
55374
|
+
};
|
|
55375
|
+
if (this.sessionId) headers2["mcp-session-id"] = this.sessionId;
|
|
55376
|
+
const res = await fetch(this.opts.url, {
|
|
55377
|
+
method: "POST",
|
|
55378
|
+
headers: headers2,
|
|
55379
|
+
signal: ac.signal,
|
|
55380
|
+
body: JSON.stringify({ jsonrpc: "2.0", ...msg })
|
|
55381
|
+
});
|
|
55382
|
+
const sid = res.headers.get("mcp-session-id");
|
|
55383
|
+
if (sid) this.sessionId = sid;
|
|
55384
|
+
if (res.status === 404 && hadSession && !replayed && msg.id !== void 0) {
|
|
55385
|
+
this.sessionId = null;
|
|
55386
|
+
await this.ensureSession();
|
|
55387
|
+
return this.post(msg, timeoutMs2, true);
|
|
55388
|
+
}
|
|
55389
|
+
if (!res.ok && res.status !== 202) {
|
|
55390
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`.trim());
|
|
55391
|
+
}
|
|
55392
|
+
if (msg.id === void 0) {
|
|
55393
|
+
await res.body?.cancel();
|
|
55394
|
+
return;
|
|
55395
|
+
}
|
|
55396
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
55397
|
+
if (contentType.includes("text/event-stream")) {
|
|
55398
|
+
await this.readSse(res);
|
|
55399
|
+
} else {
|
|
55400
|
+
const body = await res.text();
|
|
55401
|
+
if (body) this.opts.onMessage(JSON.parse(body));
|
|
55402
|
+
}
|
|
55403
|
+
} finally {
|
|
55404
|
+
clearTimeout(timer);
|
|
55405
|
+
this.controllers.delete(ac);
|
|
55406
|
+
}
|
|
55407
|
+
}
|
|
55408
|
+
/** Dedupe concurrent session-loss recoveries into one handshake. */
|
|
55409
|
+
async ensureSession() {
|
|
55410
|
+
if (!this.reinit) {
|
|
55411
|
+
this.reinit = this.opts.onSessionLost().finally(() => {
|
|
55412
|
+
this.reinit = null;
|
|
55413
|
+
}).catch(() => {
|
|
55414
|
+
});
|
|
55415
|
+
}
|
|
55416
|
+
await this.reinit;
|
|
55417
|
+
}
|
|
55418
|
+
/** Minimal SSE reader: dispatch complete `data:` events to onMessage. */
|
|
55419
|
+
async readSse(res) {
|
|
55420
|
+
const reader = res.body?.getReader();
|
|
55421
|
+
if (!reader) return;
|
|
55422
|
+
const decoder = new TextDecoder();
|
|
55423
|
+
let buf = "";
|
|
55424
|
+
let data = "";
|
|
55425
|
+
const dispatch = () => {
|
|
55426
|
+
const payload = data.trim();
|
|
55427
|
+
data = "";
|
|
55428
|
+
if (!payload) return;
|
|
55429
|
+
try {
|
|
55430
|
+
this.opts.onMessage(JSON.parse(payload));
|
|
55431
|
+
} catch {
|
|
55432
|
+
}
|
|
55433
|
+
};
|
|
55434
|
+
for (; ; ) {
|
|
55435
|
+
const { done, value } = await reader.read();
|
|
55436
|
+
if (done) break;
|
|
55437
|
+
buf += decoder.decode(value, { stream: true });
|
|
55438
|
+
let nl;
|
|
55439
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
55440
|
+
const line = buf.slice(0, nl).replace(/\r$/, "");
|
|
55441
|
+
buf = buf.slice(nl + 1);
|
|
55442
|
+
if (line === "") {
|
|
55443
|
+
dispatch();
|
|
55444
|
+
continue;
|
|
55445
|
+
}
|
|
55446
|
+
const m = SSE_DATA_RE.exec(line);
|
|
55447
|
+
if (m) data += (data ? "\n" : "") + m[1];
|
|
55448
|
+
}
|
|
55449
|
+
}
|
|
55450
|
+
dispatch();
|
|
55451
|
+
}
|
|
55452
|
+
};
|
|
55453
|
+
}
|
|
55454
|
+
});
|
|
55455
|
+
|
|
55185
55456
|
// src/cli/mcp/mcpClient.ts
|
|
55186
55457
|
import { spawn as spawn14 } from "node:child_process";
|
|
55187
|
-
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
|
|
55458
|
+
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MAX_LIST_PAGES, MCP_PROTOCOL_VERSION, McpClient;
|
|
55188
55459
|
var init_mcpClient = __esm({
|
|
55189
55460
|
"src/cli/mcp/mcpClient.ts"() {
|
|
55190
55461
|
"use strict";
|
|
55191
55462
|
init_cmdline();
|
|
55192
55463
|
init_updater();
|
|
55464
|
+
init_httpTransport();
|
|
55193
55465
|
DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
55194
55466
|
INIT_TIMEOUT_MS = 15e3;
|
|
55467
|
+
MAX_LIST_PAGES = 50;
|
|
55195
55468
|
MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
55196
55469
|
McpClient = class {
|
|
55197
55470
|
constructor(serverName, config2) {
|
|
55198
55471
|
this.serverName = serverName;
|
|
55199
55472
|
this.config = config2;
|
|
55473
|
+
this.defaultTimeoutMs = config2.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
55474
|
+
this.serial = config2.serial ?? this.transportKind() === "http";
|
|
55200
55475
|
}
|
|
55201
55476
|
child = null;
|
|
55477
|
+
transport = null;
|
|
55202
55478
|
nextId = 1;
|
|
55203
55479
|
pending = /* @__PURE__ */ new Map();
|
|
55204
55480
|
stdoutBuffer = "";
|
|
55205
55481
|
closed = false;
|
|
55206
|
-
/**
|
|
55482
|
+
/** Tail of the serial queue (config.serial); requests run one at a time. */
|
|
55483
|
+
queueTail = Promise.resolve();
|
|
55484
|
+
/** True while the 404-recovery handshake runs: bypasses the serial queue
|
|
55485
|
+
* (the queued request that triggered the 404 is waiting on this handshake). */
|
|
55486
|
+
recovering = false;
|
|
55487
|
+
serial;
|
|
55488
|
+
defaultTimeoutMs;
|
|
55489
|
+
transportKind() {
|
|
55490
|
+
if (this.config.type) return this.config.type;
|
|
55491
|
+
return this.config.url && !this.config.command ? "http" : "stdio";
|
|
55492
|
+
}
|
|
55493
|
+
/** Connect (spawn / HTTP session) and run the MCP initialize handshake. */
|
|
55207
55494
|
async start() {
|
|
55208
|
-
if (this.child) return;
|
|
55495
|
+
if (this.child || this.transport) return;
|
|
55496
|
+
if (this.transportKind() === "http") {
|
|
55497
|
+
const url2 = this.config.url;
|
|
55498
|
+
if (!url2) {
|
|
55499
|
+
throw new Error(`[mcp:${this.serverName}] http server requires a url`);
|
|
55500
|
+
}
|
|
55501
|
+
this.transport = new McpHttpTransport({
|
|
55502
|
+
serverName: this.serverName,
|
|
55503
|
+
url: url2,
|
|
55504
|
+
// env may carry an explicit Authorization header for remote servers.
|
|
55505
|
+
headers: this.config.env?.AUTHORIZATION ? { Authorization: this.config.env.AUTHORIZATION } : void 0,
|
|
55506
|
+
onMessage: (msg) => this.handleMessage(msg),
|
|
55507
|
+
onSessionLost: () => this.handshake()
|
|
55508
|
+
});
|
|
55509
|
+
await this.handshake();
|
|
55510
|
+
return;
|
|
55511
|
+
}
|
|
55512
|
+
const command = this.config.command;
|
|
55513
|
+
if (!command) {
|
|
55514
|
+
throw new Error(`[mcp:${this.serverName}] stdio server requires a command`);
|
|
55515
|
+
}
|
|
55209
55516
|
const spawnOpts = {
|
|
55210
55517
|
stdio: ["pipe", "pipe", "pipe"],
|
|
55211
55518
|
env: { ...process.env, ...this.config.env ?? {} },
|
|
55212
55519
|
windowsHide: true
|
|
55213
55520
|
};
|
|
55214
|
-
const child = process.platform === "win32" ? spawn14(buildCmdLine(
|
|
55521
|
+
const child = process.platform === "win32" ? spawn14(buildCmdLine(command, this.config.args ?? []), {
|
|
55215
55522
|
...spawnOpts,
|
|
55216
55523
|
shell: true
|
|
55217
|
-
}) : spawn14(
|
|
55524
|
+
}) : spawn14(command, this.config.args ?? [], spawnOpts);
|
|
55218
55525
|
this.child = child;
|
|
55219
55526
|
child.stdout.setEncoding("utf8");
|
|
55220
55527
|
child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
@@ -55233,37 +55540,60 @@ var init_mcpClient = __esm({
|
|
|
55233
55540
|
);
|
|
55234
55541
|
}
|
|
55235
55542
|
});
|
|
55236
|
-
await this.
|
|
55237
|
-
|
|
55238
|
-
|
|
55239
|
-
|
|
55240
|
-
|
|
55241
|
-
|
|
55242
|
-
|
|
55243
|
-
|
|
55244
|
-
|
|
55245
|
-
|
|
55543
|
+
await this.handshake();
|
|
55544
|
+
}
|
|
55545
|
+
async handshake() {
|
|
55546
|
+
const prev2 = this.recovering;
|
|
55547
|
+
this.recovering = true;
|
|
55548
|
+
try {
|
|
55549
|
+
await this.request(
|
|
55550
|
+
"initialize",
|
|
55551
|
+
{
|
|
55552
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
55553
|
+
capabilities: {},
|
|
55554
|
+
clientInfo: { name: "zelari-code", version: getCurrentVersion() }
|
|
55555
|
+
},
|
|
55556
|
+
INIT_TIMEOUT_MS
|
|
55557
|
+
);
|
|
55558
|
+
this.notify("notifications/initialized", {});
|
|
55559
|
+
} finally {
|
|
55560
|
+
this.recovering = prev2;
|
|
55561
|
+
}
|
|
55246
55562
|
}
|
|
55247
|
-
/**
|
|
55563
|
+
/**
|
|
55564
|
+
* Discover the server's tools. Follows `nextCursor` pagination so
|
|
55565
|
+
* large servers (hundreds of tools) are listed completely.
|
|
55566
|
+
*/
|
|
55248
55567
|
async listTools() {
|
|
55249
|
-
const
|
|
55250
|
-
|
|
55251
|
-
|
|
55252
|
-
|
|
55253
|
-
|
|
55254
|
-
|
|
55255
|
-
|
|
55256
|
-
|
|
55568
|
+
const tools = [];
|
|
55569
|
+
let cursor;
|
|
55570
|
+
for (let page = 0; page < MAX_LIST_PAGES; page++) {
|
|
55571
|
+
const res = await this.request(
|
|
55572
|
+
"tools/list",
|
|
55573
|
+
cursor ? { cursor } : {}
|
|
55574
|
+
);
|
|
55575
|
+
for (const t of res.tools ?? []) {
|
|
55576
|
+
if (!t.name) continue;
|
|
55577
|
+
tools.push({
|
|
55578
|
+
name: t.name,
|
|
55579
|
+
description: t.description ?? "",
|
|
55580
|
+
inputSchema: t.inputSchema ?? { type: "object", properties: {} }
|
|
55581
|
+
});
|
|
55582
|
+
}
|
|
55583
|
+
cursor = typeof res.nextCursor === "string" ? res.nextCursor : void 0;
|
|
55584
|
+
if (!cursor) break;
|
|
55585
|
+
}
|
|
55586
|
+
return tools;
|
|
55257
55587
|
}
|
|
55258
55588
|
/**
|
|
55259
55589
|
* Call a tool. Returns the concatenated text content; non-text content
|
|
55260
55590
|
* items are summarized by type. Throws when the server flags isError.
|
|
55261
55591
|
*/
|
|
55262
|
-
async callTool(name, args, timeoutMs2
|
|
55592
|
+
async callTool(name, args, timeoutMs2) {
|
|
55263
55593
|
const res = await this.request(
|
|
55264
55594
|
"tools/call",
|
|
55265
55595
|
{ name, arguments: args },
|
|
55266
|
-
timeoutMs2
|
|
55596
|
+
timeoutMs2 ?? this.defaultTimeoutMs
|
|
55267
55597
|
);
|
|
55268
55598
|
const text = (res.content ?? []).map(
|
|
55269
55599
|
(c) => c.type === "text" && typeof c.text === "string" ? c.text : `[${c.type ?? "unknown"} content]`
|
|
@@ -55272,20 +55602,39 @@ var init_mcpClient = __esm({
|
|
|
55272
55602
|
throw new Error(text || `tool "${name}" reported an error`);
|
|
55273
55603
|
return text;
|
|
55274
55604
|
}
|
|
55275
|
-
/** Terminate the server
|
|
55605
|
+
/** Terminate the server / session and reject all in-flight requests. */
|
|
55276
55606
|
close() {
|
|
55277
55607
|
this.closed = true;
|
|
55278
55608
|
this.failAll(new Error(`[mcp:${this.serverName}] client closed`));
|
|
55609
|
+
if (this.transport) {
|
|
55610
|
+
this.transport.close();
|
|
55611
|
+
this.transport = null;
|
|
55612
|
+
return;
|
|
55613
|
+
}
|
|
55279
55614
|
this.child?.kill();
|
|
55280
55615
|
this.child = null;
|
|
55281
55616
|
}
|
|
55282
|
-
// ── JSON-RPC plumbing
|
|
55283
|
-
request(method, params, timeoutMs2
|
|
55617
|
+
// ── JSON-RPC plumbing (shared by both transports) ────────────────────
|
|
55618
|
+
request(method, params, timeoutMs2) {
|
|
55619
|
+
const effective = timeoutMs2 ?? this.defaultTimeoutMs;
|
|
55620
|
+
if (!this.serial || this.recovering)
|
|
55621
|
+
return this.dispatch(method, params, effective);
|
|
55622
|
+
const run = this.queueTail.then(
|
|
55623
|
+
() => this.dispatch(method, params, effective),
|
|
55624
|
+
() => this.dispatch(method, params, effective)
|
|
55625
|
+
);
|
|
55626
|
+
this.queueTail = run.then(
|
|
55627
|
+
() => void 0,
|
|
55628
|
+
() => void 0
|
|
55629
|
+
);
|
|
55630
|
+
return run;
|
|
55631
|
+
}
|
|
55632
|
+
dispatch(method, params, timeoutMs2) {
|
|
55633
|
+
const transport = this.transport;
|
|
55284
55634
|
const child = this.child;
|
|
55285
|
-
if (!child)
|
|
55635
|
+
if (!transport && !child)
|
|
55286
55636
|
return Promise.reject(new Error(`[mcp:${this.serverName}] not started`));
|
|
55287
55637
|
const id3 = this.nextId++;
|
|
55288
|
-
const payload = JSON.stringify({ jsonrpc: "2.0", id: id3, method, params });
|
|
55289
55638
|
return new Promise((resolve9, reject) => {
|
|
55290
55639
|
const timer = setTimeout(() => {
|
|
55291
55640
|
this.pending.delete(id3);
|
|
@@ -55296,16 +55645,25 @@ var init_mcpClient = __esm({
|
|
|
55296
55645
|
);
|
|
55297
55646
|
}, timeoutMs2);
|
|
55298
55647
|
this.pending.set(id3, { resolve: resolve9, reject, timer });
|
|
55299
|
-
|
|
55300
|
-
|
|
55301
|
-
|
|
55302
|
-
|
|
55303
|
-
|
|
55304
|
-
|
|
55305
|
-
|
|
55648
|
+
if (transport) {
|
|
55649
|
+
void transport.send({ id: id3, method, params }, timeoutMs2);
|
|
55650
|
+
} else {
|
|
55651
|
+
const payload = JSON.stringify({ jsonrpc: "2.0", id: id3, method, params });
|
|
55652
|
+
child.stdin.write(payload + "\n", (err) => {
|
|
55653
|
+
if (err) {
|
|
55654
|
+
clearTimeout(timer);
|
|
55655
|
+
this.pending.delete(id3);
|
|
55656
|
+
reject(err);
|
|
55657
|
+
}
|
|
55658
|
+
});
|
|
55659
|
+
}
|
|
55306
55660
|
});
|
|
55307
55661
|
}
|
|
55308
55662
|
notify(method, params) {
|
|
55663
|
+
if (this.transport) {
|
|
55664
|
+
void this.transport.send({ method, params }, this.defaultTimeoutMs);
|
|
55665
|
+
return;
|
|
55666
|
+
}
|
|
55309
55667
|
this.child?.stdin.write(
|
|
55310
55668
|
JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n"
|
|
55311
55669
|
);
|
|
@@ -55317,26 +55675,29 @@ var init_mcpClient = __esm({
|
|
|
55317
55675
|
const line = this.stdoutBuffer.slice(0, nl).trim();
|
|
55318
55676
|
this.stdoutBuffer = this.stdoutBuffer.slice(nl + 1);
|
|
55319
55677
|
if (!line) continue;
|
|
55320
|
-
let msg;
|
|
55321
55678
|
try {
|
|
55322
|
-
|
|
55679
|
+
this.handleMessage(JSON.parse(line));
|
|
55323
55680
|
} catch {
|
|
55324
55681
|
continue;
|
|
55325
55682
|
}
|
|
55326
|
-
|
|
55327
|
-
|
|
55328
|
-
|
|
55329
|
-
|
|
55330
|
-
|
|
55331
|
-
|
|
55332
|
-
|
|
55333
|
-
|
|
55334
|
-
|
|
55335
|
-
|
|
55336
|
-
|
|
55337
|
-
|
|
55338
|
-
|
|
55339
|
-
|
|
55683
|
+
}
|
|
55684
|
+
}
|
|
55685
|
+
/** Route one parsed JSON-RPC message to its pending request (both transports). */
|
|
55686
|
+
handleMessage(msg) {
|
|
55687
|
+
const m = msg;
|
|
55688
|
+
if (typeof m.id !== "number") return;
|
|
55689
|
+
const pending = this.pending.get(m.id);
|
|
55690
|
+
if (!pending) return;
|
|
55691
|
+
this.pending.delete(m.id);
|
|
55692
|
+
clearTimeout(pending.timer);
|
|
55693
|
+
if (m.error) {
|
|
55694
|
+
pending.reject(
|
|
55695
|
+
new Error(
|
|
55696
|
+
`[mcp:${this.serverName}] ${m.error.message ?? "JSON-RPC error"} (code ${m.error.code ?? "?"})`
|
|
55697
|
+
)
|
|
55698
|
+
);
|
|
55699
|
+
} else {
|
|
55700
|
+
pending.resolve(m.result);
|
|
55340
55701
|
}
|
|
55341
55702
|
}
|
|
55342
55703
|
failAll(err) {
|
|
@@ -55371,11 +55732,17 @@ function readFile6(path91) {
|
|
|
55371
55732
|
const parsed = JSON.parse(readFileSync29(path91, "utf8"));
|
|
55372
55733
|
const out = {};
|
|
55373
55734
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
55374
|
-
|
|
55735
|
+
const hasCommand = !!cfg && typeof cfg.command === "string" && !!cfg.command.trim();
|
|
55736
|
+
const hasUrl = !!cfg && typeof cfg.url === "string" && /^https?:\/\//i.test(cfg.url);
|
|
55737
|
+
if (!cfg || !hasCommand && !hasUrl) continue;
|
|
55375
55738
|
out[name] = {
|
|
55376
|
-
command: cfg.command.trim(),
|
|
55739
|
+
command: hasCommand ? cfg.command.trim() : void 0,
|
|
55377
55740
|
args: Array.isArray(cfg.args) ? cfg.args.map(String) : void 0,
|
|
55378
55741
|
env: cfg.env && typeof cfg.env === "object" ? cfg.env : void 0,
|
|
55742
|
+
type: hasUrl && !hasCommand ? "http" : cfg.type === "http" ? "http" : "stdio",
|
|
55743
|
+
url: hasUrl ? cfg.url.trim() : void 0,
|
|
55744
|
+
timeoutMs: typeof cfg.timeoutMs === "number" && cfg.timeoutMs > 0 ? cfg.timeoutMs : void 0,
|
|
55745
|
+
serial: typeof cfg.serial === "boolean" ? cfg.serial : void 0,
|
|
55379
55746
|
enabled: cfg.enabled !== false
|
|
55380
55747
|
};
|
|
55381
55748
|
}
|
|
@@ -55417,8 +55784,13 @@ function upsertMcpServer(opts) {
|
|
|
55417
55784
|
error: "Invalid server name (use letters, digits, _ -)"
|
|
55418
55785
|
};
|
|
55419
55786
|
}
|
|
55420
|
-
|
|
55421
|
-
|
|
55787
|
+
const hasCommand = !!opts.config.command?.trim();
|
|
55788
|
+
const hasUrl = typeof opts.config.url === "string" && /^https?:\/\//i.test(opts.config.url);
|
|
55789
|
+
if (!hasCommand && !hasUrl) {
|
|
55790
|
+
return {
|
|
55791
|
+
ok: false,
|
|
55792
|
+
error: "either command (stdio) or url (http) is required"
|
|
55793
|
+
};
|
|
55422
55794
|
}
|
|
55423
55795
|
let path91;
|
|
55424
55796
|
if (opts.scope === "user") {
|
|
@@ -55435,9 +55807,13 @@ function upsertMcpServer(opts) {
|
|
|
55435
55807
|
}
|
|
55436
55808
|
const current = readFile6(path91);
|
|
55437
55809
|
current[name] = {
|
|
55438
|
-
command: opts.config.command.trim(),
|
|
55810
|
+
command: hasCommand ? opts.config.command.trim() : void 0,
|
|
55439
55811
|
args: opts.config.args,
|
|
55440
55812
|
env: opts.config.env,
|
|
55813
|
+
type: hasUrl ? "http" : opts.config.type === "http" ? "http" : "stdio",
|
|
55814
|
+
url: hasUrl ? opts.config.url.trim() : void 0,
|
|
55815
|
+
timeoutMs: opts.config.timeoutMs,
|
|
55816
|
+
serial: opts.config.serial,
|
|
55441
55817
|
enabled: opts.config.enabled !== false
|
|
55442
55818
|
};
|
|
55443
55819
|
writeFile2(path91, current);
|
|
@@ -55512,9 +55888,35 @@ function buildQwenMmPreset() {
|
|
|
55512
55888
|
]
|
|
55513
55889
|
};
|
|
55514
55890
|
}
|
|
55891
|
+
function buildUnrealPreset() {
|
|
55892
|
+
const url2 = process.env.UNREAL_MCP_URL?.trim() || "http://127.0.0.1:8000/mcp";
|
|
55893
|
+
return {
|
|
55894
|
+
id: "unreal-mcp",
|
|
55895
|
+
servers: {
|
|
55896
|
+
"unreal-mcp": {
|
|
55897
|
+
type: "http",
|
|
55898
|
+
url: url2,
|
|
55899
|
+
// Editor tool calls (asset scans, builds, PIE) can be slow.
|
|
55900
|
+
timeoutMs: 12e4,
|
|
55901
|
+
// Epic guidance: never overlap calls on the editor's game thread.
|
|
55902
|
+
serial: true,
|
|
55903
|
+
enabled: true
|
|
55904
|
+
}
|
|
55905
|
+
},
|
|
55906
|
+
notes: [
|
|
55907
|
+
"Unreal Engine 5.8+ \u2014 MCP server embedded in the editor (Experimental feature).",
|
|
55908
|
+
"Editor: enable the 'Model Context Protocol' plugin, then Edit \u2192 Project Settings \u2192 Plugins \u2192 MCP Server.",
|
|
55909
|
+
`Endpoint: ${url2} (override with UNREAL_MCP_URL; port/path configurable in the editor).`,
|
|
55910
|
+
"Tool Search ON by default: tools surface as list_toolsets / describe_toolset / call_tool.",
|
|
55911
|
+
"Requests are serialized and time out after 120s (editor tools can be slow).",
|
|
55912
|
+
"Start the editor before OR after zelari \u2014 unreachable servers are retried on each turn.",
|
|
55913
|
+
"Kill switch: disable the unreal-mcp server in mcp.json or ZELARI_MCP=0"
|
|
55914
|
+
]
|
|
55915
|
+
};
|
|
55916
|
+
}
|
|
55515
55917
|
function listMcpPresetIds() {
|
|
55516
55918
|
return Object.keys(PRESETS).filter(
|
|
55517
|
-
(k) => k === "cua" || k === "composio" || k === "qwen-mm-plugins"
|
|
55919
|
+
(k) => k === "cua" || k === "composio" || k === "qwen-mm-plugins" || k === "unreal-mcp"
|
|
55518
55920
|
);
|
|
55519
55921
|
}
|
|
55520
55922
|
function getMcpPreset(id3) {
|
|
@@ -55583,7 +55985,10 @@ var init_mcpPresets = __esm({
|
|
|
55583
55985
|
"cua-driver": () => CUA_DRIVER_PRESET,
|
|
55584
55986
|
composio: buildComposioPreset,
|
|
55585
55987
|
"qwen-mm-plugins": buildQwenMmPreset,
|
|
55586
|
-
"qwen-mm": buildQwenMmPreset
|
|
55988
|
+
"qwen-mm": buildQwenMmPreset,
|
|
55989
|
+
unreal: buildUnrealPreset,
|
|
55990
|
+
"unreal-mcp": buildUnrealPreset,
|
|
55991
|
+
unrealEditor: buildUnrealPreset
|
|
55587
55992
|
};
|
|
55588
55993
|
}
|
|
55589
55994
|
});
|
|
@@ -55613,7 +56018,9 @@ function readMcpConfig(projectRoot = process.cwd(), opts) {
|
|
|
55613
56018
|
try {
|
|
55614
56019
|
const parsed = JSON.parse(readFileSync30(p3, "utf8"));
|
|
55615
56020
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
55616
|
-
|
|
56021
|
+
const hasCommand = !!cfg && typeof cfg.command === "string" && cfg.command.length > 0;
|
|
56022
|
+
const hasUrl = !!cfg && typeof cfg.url === "string" && /^https?:\/\//i.test(cfg.url);
|
|
56023
|
+
if (!cfg || !hasCommand && !hasUrl) continue;
|
|
55617
56024
|
merged[name] = cfg;
|
|
55618
56025
|
}
|
|
55619
56026
|
} catch {
|
|
@@ -55654,16 +56061,52 @@ async function ensureLoaded(projectRoot) {
|
|
|
55654
56061
|
}
|
|
55655
56062
|
} catch (err) {
|
|
55656
56063
|
client.close();
|
|
55657
|
-
|
|
56064
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
56065
|
+
if (isHttpConfig(cfg)) {
|
|
56066
|
+
state2.pendingHttp.push({ name, cfg });
|
|
56067
|
+
state2.warnings.push(
|
|
56068
|
+
`[mcp:${name}] not reachable yet (${msg}) \u2014 will retry each turn`
|
|
56069
|
+
);
|
|
56070
|
+
} else {
|
|
56071
|
+
state2.warnings.push(`[mcp:${name}] disabled: ${msg}`);
|
|
56072
|
+
}
|
|
55658
56073
|
}
|
|
55659
56074
|
}
|
|
55660
56075
|
}
|
|
56076
|
+
function isHttpConfig(cfg) {
|
|
56077
|
+
return cfg.type === "http" || !!cfg.url && !cfg.command;
|
|
56078
|
+
}
|
|
56079
|
+
async function retryPendingHttp() {
|
|
56080
|
+
if (state2.pendingHttp.length === 0) return;
|
|
56081
|
+
const remaining = [];
|
|
56082
|
+
for (const p3 of state2.pendingHttp) {
|
|
56083
|
+
const client = new McpClient(p3.name, p3.cfg);
|
|
56084
|
+
try {
|
|
56085
|
+
await client.start();
|
|
56086
|
+
const tools = await client.listTools();
|
|
56087
|
+
state2.clients.push(client);
|
|
56088
|
+
for (const info of tools) {
|
|
56089
|
+
state2.tools.push({
|
|
56090
|
+
registryName: sanitizeToolName(`mcp_${p3.name}_${info.name}`),
|
|
56091
|
+
serverName: p3.name,
|
|
56092
|
+
info,
|
|
56093
|
+
client
|
|
56094
|
+
});
|
|
56095
|
+
}
|
|
56096
|
+
} catch {
|
|
56097
|
+
client.close();
|
|
56098
|
+
remaining.push(p3);
|
|
56099
|
+
}
|
|
56100
|
+
}
|
|
56101
|
+
state2.pendingHttp = remaining;
|
|
56102
|
+
}
|
|
55661
56103
|
function sanitizeToolName(raw) {
|
|
55662
56104
|
return raw.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
|
|
55663
56105
|
}
|
|
55664
56106
|
async function registerMcpTools(registry4, projectRoot = process.cwd(), opts) {
|
|
55665
56107
|
if (process.env["ZELARI_MCP"] === "0") return { registered: [], warnings: [] };
|
|
55666
56108
|
await ensureLoaded(projectRoot);
|
|
56109
|
+
await retryPendingHttp();
|
|
55667
56110
|
const skipCuaForCouncil = opts?.councilMode === true && !isCuaAllowedForCouncil();
|
|
55668
56111
|
const registered = [];
|
|
55669
56112
|
for (const t of state2.tools) {
|
|
@@ -55701,6 +56144,7 @@ function closeMcpClients() {
|
|
|
55701
56144
|
state2.tools = [];
|
|
55702
56145
|
state2.loaded = false;
|
|
55703
56146
|
state2.warnings = [];
|
|
56147
|
+
state2.pendingHttp = [];
|
|
55704
56148
|
}
|
|
55705
56149
|
function _resetMcpForTests() {
|
|
55706
56150
|
closeMcpClients();
|
|
@@ -55714,7 +56158,7 @@ var init_mcpManager = __esm({
|
|
|
55714
56158
|
init_mcpClient();
|
|
55715
56159
|
init_mcpPresets();
|
|
55716
56160
|
init_folderTrust();
|
|
55717
|
-
state2 = { loaded: false, tools: [], warnings: [], clients: [] };
|
|
56161
|
+
state2 = { loaded: false, tools: [], warnings: [], clients: [], pendingHttp: [] };
|
|
55718
56162
|
}
|
|
55719
56163
|
});
|
|
55720
56164
|
|
|
@@ -66217,11 +66661,14 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
|
|
|
66217
66661
|
const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
|
|
66218
66662
|
const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
|
|
66219
66663
|
const durableState = await loadDurableContext2(cwd);
|
|
66664
|
+
const { resolveRetrievalPolicy: resolveRetrievalPolicy2 } = await Promise.resolve().then(() => (init_retrievalPolicy(), retrievalPolicy_exports));
|
|
66665
|
+
const retrieval = resolveRetrievalPolicy2(councilContext.budget.occupancy);
|
|
66220
66666
|
const memoryContext = nativeMemory ? (await nativeMemory.buildContext({
|
|
66221
66667
|
text: effectiveTask,
|
|
66222
66668
|
useGraph: true,
|
|
66223
|
-
maxChars:
|
|
66224
|
-
maxMemories:
|
|
66669
|
+
maxChars: retrieval.maxChars,
|
|
66670
|
+
maxMemories: retrieval.maxMemories,
|
|
66671
|
+
...retrieval.weights ? { weights: retrieval.weights } : {}
|
|
66225
66672
|
})).text : "";
|
|
66226
66673
|
const composed = composeProjectContext2({
|
|
66227
66674
|
mode: "council",
|
|
@@ -67412,6 +67859,7 @@ var init_skillConfigIo = __esm({
|
|
|
67412
67859
|
"@zelari/core/skills/builtin/planning",
|
|
67413
67860
|
"@zelari/core/skills/builtin/refactoring",
|
|
67414
67861
|
"@zelari/core/skills/builtin/review",
|
|
67862
|
+
"@zelari/core/skills/builtin/unrealEditor",
|
|
67415
67863
|
"@zelari/core/skills/builtin/testing",
|
|
67416
67864
|
"@zelari/core/skills/builtin/schema-loop",
|
|
67417
67865
|
"@zelari/core/skills/builtin/computer-use-cua",
|
|
@@ -73439,21 +73887,27 @@ function createPermissionAskHandler(opts) {
|
|
|
73439
73887
|
const catLabel = cats.length === 1 ? cats[0] : cats.join("+") || "action";
|
|
73440
73888
|
const title = `Allow tool "${req.toolName}"?`;
|
|
73441
73889
|
const detail = req.reason + (cats.length ? ` [${cats.join(", ")}]` : "");
|
|
73890
|
+
const claimLines = (req.claims ?? []).map((c) => `\xB7 ${c.summary}`);
|
|
73891
|
+
const note = req.policyNote ? `
|
|
73892
|
+
${req.policyNote}` : "";
|
|
73893
|
+
const claimsBlock = claimLines.length ? `
|
|
73894
|
+
Claims:
|
|
73895
|
+
${claimLines.join("\n")}` : "";
|
|
73442
73896
|
appendSystem2?.(
|
|
73443
73897
|
`[permission] ${title}
|
|
73444
|
-
${detail}
|
|
73898
|
+
${detail}${note}${claimsBlock}
|
|
73445
73899
|
\u2192 Allow once \xB7 Always (tool) \xB7 Always (${catLabel}) \xB7 Deny`,
|
|
73446
73900
|
Date.now()
|
|
73447
73901
|
);
|
|
73448
73902
|
let settled = false;
|
|
73449
73903
|
const askTimeoutMs = askUserTimeoutMs();
|
|
73450
73904
|
let cancelAskTimeout = () => void 0;
|
|
73451
|
-
const finish2 = (ok,
|
|
73905
|
+
const finish2 = (ok, note2) => {
|
|
73452
73906
|
if (settled) return;
|
|
73453
73907
|
settled = true;
|
|
73454
73908
|
cancelAskTimeout();
|
|
73455
73909
|
setPicker2(null);
|
|
73456
|
-
if (
|
|
73910
|
+
if (note2) appendSystem2?.(note2, Date.now());
|
|
73457
73911
|
resolve9(ok);
|
|
73458
73912
|
};
|
|
73459
73913
|
cancelAskTimeout = armPickerTimeout(
|
|
@@ -74587,6 +75041,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
74587
75041
|
let councilMemory;
|
|
74588
75042
|
let councilMemoryAutoWrite = false;
|
|
74589
75043
|
let nativeMemoryContext = "";
|
|
75044
|
+
let councilRetrievalBand = "low";
|
|
74590
75045
|
try {
|
|
74591
75046
|
const memoryFactory = await Promise.resolve().then(() => (init_serviceFactory(), serviceFactory_exports));
|
|
74592
75047
|
if (memoryFactory.isMemoryV2Enabled()) {
|
|
@@ -74601,11 +75056,17 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
74601
75056
|
});
|
|
74602
75057
|
councilMemoryAutoWrite = memoryFactory.isMemoryAutoWriteEnabled();
|
|
74603
75058
|
if (!overrides.ragContext) {
|
|
75059
|
+
const { resolveRetrievalPolicy: resolveRetrievalPolicy2 } = await Promise.resolve().then(() => (init_retrievalPolicy(), retrievalPolicy_exports));
|
|
75060
|
+
const retrieval = resolveRetrievalPolicy2(
|
|
75061
|
+
councilContext.budget.occupancy
|
|
75062
|
+
);
|
|
75063
|
+
councilRetrievalBand = retrieval.band;
|
|
74604
75064
|
nativeMemoryContext = (await councilMemory.buildContext({
|
|
74605
75065
|
text: effectiveText,
|
|
74606
75066
|
useGraph: true,
|
|
74607
|
-
maxChars:
|
|
74608
|
-
maxMemories:
|
|
75067
|
+
maxChars: retrieval.maxChars,
|
|
75068
|
+
maxMemories: retrieval.maxMemories,
|
|
75069
|
+
...retrieval.weights ? { weights: retrieval.weights } : {}
|
|
74609
75070
|
})).text;
|
|
74610
75071
|
}
|
|
74611
75072
|
}
|
|
@@ -80285,7 +80746,7 @@ function pickRootComponent() {
|
|
|
80285
80746
|
}
|
|
80286
80747
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
80287
80748
|
console.log(
|
|
80288
|
-
"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 --inspect [--json] Unified project inspection (config, skills, MCP,\n hooks, plugins, AGENTS.md, trust status)\n --trust [path] Trust the cwd (or path) so project MCP + hooks load\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --permission-mcp <socket> MCP stdio server for external agent permission prompts\n (spawned by claude --permission-prompt-tool)\n --memory-mcp Optional MCP stdio server for project memory\n --cwd <path> Trusted project root (requires ZELARI_MEMORY_MCP=1)\n --client-id <id> Stable local owner identity for private memories\n --memory-json <json> Read-only project-memory bridge for Zelari Desktop\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 kraken|council|zelari Dispatch mode (default: kraken; agent=alias)\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 --profile <id> Capability profile (minimal/v1|kraken/v1|council/v1|mission/v1)\n --resume <id> Continue a 2.0 spine session\n --export-session <path> Write zelari-session-export/1 after the run\n --strict-done Evidence-based BUILD completion gate\n --session-export <id> Print a portable 2.0 session export (no LLM)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\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 --thinking <spec> Thinking effort (auto|off|low|medium|high|budget:N)\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --login-oauth Start subscription OAuth (grok, chatgpt, anthropic)\n --provider <id> grok | chatgpt | anthropic\n --code <paste> Anthropic magic-link code (CODE#STATE)\n --no-browser Do not open the system browser\n --refresh-oauth Force-refresh an OAuth access token\n --provider <id> grok | chatgpt | anthropic\n --logout-oauth Clear stored OAuth credentials\n --provider <id> grok | chatgpt | anthropic\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 (
|
|
80749
|
+
"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 --inspect [--json] Unified project inspection (config, skills, MCP,\n hooks, plugins, AGENTS.md, trust status)\n --trust [path] Trust the cwd (or path) so project MCP + hooks load\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --permission-mcp <socket> MCP stdio server for external agent permission prompts\n (spawned by claude --permission-prompt-tool)\n --memory-mcp Optional MCP stdio server for project memory\n --cwd <path> Trusted project root (requires ZELARI_MEMORY_MCP=1)\n --client-id <id> Stable local owner identity for private memories\n --memory-json <json> Read-only project-memory bridge for Zelari Desktop\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 kraken|council|zelari Dispatch mode (default: kraken; agent=alias)\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 --profile <id> Capability profile (minimal/v1|kraken/v1|council/v1|mission/v1)\n --resume <id> Continue a 2.0 spine session\n --export-session <path> Write zelari-session-export/1 after the run\n --strict-done Evidence-based BUILD completion gate\n --session-export <id> Print a portable 2.0 session export (no LLM)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\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 --thinking <spec> Thinking effort (auto|off|low|medium|high|budget:N)\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --login-oauth Start subscription OAuth (grok, chatgpt, anthropic)\n --provider <id> grok | chatgpt | anthropic\n --code <paste> Anthropic magic-link code (CODE#STATE)\n --no-browser Do not open the system browser\n --refresh-oauth Force-refresh an OAuth access token\n --provider <id> grok | chatgpt | anthropic\n --logout-oauth Clear stored OAuth credentials\n --provider <id> grok | chatgpt | anthropic\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 for stdio servers\n --url <endpoint> Streamable HTTP endpoint (e.g. UE 5.8 editor)\n --args <json> JSON array of args (stdio, optional)\n --timeout <ms> Per-server request timeout (http, 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 --preset unreal-mcp Unreal Engine 5.8+ editor (MCP over HTTP)\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-skills Print skills (builtin + user + project)\n --cwd <path> Project root for .zelari/skills\n --set-skill Add/update a SKILL.md skill\n --name <id> Skill id (required)\n --description <t> One-line description (required)\n --body <text> Markdown instructions (required)\n --category <c> plan|refactor|debug|review|test|docs|ops|git|db|maint\n --tools <csv> Comma-separated required tools (optional)\n --cost <l> low|medium|high (default: medium)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-skill Remove a user/project SKILL.md\n --name <id> --scope user|project [--cwd <path>]\n --generate-skill-from-url Fetch URL + draft skill via model (JSON)\n --url <https://...> Required\n --provider <id> Override active provider\n --model <name> Override model for the selected provider\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 ZELARI_MEMORY_MCP=1 Enable the optional external memory MCP server\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
80289
80750
|
);
|
|
80290
80751
|
process.exit(0);
|
|
80291
80752
|
}
|
|
@@ -80311,6 +80772,8 @@ function pickRootComponent() {
|
|
|
80311
80772
|
};
|
|
80312
80773
|
const name = get("--name");
|
|
80313
80774
|
const command = get("--command");
|
|
80775
|
+
const url2 = get("--url");
|
|
80776
|
+
const timeoutRaw = get("--timeout");
|
|
80314
80777
|
const scopeRaw = get("--scope") ?? "user";
|
|
80315
80778
|
const scope = scopeRaw === "project" ? "project" : "user";
|
|
80316
80779
|
const cwd = get("--cwd") ?? process.cwd();
|
|
@@ -80323,14 +80786,22 @@ function pickRootComponent() {
|
|
|
80323
80786
|
if (!Array.isArray(parsed)) throw new Error("--args must be a JSON array");
|
|
80324
80787
|
args = parsed.map(String);
|
|
80325
80788
|
}
|
|
80326
|
-
|
|
80327
|
-
|
|
80789
|
+
const timeoutMs2 = timeoutRaw !== void 0 ? Number(timeoutRaw) : void 0;
|
|
80790
|
+
if (timeoutMs2 !== void 0 && (!Number.isFinite(timeoutMs2) || timeoutMs2 <= 0)) {
|
|
80791
|
+
throw new Error("--timeout must be a positive number of milliseconds");
|
|
80792
|
+
}
|
|
80793
|
+
if (!name) throw new Error("--name is required");
|
|
80794
|
+
if (!command && !url2) {
|
|
80795
|
+
throw new Error("either --command (stdio) or --url (http) is required");
|
|
80796
|
+
}
|
|
80797
|
+
if (url2 && !/^https?:\/\//i.test(url2)) {
|
|
80798
|
+
throw new Error("--url must be an http(s) endpoint");
|
|
80328
80799
|
}
|
|
80329
80800
|
const result = upsertMcpServer({
|
|
80330
80801
|
scope,
|
|
80331
80802
|
name,
|
|
80332
80803
|
projectRoot: cwd,
|
|
80333
|
-
config: { command, args, enabled }
|
|
80804
|
+
config: url2 ? { type: "http", url: url2, timeoutMs: timeoutMs2, serial: true, enabled } : { command, args, enabled }
|
|
80334
80805
|
});
|
|
80335
80806
|
if (!result.ok) throw new Error(result.error);
|
|
80336
80807
|
console.log(JSON.stringify({ ok: true, path: result.path, name, scope }));
|