zelari-code 2.18.1 → 2.20.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.
Files changed (37) hide show
  1. package/dist/cli/budget/contextProjection.js +52 -0
  2. package/dist/cli/budget/contextProjection.js.map +1 -0
  3. package/dist/cli/budget/modelContextBuilder.js +9 -0
  4. package/dist/cli/budget/modelContextBuilder.js.map +1 -1
  5. package/dist/cli/budget/retrievalPolicy.js +82 -0
  6. package/dist/cli/budget/retrievalPolicy.js.map +1 -0
  7. package/dist/cli/commands/inspectSession.js +12 -2
  8. package/dist/cli/commands/inspectSession.js.map +1 -1
  9. package/dist/cli/harnessState.js +17 -5
  10. package/dist/cli/harnessState.js.map +1 -1
  11. package/dist/cli/headless/runOneTurn.js +7 -2
  12. package/dist/cli/headless/runOneTurn.js.map +1 -1
  13. package/dist/cli/hooks/permissionPicker.js +9 -1
  14. package/dist/cli/hooks/permissionPicker.js.map +1 -1
  15. package/dist/cli/hooks/useChatTurn.js +10 -2
  16. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  17. package/dist/cli/main.bundled.js +1141 -902
  18. package/dist/cli/main.bundled.js.map +4 -4
  19. package/dist/cli/memory/spineTelemetry.js +38 -5
  20. package/dist/cli/memory/spineTelemetry.js.map +1 -1
  21. package/dist/cli/provider/capabilities.js +3 -0
  22. package/dist/cli/provider/capabilities.js.map +1 -1
  23. package/dist/cli/provider/openai-compatible.js +27 -1
  24. package/dist/cli/provider/openai-compatible.js.map +1 -1
  25. package/dist/cli/runHeadless.js +16 -4
  26. package/dist/cli/runHeadless.js.map +1 -1
  27. package/dist/cli/safety/resourceClaims.js +33 -2
  28. package/dist/cli/safety/resourceClaims.js.map +1 -1
  29. package/dist/cli/safety/toolPermissions.js +11 -4
  30. package/dist/cli/safety/toolPermissions.js.map +1 -1
  31. package/dist/cli/slashHandlers/krakenGraph.js +4 -1
  32. package/dist/cli/slashHandlers/krakenGraph.js.map +1 -1
  33. package/dist/cli/toolRegistry.js +16 -2
  34. package/dist/cli/toolRegistry.js.map +1 -1
  35. package/dist/cli/tools/skillTool.js +20 -6
  36. package/dist/cli/tools/skillTool.js.map +1 -1
  37. package/package.json +2 -2
@@ -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
- ...raw,
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 skills = listCodingSkills();
40731
- if (skills.length === 0) {
41579
+ const band = opts?.pressureBand;
41580
+ const all = listCodingSkills();
41581
+ if (all.length === 0) {
40732
41582
  return "No skills registered.";
40733
41583
  }
40734
- const lines = skills.slice(0, 80).map((s) => {
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, "allow"),
46390
- network: parseAction(process.env.ZELARI_PERMISSION_NETWORK, "allow"),
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(
@@ -50136,15 +50911,39 @@ function spineMemoryEventNote(handle, event) {
50136
50911
  } catch {
50137
50912
  }
50138
50913
  }
50914
+ function bufferOf(holder) {
50915
+ let buf = buffers.get(holder);
50916
+ if (!buf) {
50917
+ buf = [];
50918
+ buffers.set(holder, buf);
50919
+ }
50920
+ return buf;
50921
+ }
50139
50922
  function memorySinkFor(holder) {
50140
50923
  return (event) => {
50141
50924
  const handle = holder.current;
50142
- if (handle) spineMemoryEventNote(handle, event);
50925
+ if (handle) {
50926
+ spineMemoryEventNote(handle, event);
50927
+ return;
50928
+ }
50929
+ const buf = bufferOf(holder);
50930
+ if (buf.length < PRE_BIND_BUFFER_CAP) buf.push(event);
50931
+ else holder.droppedEvents = (holder.droppedEvents ?? 0) + 1;
50143
50932
  };
50144
50933
  }
50934
+ function flushMemorySpineNotes(holder) {
50935
+ const handle = holder.current;
50936
+ const buf = buffers.get(holder);
50937
+ if (!handle || !buf?.length) return;
50938
+ const pending = buf.splice(0, buf.length);
50939
+ for (const event of pending) spineMemoryEventNote(handle, event);
50940
+ }
50941
+ var PRE_BIND_BUFFER_CAP, buffers;
50145
50942
  var init_spineTelemetry = __esm({
50146
50943
  "src/cli/memory/spineTelemetry.ts"() {
50147
50944
  "use strict";
50945
+ PRE_BIND_BUFFER_CAP = 32;
50946
+ buffers = /* @__PURE__ */ new WeakMap();
50148
50947
  }
50149
50948
  });
50150
50949
 
@@ -50356,567 +51155,123 @@ var init_fileStateStore = __esm({
50356
51155
  mode: input.mode,
50357
51156
  label: input.label,
50358
51157
  verification: input.verification,
50359
- changedPaths: input.changedPaths ?? [],
50360
- discoveryCount: input.discoveries?.length ?? 0
50361
- };
50362
- }
50363
- async head() {
50364
- return null;
50365
- }
50366
- async get() {
50367
- return null;
50368
- }
50369
- async list() {
50370
- return [];
50371
- }
50372
- async setHead(id3) {
50373
- throw new Error(`NoopDurableStateStore.setHead: state disabled (id=${id3})`);
50374
- }
50375
- async loadDiscoveries() {
50376
- return [];
50377
- }
50378
- async materializeContext() {
50379
- return "";
50380
- }
50381
- async close() {
50382
- }
50383
- };
50384
- }
50385
- });
50386
-
50387
- // src/cli/hooks/messageHelpers.ts
50388
- function appendSystem(setMessages, content, ts = Date.now()) {
50389
- setMessages((prev2) => [
50390
- ...prev2,
50391
- { id: crypto.randomUUID(), role: "system", content, ts }
50392
- ]);
50393
- }
50394
- function appendUser(setMessages, content, ts = Date.now()) {
50395
- setMessages((prev2) => [
50396
- ...prev2,
50397
- { id: crypto.randomUUID(), role: "user", content, ts }
50398
- ]);
50399
- }
50400
- function appendOrExtendStreamingAssistant(setMessages, fullContent, ts, memberContext) {
50401
- setMessages((prev2) => {
50402
- const last = prev2[prev2.length - 1];
50403
- if (last && last.role === "assistant" && last.id.startsWith("streaming-")) {
50404
- return [...prev2.slice(0, -1), { ...last, content: fullContent }];
50405
- }
50406
- return [
50407
- ...prev2,
50408
- {
50409
- id: `streaming-${crypto.randomUUID()}`,
50410
- role: "assistant",
50411
- content: fullContent,
50412
- ts,
50413
- ...memberContext?.memberId ? { memberId: memberContext.memberId } : {},
50414
- ...memberContext?.memberName ? { memberName: memberContext.memberName } : {}
50415
- }
50416
- ];
50417
- });
50418
- }
50419
- function appendToolStart(setMessages, toolName, toolCallId, args, ts) {
50420
- const argsPreview = formatToolSummary(toolName, args);
50421
- setMessages((prev2) => [
50422
- ...prev2,
50423
- {
50424
- id: crypto.randomUUID(),
50425
- role: "tool",
50426
- content: argsPreview,
50427
- ts,
50428
- toolName,
50429
- toolCallId,
50430
- toolOk: void 0,
50431
- toolDurationMs: void 0
50432
- }
50433
- ]);
50434
- }
50435
- function updateToolMessageEnd(setMessages, toolCallId, isError, durationMs, result) {
50436
- setMessages((prev2) => {
50437
- for (let i = prev2.length - 1; i >= 0; i--) {
50438
- const m = prev2[i];
50439
- if (m && m.role === "tool" && m.toolCallId === toolCallId && m.toolDurationMs === void 0) {
50440
- const updated = [...prev2];
50441
- updated[i] = {
50442
- ...m,
50443
- toolOk: !isError,
50444
- toolDurationMs: durationMs,
50445
- ...result !== void 0 ? {
50446
- toolResult: toolResultForStorage(
50447
- m.toolName ?? "",
50448
- result,
50449
- isError
50450
- )
50451
- } : {}
50452
- };
50453
- return updated;
50454
- }
50455
- }
50456
- return prev2;
50457
- });
50458
- }
50459
- function finalizeStreamingAssistant(setMessages) {
50460
- setMessages((prev2) => {
50461
- const last = prev2[prev2.length - 1];
50462
- if (last && last.role === "assistant" && last.id.startsWith("streaming-")) {
50463
- return [
50464
- ...prev2.slice(0, -1),
50465
- { ...last, id: last.id.slice("streaming-".length) }
50466
- ];
50467
- }
50468
- return prev2;
50469
- });
50470
- }
50471
- var init_messageHelpers = __esm({
50472
- "src/cli/hooks/messageHelpers.ts"() {
50473
- "use strict";
50474
- init_toolFormat();
50475
- init_toolFormat();
50476
- }
50477
- });
50478
-
50479
- // src/cli/budget/historySummary.ts
50480
- function extractiveHistorySummary(dropped, opts) {
50481
- const maxChars = opts?.maxChars ?? MAX_SUMMARY_CHARS;
50482
- if (dropped.length === 0) return "No prior turns.";
50483
- const userGoals = [];
50484
- const assistantNotes = [];
50485
- const userConstraints = [];
50486
- const unresolved = [];
50487
- const verification = [];
50488
- const decisions = [];
50489
- const tools = /* @__PURE__ */ new Map();
50490
- const files = /* @__PURE__ */ new Set();
50491
- let toolResults = 0;
50492
- for (const m of dropped) {
50493
- if (m.role === "user" && m.content.trim()) {
50494
- const goal = oneLine(m.content, 220);
50495
- userGoals.push(goal);
50496
- if (/\b(must|never|required|only|do not|constraint|vincolo|deve|senza)\b/i.test(goal)) userConstraints.push(goal);
50497
- } else if (m.role === "assistant") {
50498
- if (m.content.trim()) {
50499
- const note = oneLine(m.content, 220);
50500
- assistantNotes.push(note);
50501
- if (/\b(fail|failed|error|unresolved|remaining|todo|blocked|gap|errore|fallit|irrisolt|manca)\b/i.test(note)) unresolved.push(note);
50502
- if (/\b(test|typecheck|build|verify|verification|passed|failed|green|red)\b/i.test(note)) verification.push(note);
50503
- if (/\b(decid|decision|chosen|choose|scelt|adopt|implement)\b/i.test(note)) decisions.push(note);
50504
- }
50505
- if (m.toolCalls) {
50506
- for (const tc of m.toolCalls) {
50507
- tools.set(tc.name, (tools.get(tc.name) ?? 0) + 1);
50508
- collectPaths3(tc.args, files);
50509
- }
50510
- }
50511
- } else if (m.role === "tool") {
50512
- toolResults += 1;
50513
- collectPathsFromText(m.content, files);
50514
- if (/\b(fail|failed|error|exception|blocked|errore|fallit)\b/i.test(m.content)) unresolved.push(oneLine(m.content, 220));
50515
- if (/\b(test|typecheck|build|verify|passed|failed|success)\b/i.test(m.content)) verification.push(oneLine(m.content, 220));
50516
- }
50517
- }
50518
- const parts = [
50519
- "[history-summary] Earlier turns were compacted to stay within the context budget.",
50520
- `Dropped ${dropped.length} message(s) (${userGoals.length} user, ${assistantNotes.length} assistant notes, ${toolResults} tool results).`
50521
- ];
50522
- if (userGoals.length) {
50523
- parts.push("## User goals / requests");
50524
- for (const g of userGoals.slice(-6)) parts.push(`- ${g}`);
50525
- }
50526
- if (userConstraints.length) {
50527
- parts.push("## User constraints (preserve exactly)");
50528
- for (const item of [...new Set(userConstraints)].slice(-8)) parts.push(`- ${item}`);
50529
- }
50530
- if (unresolved.length) {
50531
- parts.push("## Unresolved failures / pending repair");
50532
- for (const item of [...new Set(unresolved)].slice(-8)) parts.push(`- ${item}`);
50533
- }
50534
- if (verification.length) {
50535
- parts.push("## Latest verification state");
50536
- for (const item of [...new Set(verification)].slice(-6)) parts.push(`- ${item}`);
50537
- }
50538
- if (decisions.length) {
50539
- parts.push("## Recent active decisions");
50540
- for (const item of [...new Set(decisions)].slice(-6)) parts.push(`- ${item}`);
50541
- }
50542
- if (assistantNotes.length) {
50543
- parts.push("## Assistant conclusions (truncated)");
50544
- for (const a of assistantNotes.slice(-5)) parts.push(`- ${a}`);
50545
- }
50546
- if (tools.size) {
50547
- const ranked = [...tools.entries()].sort((a, b) => b[1] - a[1]).slice(0, 12).map(([n, c]) => `${n}\xD7${c}`);
50548
- parts.push(`## Tools used: ${ranked.join(", ")}`);
50549
- }
50550
- if (files.size) {
50551
- const list = [...files].slice(0, 24);
50552
- parts.push(`## Paths mentioned: ${list.join(", ")}`);
50553
- }
50554
- parts.push(
50555
- "Continue from the recent messages below; do not re-ask goals already answered above unless the user changes them."
50556
- );
50557
- let out = parts.join("\n");
50558
- if (out.length > maxChars) {
50559
- out = `${out.slice(0, maxChars - 1)}\u2026`;
50560
- }
50561
- return out;
50562
- }
50563
- function oneLine(s, max) {
50564
- const t = s.replace(/\s+/g, " ").trim();
50565
- if (t.length <= max) return t;
50566
- return `${t.slice(0, max - 1)}\u2026`;
50567
- }
50568
- function collectPaths3(args, out) {
50569
- if (!args || typeof args !== "object") return;
50570
- const obj = args;
50571
- for (const key of ["path", "file", "filepath", "filePath", "target", "cwd"]) {
50572
- const v = obj[key];
50573
- if (typeof v === "string" && v.length > 1 && v.length < 260) {
50574
- out.add(v.replace(/\\/g, "/"));
50575
- }
50576
- }
50577
- if (typeof obj.file_path === "string") out.add(String(obj.file_path));
50578
- }
50579
- function collectPathsFromText(text, out) {
50580
- const re = /(?:^|[\s"'`])((?:[\w.-]+\/)+[\w.-]+\.\w{1,8})/g;
50581
- let m;
50582
- let n = 0;
50583
- while ((m = re.exec(text)) !== null && n < 8) {
50584
- out.add(m[1]);
50585
- n += 1;
50586
- }
50587
- }
50588
- var MAX_SUMMARY_CHARS;
50589
- var init_historySummary = __esm({
50590
- "src/cli/budget/historySummary.ts"() {
50591
- "use strict";
50592
- MAX_SUMMARY_CHARS = 3500;
50593
- }
50594
- });
50595
-
50596
- // src/cli/budget/llmCompact.ts
50597
- function isLlmCompactEnabled() {
50598
- const v = process.env.ZELARI_LLM_COMPACT?.trim().toLowerCase();
50599
- if (v === "0" || v === "false" || v === "off" || v === "no") return false;
50600
- return true;
50601
- }
50602
- function compactModelOverride() {
50603
- const v = process.env.ZELARI_COMPACT_MODEL?.trim();
50604
- return v ? v : void 0;
50605
- }
50606
- async function llmSummarizeHistoryReplay(input) {
50607
- const override = input.overrideModel ?? compactModelOverride();
50608
- const model = override ?? input.model;
50609
- const cacheReuseExpected = !override;
50610
- if (!isLlmCompactEnabled()) return { summary: null, model, cacheReuseExpected };
50611
- if (input.droppedMessages.length === 0) {
50612
- return { summary: null, model, cacheReuseExpected };
50613
- }
50614
- const messages = [
50615
- ...input.systemMessages,
50616
- ...input.droppedMessages,
50617
- {
50618
- role: "user",
50619
- content: COMPACTION_INSTRUCTION
50620
- }
50621
- ];
50622
- const controller = new AbortController();
50623
- const timeout = setTimeout(() => controller.abort(), REPLAY_TIMEOUT_MS);
50624
- const onOuterAbort = () => controller.abort();
50625
- input.signal?.addEventListener("abort", onOuterAbort, { once: true });
50626
- try {
50627
- let text = "";
50628
- let emittedToolCall = false;
50629
- for await (const delta of input.providerStream({
50630
- provider: input.provider,
50631
- model,
50632
- messages,
50633
- // Tools stay advertised: dropping them would change the prefix token
50634
- // sequence and destroy cache reuse (explicit DSH decision). They are
50635
- // sorted canonically (same discipline as the live routed request and
50636
- // the snapshot fingerprints) so the replay prefix is byte-identical.
50637
- tools: [...input.tools].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
50638
- signal: controller.signal,
50639
- generation: {
50640
- purpose: "compaction",
50641
- temperature: 0.1,
50642
- maxTokens: 900
50643
- }
50644
- })) {
50645
- if (delta.kind === "text") text += delta.delta;
50646
- if (delta.kind === "tool_call") emittedToolCall = true;
50647
- }
50648
- if (emittedToolCall) return { summary: null, model, cacheReuseExpected };
50649
- if (!text.trim()) return { summary: null, model, cacheReuseExpected };
50650
- return { summary: text.trim(), model, cacheReuseExpected };
50651
- } catch {
50652
- return { summary: null, model, cacheReuseExpected };
50653
- } finally {
50654
- clearTimeout(timeout);
50655
- input.signal?.removeEventListener("abort", onOuterAbort);
50656
- }
50657
- }
50658
- var COMPACTION_INSTRUCTION, REPLAY_TIMEOUT_MS;
50659
- var init_llmCompact = __esm({
50660
- "src/cli/budget/llmCompact.ts"() {
50661
- "use strict";
50662
- COMPACTION_INSTRUCTION = `
50663
- You are now acting as a compaction engine for this coding-agent session.
50664
-
50665
- Condense the conversation ABOVE into a compact checkpoint sufficient to continue the task.
50666
-
50667
- Preserve:
50668
- - user's goal and evolving intent
50669
- - decisions already made
50670
- - exact file paths and identifiers
50671
- - code changes already completed
50672
- - commands/errors that still matter
50673
- - constraints
50674
- - unfinished work
50675
- - the single most likely next action
50676
-
50677
- Do not call tools.
50678
- Do not mention this summarization request.
50679
- Output only the checkpoint.
50680
- Be concise.
50681
- `.trim();
50682
- REPLAY_TIMEOUT_MS = 6e4;
50683
- }
50684
- });
50685
-
50686
- // src/cli/hooks/historyCompaction.ts
50687
- function compactedRangeFromDropped(dropped) {
50688
- if (dropped.length === 0) return void 0;
50689
- const seqs = [];
50690
- const sources = [];
50691
- for (const m of dropped) {
50692
- 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;
50693
- if (hasCompactRange) {
50694
- seqs.push(m.compactedFromSeq, m.compactedToSeq);
50695
- sources.push(...m.sourceEventSeqs ?? []);
50696
- if (typeof m.seq === "number" && Number.isInteger(m.seq) && m.seq > 0) {
50697
- seqs.push(m.seq);
50698
- sources.push(m.seq);
50699
- }
50700
- continue;
50701
- }
50702
- if (typeof m.seq !== "number" || !Number.isInteger(m.seq) || m.seq < 1) return void 0;
50703
- seqs.push(m.seq);
50704
- sources.push(m.seq);
50705
- }
50706
- return {
50707
- fromSeq: Math.min(...seqs),
50708
- toSeq: Math.max(...seqs),
50709
- sourceEventSeqs: [...new Set(sources)]
50710
- };
50711
- }
50712
- function withDroppedRange(result, dropped, strategy) {
50713
- const range = compactedRangeFromDropped(dropped);
50714
- if (!range) return { ...result, strategy };
50715
- return { ...result, ...range, strategy };
50716
- }
50717
- function resolveMaxMessages(opts) {
50718
- const envTurns = envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 });
50719
- let turns = opts?.maxMessages ? Math.ceil(opts.maxMessages / 4) : envTurns;
50720
- if (opts?.durableStatePresent && !opts?.maxMessages && process.env.ZELARI_HISTORY_TURNS === void 0) {
50721
- turns = Math.min(turns, 3);
50722
- }
50723
- if (turns <= 0) return 0;
50724
- if (opts?.force && opts?.maxMessages) return Math.max(1, opts.maxMessages);
50725
- return turns * 4;
50726
- }
50727
- function findValidCutIndex(messages, naiveCut) {
50728
- let cut = naiveCut;
50729
- while (cut < messages.length) {
50730
- const kept = messages.slice(cut);
50731
- const declared = /* @__PURE__ */ new Set();
50732
- for (const m of kept) {
50733
- if (m.role === "assistant" && m.toolCalls) {
50734
- for (const tc of m.toolCalls) declared.add(tc.id);
51158
+ changedPaths: input.changedPaths ?? [],
51159
+ discoveryCount: input.discoveries?.length ?? 0
51160
+ };
50735
51161
  }
50736
- }
50737
- let moved = false;
50738
- for (let k = 0; k < kept.length; k++) {
50739
- const m = kept[k];
50740
- if (m.role === "tool" && m.toolCallId && !declared.has(m.toolCallId)) {
50741
- for (let j = cut - 1; j >= 0; j--) {
50742
- const prev2 = messages[j];
50743
- if (prev2.role === "assistant" && prev2.toolCalls && prev2.toolCalls.some((tc) => tc.id === m.toolCallId)) {
50744
- cut = j;
50745
- moved = true;
50746
- break;
50747
- }
50748
- }
50749
- break;
51162
+ async head() {
51163
+ return null;
50750
51164
  }
50751
- }
50752
- if (!moved) break;
51165
+ async get() {
51166
+ return null;
51167
+ }
51168
+ async list() {
51169
+ return [];
51170
+ }
51171
+ async setHead(id3) {
51172
+ throw new Error(`NoopDurableStateStore.setHead: state disabled (id=${id3})`);
51173
+ }
51174
+ async loadDiscoveries() {
51175
+ return [];
51176
+ }
51177
+ async materializeContext() {
51178
+ return "";
51179
+ }
51180
+ async close() {
51181
+ }
51182
+ };
50753
51183
  }
50754
- return cut;
51184
+ });
51185
+
51186
+ // src/cli/hooks/messageHelpers.ts
51187
+ function appendSystem(setMessages, content, ts = Date.now()) {
51188
+ setMessages((prev2) => [
51189
+ ...prev2,
51190
+ { id: crypto.randomUUID(), role: "system", content, ts }
51191
+ ]);
50755
51192
  }
50756
- function resolvePruneLimits(opts) {
50757
- const maxChars = opts?.maxChars ?? envNumber(process.env.ZELARI_TOOL_RESULT_MAX_CHARS, { default: 8e3, min: 256 });
50758
- const rawTail = opts?.tailChars ?? envNumber(process.env.ZELARI_TOOL_RESULT_TAIL_CHARS, { default: 1e3, min: 0 });
50759
- const tailChars = Math.min(rawTail, maxChars);
50760
- return { maxChars, tailChars };
51193
+ function appendUser(setMessages, content, ts = Date.now()) {
51194
+ setMessages((prev2) => [
51195
+ ...prev2,
51196
+ { id: crypto.randomUUID(), role: "user", content, ts }
51197
+ ]);
50761
51198
  }
50762
- function pruneToolResultsDetailed(messages, opts) {
50763
- const { maxChars, tailChars } = resolvePruneLimits(opts);
50764
- const headChars = maxChars - tailChars;
50765
- const stats = { pruned: 0, charsOmitted: 0 };
50766
- let changed = false;
50767
- const out = messages.map((m) => {
50768
- if (m.role !== "tool") return m;
50769
- const body = m.content ?? "";
50770
- if (body.length <= maxChars) return m;
50771
- const head = headChars > 0 ? body.slice(0, headChars) : "";
50772
- const tail2 = tailChars > 0 ? body.slice(-tailChars) : "";
50773
- const omitted = body.length - head.length - tail2.length;
50774
- changed = true;
50775
- stats.pruned += 1;
50776
- stats.charsOmitted += omitted;
50777
- return {
50778
- ...m,
50779
- content: [head, "\u2026[pruned " + omitted + " chars]\u2026", tail2].join(String.fromCharCode(10))
50780
- };
51199
+ function appendOrExtendStreamingAssistant(setMessages, fullContent, ts, memberContext) {
51200
+ setMessages((prev2) => {
51201
+ const last = prev2[prev2.length - 1];
51202
+ if (last && last.role === "assistant" && last.id.startsWith("streaming-")) {
51203
+ return [...prev2.slice(0, -1), { ...last, content: fullContent }];
51204
+ }
51205
+ return [
51206
+ ...prev2,
51207
+ {
51208
+ id: `streaming-${crypto.randomUUID()}`,
51209
+ role: "assistant",
51210
+ content: fullContent,
51211
+ ts,
51212
+ ...memberContext?.memberId ? { memberId: memberContext.memberId } : {},
51213
+ ...memberContext?.memberName ? { memberName: memberContext.memberName } : {}
51214
+ }
51215
+ ];
50781
51216
  });
50782
- return {
50783
- messages: changed ? out : messages,
50784
- stats
50785
- };
50786
- }
50787
- function compactHistory(messages, opts) {
50788
- return compactHistoryDetailed(messages, opts).messages;
50789
- }
50790
- function buildCheckpointMessage(summaryText, range) {
50791
- return {
50792
- role: "user",
50793
- content: CHECKPOINT_WRAPPER_PREFIX + "\n\n<compacted-summary>\n" + summaryText + "\n</compacted-summary>",
50794
- ...range ? {
50795
- compactedFromSeq: range.fromSeq,
50796
- compactedToSeq: range.toSeq,
50797
- sourceEventSeqs: [...range.sourceEventSeqs]
50798
- } : {}
50799
- };
50800
51217
  }
50801
- function compactHistoryDetailed(messages, opts) {
50802
- const maxMessages = resolveMaxMessages(opts);
50803
- if (maxMessages === 0) {
50804
- return withDroppedRange(
50805
- { messages: [], compacted: true, messagesRemoved: messages.length, summary: "" },
50806
- messages,
50807
- "extractive"
50808
- );
50809
- }
50810
- if (messages.length <= maxMessages * 2 && !opts?.force) {
50811
- return {
50812
- messages,
50813
- compacted: false,
50814
- messagesRemoved: 0,
50815
- summary: ""
50816
- };
50817
- }
50818
- const naiveCut = Math.max(0, messages.length - maxMessages);
50819
- const cut = findValidCutIndex(messages, naiveCut);
50820
- if (cut === 0) {
50821
- return {
50822
- messages,
50823
- compacted: false,
50824
- messagesRemoved: 0,
50825
- summary: ""
50826
- };
50827
- }
50828
- const droppedMsgs = messages.slice(0, cut);
50829
- const droppedRange = compactedRangeFromDropped(droppedMsgs);
50830
- const pruned = pruneToolResultsDetailed(messages.slice(cut));
50831
- const kept = pruned.messages;
50832
- const summaryText = extractiveHistorySummary(droppedMsgs);
50833
- const summary = buildCheckpointMessage(
50834
- summaryText || `${COMPACT_MARKER} ${cut} earlier message(s) dropped.`,
50835
- droppedRange
50836
- );
50837
- return withDroppedRange(
51218
+ function appendToolStart(setMessages, toolName, toolCallId, args, ts) {
51219
+ const argsPreview = formatToolSummary(toolName, args);
51220
+ setMessages((prev2) => [
51221
+ ...prev2,
50838
51222
  {
50839
- messages: [summary, ...kept],
50840
- compacted: true,
50841
- messagesRemoved: cut,
50842
- summary: summary.content,
50843
- prunedToolResults: pruned.stats.pruned
50844
- },
50845
- droppedMsgs,
50846
- "extractive"
50847
- );
51223
+ id: crypto.randomUUID(),
51224
+ role: "tool",
51225
+ content: argsPreview,
51226
+ ts,
51227
+ toolName,
51228
+ toolCallId,
51229
+ toolOk: void 0,
51230
+ toolDurationMs: void 0
51231
+ }
51232
+ ]);
50848
51233
  }
50849
- async function compactHistoryAsync(messages, opts) {
50850
- const base2 = compactHistoryDetailed(messages, opts);
50851
- if (!base2.compacted || base2.messagesRemoved === 0) return base2;
50852
- const cut = base2.messagesRemoved;
50853
- const droppedMsgs = messages.slice(0, cut);
50854
- const extractive = extractiveHistorySummary(droppedMsgs);
50855
- let summaryText = extractive;
50856
- let cacheReuseExpected;
50857
- let replayExactPrefix;
50858
- const canReplay = !!(opts?.providerStream && opts?.requestSnapshot);
50859
- if (canReplay) {
50860
- try {
50861
- const replay = await llmSummarizeHistoryReplay({
50862
- providerStream: opts.providerStream,
50863
- provider: opts.requestSnapshot.provider,
50864
- model: opts.requestSnapshot.model,
50865
- systemMessages: opts.requestSnapshot.systemMessages,
50866
- tools: opts.requestSnapshot.tools,
50867
- droppedMessages: droppedMsgs,
50868
- signal: opts?.signal
50869
- });
50870
- cacheReuseExpected = replay.cacheReuseExpected;
50871
- if (replay.summary && replay.summary.trim().length > 40) {
50872
- const sourceTokens = roughTokens(droppedMsgs);
50873
- const summaryTok = Math.ceil(replay.summary.length / 4);
50874
- if (summaryTok < sourceTokens) {
50875
- summaryText = replay.summary.trim();
50876
- }
51234
+ function updateToolMessageEnd(setMessages, toolCallId, isError, durationMs, result) {
51235
+ setMessages((prev2) => {
51236
+ for (let i = prev2.length - 1; i >= 0; i--) {
51237
+ const m = prev2[i];
51238
+ if (m && m.role === "tool" && m.toolCallId === toolCallId && m.toolDurationMs === void 0) {
51239
+ const updated = [...prev2];
51240
+ updated[i] = {
51241
+ ...m,
51242
+ toolOk: !isError,
51243
+ toolDurationMs: durationMs,
51244
+ ...result !== void 0 ? {
51245
+ toolResult: toolResultForStorage(
51246
+ m.toolName ?? "",
51247
+ result,
51248
+ isError
51249
+ )
51250
+ } : {}
51251
+ };
51252
+ return updated;
50877
51253
  }
50878
- } catch {
50879
51254
  }
50880
- }
50881
- const pruned = pruneToolResultsDetailed(messages.slice(cut));
50882
- const kept = pruned.messages;
50883
- const summary = buildCheckpointMessage(summaryText, compactedRangeFromDropped(droppedMsgs));
50884
- const usedLlm = summaryText !== extractive && summaryText.trim().length > 40;
50885
- return withDroppedRange(
50886
- {
50887
- messages: [summary, ...kept],
50888
- compacted: true,
50889
- messagesRemoved: cut,
50890
- summary: summaryText,
50891
- prunedToolResults: pruned.stats.pruned,
50892
- cacheReuseExpected,
50893
- replayExactPrefix
50894
- },
50895
- droppedMsgs,
50896
- usedLlm ? "llm" : "extractive"
50897
- );
51255
+ return prev2;
51256
+ });
50898
51257
  }
50899
- function roughTokens(msgs) {
50900
- let n = 0;
50901
- for (const m of msgs) {
50902
- n += Math.ceil((m.content ?? "").length / 4);
50903
- if (m.toolCalls) {
50904
- for (const tc of m.toolCalls) {
50905
- n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
50906
- }
51258
+ function finalizeStreamingAssistant(setMessages) {
51259
+ setMessages((prev2) => {
51260
+ const last = prev2[prev2.length - 1];
51261
+ if (last && last.role === "assistant" && last.id.startsWith("streaming-")) {
51262
+ return [
51263
+ ...prev2.slice(0, -1),
51264
+ { ...last, id: last.id.slice("streaming-".length) }
51265
+ ];
50907
51266
  }
50908
- }
50909
- return Math.max(1, n);
51267
+ return prev2;
51268
+ });
50910
51269
  }
50911
- var COMPACT_MARKER, CHECKPOINT_WRAPPER_PREFIX;
50912
- var init_historyCompaction = __esm({
50913
- "src/cli/hooks/historyCompaction.ts"() {
51270
+ var init_messageHelpers = __esm({
51271
+ "src/cli/hooks/messageHelpers.ts"() {
50914
51272
  "use strict";
50915
- init_historySummary();
50916
- init_llmCompact();
50917
- init_envNumber();
50918
- COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
50919
- CHECKPOINT_WRAPPER_PREFIX = "This is an automatically generated checkpoint of earlier conversation. Treat it as established context and continue directly.";
51273
+ init_toolFormat();
51274
+ init_toolFormat();
50920
51275
  }
50921
51276
  });
50922
51277
 
@@ -51229,215 +51584,6 @@ var init_phase = __esm({
51229
51584
  }
51230
51585
  });
51231
51586
 
51232
- // src/cli/budget/tokenBudget.ts
51233
- function mergeCompactRange(into, r) {
51234
- if (r.fromSeq !== void 0 && r.toSeq !== void 0) {
51235
- into.fromSeq = into.fromSeq === void 0 ? r.fromSeq : Math.min(into.fromSeq, r.fromSeq);
51236
- into.toSeq = into.toSeq === void 0 ? r.toSeq : Math.max(into.toSeq, r.toSeq);
51237
- if (r.sourceEventSeqs) into.sourceSeqs.push(...r.sourceEventSeqs);
51238
- }
51239
- if (r.strategy === "llm") into.strategy = "llm";
51240
- else if (r.strategy && !into.strategy) into.strategy = r.strategy;
51241
- }
51242
- function estimateTokens2(text) {
51243
- if (!text) return 0;
51244
- return Math.max(1, Math.ceil(text.length / 4));
51245
- }
51246
- function estimateHistoryTokens(messages) {
51247
- let n = 0;
51248
- for (const m of messages) {
51249
- n += estimateTokens2(m.content);
51250
- if (m.toolCalls) {
51251
- for (const tc of m.toolCalls) {
51252
- n += estimateTokens2(tc.name) + estimateTokens2(JSON.stringify(tc.args ?? {}));
51253
- }
51254
- }
51255
- }
51256
- return n;
51257
- }
51258
- function defaultContextLimitForModel(model, provider) {
51259
- return capabilitiesFor(model, provider).contextWindow;
51260
- }
51261
- function resolveContextLimit(model, provider) {
51262
- return envNumber(process.env.ZELARI_CONTEXT_LIMIT, {
51263
- default: defaultContextLimitForModel(model, provider),
51264
- min: 4e3,
51265
- max: 2e6
51266
- });
51267
- }
51268
- function phaseKnobs(phase2) {
51269
- return {
51270
- historyTurns: phase2 === "plan" ? envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 8, min: 0 }) : envNumber(process.env.ZELARI_HISTORY_TURNS, { default: 6, min: 0 }),
51271
- maxToolLoopIterations: phase2 === "plan" ? envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, { default: 60, min: 1 }) : envNumber(process.env.ZELARI_MAX_TOOL_LOOP_ITERATIONS, {
51272
- default: 120,
51273
- min: 1
51274
- })
51275
- };
51276
- }
51277
- async function applyBudgetPolicyAsync(history2, phase2, opts) {
51278
- const contextLimit = resolveContextLimit(opts?.model, opts?.provider);
51279
- const compact3 = capabilitiesFor(opts?.model, opts?.provider).compaction;
51280
- const sessionExtra = opts?.sessionTokens ?? 0;
51281
- const warnings = [];
51282
- let { historyTurns, maxToolLoopIterations } = phaseKnobs(phase2);
51283
- const envelope = opts?.requestSnapshot ?? null;
51284
- const surface = envelope?.snapshot ?? opts?.requestSurface ?? null;
51285
- const replayBase = surface ? {
51286
- provider: surface.provider,
51287
- model: surface.model,
51288
- systemMessages: surface.systemMessages,
51289
- tools: surface.tools
51290
- } : opts?.providerStream ? { provider: "local", model: opts?.model ?? "unknown", systemMessages: [], tools: [] } : null;
51291
- const headerTokens = surface ? estimateSystemTokensLite(surface.systemMessages) + estimateToolSchemaTokensLite(surface.tools) : 0;
51292
- const convTokensOf = (h) => estimateConversationTokensLite(h);
51293
- let hist = history2;
51294
- let estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
51295
- let occupancy = Math.min(1, estimated / contextLimit);
51296
- let compactSummary = "";
51297
- let messagesRemoved = 0;
51298
- let cacheReuseExpected;
51299
- let prunedTotal = 0;
51300
- const compactRange = { sourceSeqs: [] };
51301
- if (occupancy >= compact3.warnAt && occupancy < compact3.compactAt) {
51302
- warnings.push(
51303
- `[budget] context ~${Math.round(occupancy * 100)}% full (${estimated}/${contextLimit} tok full-request est.) \u2014 consider /compact or shorter replies.`
51304
- );
51305
- }
51306
- if (occupancy >= 0.8) {
51307
- const pruned = pruneToolResultsDetailed(hist);
51308
- if (pruned.stats.pruned > 0) {
51309
- hist = pruned.messages;
51310
- prunedTotal += pruned.stats.pruned;
51311
- estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
51312
- occupancy = Math.min(1, estimated / contextLimit);
51313
- warnings.push(
51314
- `[budget] pruned ${pruned.stats.pruned} oversized tool result(s) \u2192 ${Math.round(occupancy * 100)}% (${estimated} tok).`
51315
- );
51316
- }
51317
- }
51318
- const fold = (r, label, forcedTurns) => {
51319
- hist = applySessionSurface(r.messages);
51320
- if (r.compacted) {
51321
- messagesRemoved += r.messagesRemoved;
51322
- if (r.summary) compactSummary = r.summary;
51323
- mergeCompactRange(compactRange, r);
51324
- if (r.cacheReuseExpected !== void 0) {
51325
- cacheReuseExpected = r.cacheReuseExpected;
51326
- }
51327
- }
51328
- estimated = surface ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
51329
- occupancy = Math.min(1, estimated / contextLimit);
51330
- warnings.push(
51331
- `[budget] ${label} \u2014 kept ~${forcedTurns} turns (${estimated} tok est.` + (r.messagesRemoved ? `, removed ${r.messagesRemoved} msgs` : "") + (r.cacheReuseExpected === false ? ", cache reuse NOT expected (model override)" : "") + ")."
51332
- );
51333
- };
51334
- if (occupancy >= compact3.compactAt) {
51335
- const forcedTurns = Math.max(1, Math.floor(historyTurns / 2));
51336
- historyTurns = forcedTurns;
51337
- maxToolLoopIterations = Math.min(
51338
- maxToolLoopIterations,
51339
- phase2 === "plan" ? 24 : 40
51340
- );
51341
- let r = await compactHistoryAsync(hist, {
51342
- maxMessages: Math.max(2, forcedTurns * 4),
51343
- force: true,
51344
- signal: opts?.signal,
51345
- ...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
51346
- });
51347
- if (!r.compacted) {
51348
- r = await compactHistoryAsync(hist, {
51349
- maxMessages: 2,
51350
- force: true,
51351
- signal: opts?.signal,
51352
- ...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
51353
- });
51354
- }
51355
- const label = r.cacheReuseExpected === false ? "llm-compact (model override)" : "auto-compact";
51356
- fold(r, label, forcedTurns);
51357
- }
51358
- if (occupancy >= compact3.hardAt) {
51359
- const hard = await compactHistoryAsync(hist, {
51360
- maxMessages: 2,
51361
- force: true,
51362
- signal: opts?.signal
51363
- });
51364
- fold(hard, hard.summary.includes("\xB7 llm") ? "llm-compact" : "HARD trim", 2);
51365
- historyTurns = 2;
51366
- maxToolLoopIterations = Math.min(maxToolLoopIterations, 16);
51367
- warnings.push(
51368
- "[budget] HARD context pressure (\u226595%) \u2014 prefer /clear or a new session if quality drops."
51369
- );
51370
- }
51371
- const cacheMetricsLine = envelope ? [
51372
- "compaction meter:",
51373
- `provider/model: ${envelope.snapshot.provider}/${envelope.snapshot.model}`,
51374
- `headerFingerprint: ${envelope.snapshot.headerFingerprint.slice(0, 12)}`,
51375
- `occupancy: ${Math.round(occupancy * 100)}% (${estimated}/${contextLimit})`,
51376
- ...envelope.usage?.cachedPromptTokens !== void 0 ? [`cachedPromptTokens: ${envelope.usage.cachedPromptTokens}`] : [],
51377
- ...cacheReuseExpected !== void 0 ? [`cacheReuseExpected: ${cacheReuseExpected}`] : []
51378
- ].join(" | ") : void 0;
51379
- return {
51380
- history: hist,
51381
- warnings,
51382
- maxToolLoopIterations,
51383
- historyTurns,
51384
- estimatedHistoryTokens: surface ? convTokensOf(hist) : estimated,
51385
- contextLimit,
51386
- occupancy,
51387
- compactSummary: compactSummary || void 0,
51388
- messagesRemoved: messagesRemoved || void 0,
51389
- ...compactRange.fromSeq !== void 0 && compactRange.toSeq !== void 0 ? {
51390
- compactedFromSeq: compactRange.fromSeq,
51391
- compactedToSeq: compactRange.toSeq,
51392
- compactSourceSeqs: compactRange.sourceSeqs,
51393
- compactStrategy: compactRange.strategy
51394
- } : {},
51395
- ...surface ? { contextPressureTokens: estimated } : {},
51396
- ...cacheReuseExpected !== void 0 ? { cacheReuseExpected } : {},
51397
- ...cacheMetricsLine ? { cacheMetricsLine } : {}
51398
- };
51399
- }
51400
- function estimateSystemTokensLite(systemMessages) {
51401
- let n = 0;
51402
- for (const m of systemMessages) n += 4 + Math.ceil((m.content ?? "").length / 4);
51403
- return n;
51404
- }
51405
- function estimateToolSchemaTokensLite(tools) {
51406
- let n = 0;
51407
- for (const t of tools) {
51408
- n += Math.ceil((t.name ?? "").length / 4);
51409
- n += Math.ceil((t.description ?? "").length / 4);
51410
- n += Math.ceil(JSON.stringify(t.parameters ?? {}).length / 4);
51411
- }
51412
- return n + tools.length * 4;
51413
- }
51414
- function estimateConversationTokensLite(messages) {
51415
- let n = 0;
51416
- for (const m of messages) {
51417
- n += 4 + Math.ceil((m.content ?? "").length / 4);
51418
- if (m.toolCalls) {
51419
- for (const tc of m.toolCalls) {
51420
- n += Math.ceil(tc.name.length / 4) + Math.ceil(tc.id.length / 4);
51421
- n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
51422
- }
51423
- }
51424
- if (m.reasoningContent) n += Math.ceil(m.reasoningContent.length / 4);
51425
- if (m.toolCallId) n += Math.ceil(m.toolCallId.length / 4);
51426
- }
51427
- return n;
51428
- }
51429
- var RESERVED_OUTPUT_TOKENS;
51430
- var init_tokenBudget = __esm({
51431
- "src/cli/budget/tokenBudget.ts"() {
51432
- "use strict";
51433
- init_envNumber();
51434
- init_historyCompaction();
51435
- init_observationStore();
51436
- init_capabilities();
51437
- RESERVED_OUTPUT_TOKENS = 8192;
51438
- }
51439
- });
51440
-
51441
51587
  // src/cli/budget/requestMeter.ts
51442
51588
  function estimateTokensLocal(text) {
51443
51589
  if (!text) return 0;
@@ -52114,6 +52260,51 @@ var init_persistCompact = __esm({
52114
52260
  }
52115
52261
  });
52116
52262
 
52263
+ // src/cli/budget/contextProjection.ts
52264
+ function policyFromOccupancy(occupancy, thresholds = DEFAULT_THRESHOLDS) {
52265
+ if (occupancy >= thresholds.hardAt) return "hard";
52266
+ if (occupancy >= thresholds.compactAt) return "compact";
52267
+ if (occupancy >= thresholds.warnAt) return "warn";
52268
+ return "ok";
52269
+ }
52270
+ function recordFromBudget(budget, thresholds = DEFAULT_THRESHOLDS) {
52271
+ return {
52272
+ occupancy: budget.occupancy,
52273
+ estimatedHistoryTokens: budget.estimatedHistoryTokens,
52274
+ contextLimit: budget.contextLimit,
52275
+ ...budget.contextPressureTokens !== void 0 ? { contextPressureTokens: budget.contextPressureTokens } : {},
52276
+ policy: policyFromOccupancy(budget.occupancy, thresholds)
52277
+ };
52278
+ }
52279
+ function thresholdsFor(model, provider) {
52280
+ return capabilitiesFor(model, provider).compaction;
52281
+ }
52282
+ function noteBudgetProjection(handle, record2) {
52283
+ try {
52284
+ handle.note("context.projection", {
52285
+ subject: "context.projection",
52286
+ occupancy: record2.occupancy,
52287
+ estimatedHistoryTokens: record2.estimatedHistoryTokens,
52288
+ contextLimit: record2.contextLimit,
52289
+ ...record2.contextPressureTokens !== void 0 ? { contextPressureTokens: record2.contextPressureTokens } : {},
52290
+ policy: record2.policy
52291
+ });
52292
+ } catch {
52293
+ }
52294
+ }
52295
+ var DEFAULT_THRESHOLDS;
52296
+ var init_contextProjection = __esm({
52297
+ "src/cli/budget/contextProjection.ts"() {
52298
+ "use strict";
52299
+ init_capabilities();
52300
+ DEFAULT_THRESHOLDS = {
52301
+ warnAt: 0.7,
52302
+ compactAt: 0.85,
52303
+ hardAt: 0.95
52304
+ };
52305
+ }
52306
+ });
52307
+
52117
52308
  // src/cli/budget/modelContextBuilder.ts
52118
52309
  function messageWasRecompacted(message, history2) {
52119
52310
  if (message.compactedFromSeq === void 0) return false;
@@ -52220,6 +52411,15 @@ async function buildModelContext(input) {
52220
52411
  occupancy: Math.min(1, estimated / budget.contextLimit)
52221
52412
  };
52222
52413
  }
52414
+ if (input.budgetNoteHandle) {
52415
+ try {
52416
+ noteBudgetProjection(
52417
+ input.budgetNoteHandle,
52418
+ recordFromBudget(budget, thresholdsFor(input.model, input.provider))
52419
+ );
52420
+ } catch {
52421
+ }
52422
+ }
52223
52423
  return {
52224
52424
  history: history2,
52225
52425
  requestTail,
@@ -52237,6 +52437,7 @@ var init_modelContextBuilder = __esm({
52237
52437
  init_tokenBudget();
52238
52438
  init_requestMeter();
52239
52439
  init_persistCompact();
52440
+ init_contextProjection();
52240
52441
  init_headlessSpine();
52241
52442
  init_session();
52242
52443
  }
@@ -63135,6 +63336,20 @@ function asNumber2(v) {
63135
63336
  function asCallId(v, seq) {
63136
63337
  return typeof v === "string" && v.length > 0 ? v : `seq:${seq}`;
63137
63338
  }
63339
+ function parseProjection(data) {
63340
+ const policy = asString3(data.policy);
63341
+ return {
63342
+ contextChars: asNumber2(data.contextChars) ?? 0,
63343
+ returnedCount: asNumber2(data.returnedCount) ?? 0,
63344
+ occupancy: asNumber2(data.occupancy),
63345
+ estimatedHistoryTokens: asNumber2(data.estimatedHistoryTokens),
63346
+ contextLimit: asNumber2(data.contextLimit),
63347
+ contextPressureTokens: asNumber2(data.contextPressureTokens),
63348
+ durationMs: asNumber2(data.durationMs),
63349
+ backend: asString3(data.backend) || void 0,
63350
+ policy: policy === "ok" || policy === "warn" || policy === "compact" || policy === "hard" ? policy : void 0
63351
+ };
63352
+ }
63138
63353
  function newTurn(index, userText) {
63139
63354
  return {
63140
63355
  index,
@@ -63216,17 +63431,14 @@ function deriveHarnessState(events) {
63216
63431
  }
63217
63432
  case "session.compacted": {
63218
63433
  support.compactions += 1;
63219
- const saved = asNumber2(e.data.tokensSaved);
63434
+ const saved = asNumber2(e.data.savedTokens) ?? asNumber2(e.data.tokensSaved);
63220
63435
  if (saved !== void 0) tokensSaved = (tokensSaved ?? 0) + saved;
63221
63436
  break;
63222
63437
  }
63223
63438
  case "note": {
63224
63439
  const subject = asString3(e.data.subject);
63225
63440
  if (subject === "context.projection") {
63226
- support.contextProjections.push({
63227
- contextChars: asNumber2(e.data.contextChars) ?? 0,
63228
- returnedCount: asNumber2(e.data.returnedCount) ?? 0
63229
- });
63441
+ support.contextProjections.push(parseProjection(e.data));
63230
63442
  } else if (subject === "memory_event") {
63231
63443
  support.memoryEvents += 1;
63232
63444
  }
@@ -64177,6 +64389,7 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
64177
64389
  toolSpecs: typeof toolRegistry.fingerprints === "function" ? toolRegistry.fingerprints() : void 0
64178
64390
  });
64179
64391
  spineHolder.current = spine;
64392
+ flushMemorySpineNotes(spineHolder);
64180
64393
  const seededHistory = await seedHeadlessModelHistory(spine, opts.history);
64181
64394
  emitEvent(sessionStartedEvent(spine));
64182
64395
  if (opts.orchestrationDecision) {
@@ -64314,6 +64527,8 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
64314
64527
  tools,
64315
64528
  sessionId: spine.sessionId,
64316
64529
  providerStream,
64530
+ // T4-S2: budget occupancy/policy onto the spine (context.projection note).
64531
+ budgetNoteHandle: spine,
64317
64532
  onCompactionMetric: (metrics) => recordCompactionMetrics(spine.sessionId, provider, model, metrics),
64318
64533
  persistCompaction: async (payload) => {
64319
64534
  await spine.appendEvent({
@@ -66058,6 +66273,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
66058
66273
  workspace: cwd
66059
66274
  });
66060
66275
  spineHolder.current = spine;
66276
+ flushMemorySpineNotes(spineHolder);
66061
66277
  const seededHistory = await seedHeadlessModelHistory(spine, opts.history);
66062
66278
  emitEvent(sessionStartedEvent(spine));
66063
66279
  if (opts.orchestrationDecision) {
@@ -66097,6 +66313,8 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
66097
66313
  tools: contextTools,
66098
66314
  sessionId: spine.sessionId,
66099
66315
  providerStream,
66316
+ // T4-S2: budget occupancy/policy onto the spine (context.projection note).
66317
+ budgetNoteHandle: spine,
66100
66318
  onCompactionMetric: (metrics) => recordCompactionMetrics(spine.sessionId, provider, model, metrics),
66101
66319
  persistCompaction: async (payload) => {
66102
66320
  await spine.appendEvent({
@@ -66121,11 +66339,14 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
66121
66339
  const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
66122
66340
  const { loadDurableContext: loadDurableContext2 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
66123
66341
  const durableState = await loadDurableContext2(cwd);
66342
+ const { resolveRetrievalPolicy: resolveRetrievalPolicy2 } = await Promise.resolve().then(() => (init_retrievalPolicy(), retrievalPolicy_exports));
66343
+ const retrieval = resolveRetrievalPolicy2(councilContext.budget.occupancy);
66124
66344
  const memoryContext = nativeMemory ? (await nativeMemory.buildContext({
66125
66345
  text: effectiveTask,
66126
66346
  useGraph: true,
66127
- maxChars: 2e3,
66128
- maxMemories: 8
66347
+ maxChars: retrieval.maxChars,
66348
+ maxMemories: retrieval.maxMemories,
66349
+ ...retrieval.weights ? { weights: retrieval.weights } : {}
66129
66350
  })).text : "";
66130
66351
  const composed = composeProjectContext2({
66131
66352
  mode: "council",
@@ -66312,6 +66533,8 @@ async function runHeadlessZelari(opts, provider, model, providerStream, extras)
66312
66533
  tools: contextTools,
66313
66534
  sessionId: spine.sessionId,
66314
66535
  providerStream,
66536
+ // T4-S2: budget occupancy/policy onto the spine (context.projection note).
66537
+ budgetNoteHandle: spine,
66315
66538
  onCompactionMetric: (metrics) => recordCompactionMetrics(spine.sessionId, provider, model, metrics),
66316
66539
  persistCompaction: async (payload) => {
66317
66540
  await spine.appendEvent({
@@ -70043,6 +70266,9 @@ __export(inspectSession_exports, {
70043
70266
  });
70044
70267
  import path89 from "node:path";
70045
70268
  import { existsSync as existsSync56 } from "node:fs";
70269
+ function formatLimit(limit) {
70270
+ return `${Math.round(limit / 1e3)}k`;
70271
+ }
70046
70272
  function renderInspectReport(state3) {
70047
70273
  const lines = [
70048
70274
  `session ${state3.session.sessionId} status=${state3.session.status} turns=${state3.execution.turnsTotal}`
@@ -70061,9 +70287,8 @@ function renderInspectReport(state3) {
70061
70287
  lines.push("support lens:");
70062
70288
  const projections = state3.support.contextProjections;
70063
70289
  const last = projections[projections.length - 1];
70064
- lines.push(
70065
- ` context projections: ${projections.length}` + (last ? ` (last: ${last.contextChars} chars \u2192 ${last.returnedCount} items)` : "")
70066
- );
70290
+ const tail2 = !last ? "" : last.occupancy !== void 0 && last.contextLimit !== void 0 ? ` (last: ${Math.round(last.occupancy * 100)}% ${last.policy ?? "?"} (limit ${formatLimit(last.contextLimit)}))` : ` (last: ${last.contextChars} chars \u2192 ${last.returnedCount} items)`;
70291
+ lines.push(` context projections: ${projections.length}${tail2}`);
70067
70292
  lines.push(` memory events: ${state3.support.memoryEvents}`);
70068
70293
  const saved = state3.support.tokensSavedByCompaction;
70069
70294
  lines.push(
@@ -73339,21 +73564,27 @@ function createPermissionAskHandler(opts) {
73339
73564
  const catLabel = cats.length === 1 ? cats[0] : cats.join("+") || "action";
73340
73565
  const title = `Allow tool "${req.toolName}"?`;
73341
73566
  const detail = req.reason + (cats.length ? ` [${cats.join(", ")}]` : "");
73567
+ const claimLines = (req.claims ?? []).map((c) => `\xB7 ${c.summary}`);
73568
+ const note = req.policyNote ? `
73569
+ ${req.policyNote}` : "";
73570
+ const claimsBlock = claimLines.length ? `
73571
+ Claims:
73572
+ ${claimLines.join("\n")}` : "";
73342
73573
  appendSystem2?.(
73343
73574
  `[permission] ${title}
73344
- ${detail}
73575
+ ${detail}${note}${claimsBlock}
73345
73576
  \u2192 Allow once \xB7 Always (tool) \xB7 Always (${catLabel}) \xB7 Deny`,
73346
73577
  Date.now()
73347
73578
  );
73348
73579
  let settled = false;
73349
73580
  const askTimeoutMs = askUserTimeoutMs();
73350
73581
  let cancelAskTimeout = () => void 0;
73351
- const finish2 = (ok, note) => {
73582
+ const finish2 = (ok, note2) => {
73352
73583
  if (settled) return;
73353
73584
  settled = true;
73354
73585
  cancelAskTimeout();
73355
73586
  setPicker2(null);
73356
- if (note) appendSystem2?.(note, Date.now());
73587
+ if (note2) appendSystem2?.(note2, Date.now());
73357
73588
  resolve9(ok);
73358
73589
  };
73359
73590
  cancelAskTimeout = armPickerTimeout(
@@ -74487,6 +74718,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
74487
74718
  let councilMemory;
74488
74719
  let councilMemoryAutoWrite = false;
74489
74720
  let nativeMemoryContext = "";
74721
+ let councilRetrievalBand = "low";
74490
74722
  try {
74491
74723
  const memoryFactory = await Promise.resolve().then(() => (init_serviceFactory(), serviceFactory_exports));
74492
74724
  if (memoryFactory.isMemoryV2Enabled()) {
@@ -74501,11 +74733,17 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
74501
74733
  });
74502
74734
  councilMemoryAutoWrite = memoryFactory.isMemoryAutoWriteEnabled();
74503
74735
  if (!overrides.ragContext) {
74736
+ const { resolveRetrievalPolicy: resolveRetrievalPolicy2 } = await Promise.resolve().then(() => (init_retrievalPolicy(), retrievalPolicy_exports));
74737
+ const retrieval = resolveRetrievalPolicy2(
74738
+ councilContext.budget.occupancy
74739
+ );
74740
+ councilRetrievalBand = retrieval.band;
74504
74741
  nativeMemoryContext = (await councilMemory.buildContext({
74505
74742
  text: effectiveText,
74506
74743
  useGraph: true,
74507
- maxChars: 2e3,
74508
- maxMemories: 8
74744
+ maxChars: retrieval.maxChars,
74745
+ maxMemories: retrieval.maxMemories,
74746
+ ...retrieval.weights ? { weights: retrieval.weights } : {}
74509
74747
  })).text;
74510
74748
  }
74511
74749
  }
@@ -76772,6 +77010,7 @@ async function handleKrakenGraph(ctx, prompt) {
76772
77010
  onWarning: (warning) => appendSystem(ctx.setMessages, warning),
76773
77011
  onEvent: memorySinkFor(tuiSpineHolder)
76774
77012
  }) : void 0;
77013
+ flushMemorySpineNotes(tuiSpineHolder);
76775
77014
  const audit = new AuditLogger();
76776
77015
  const taskToolDeps = {
76777
77016
  createSubAgentContext: createKrakenSubAgentContextFactory({