zelari-code 1.38.0 → 1.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/budget/llmCompact.js +97 -83
- package/dist/cli/budget/llmCompact.js.map +1 -1
- package/dist/cli/budget/requestMeter.js +138 -0
- package/dist/cli/budget/requestMeter.js.map +1 -0
- package/dist/cli/budget/requestSnapshotStore.js +55 -0
- package/dist/cli/budget/requestSnapshotStore.js.map +1 -0
- package/dist/cli/budget/tokenBudget.js +147 -15
- package/dist/cli/budget/tokenBudget.js.map +1 -1
- package/dist/cli/desktopConfig.js +8 -2
- package/dist/cli/desktopConfig.js.map +1 -1
- package/dist/cli/hooks/conversationContext.js +4 -0
- package/dist/cli/hooks/conversationContext.js.map +1 -1
- package/dist/cli/hooks/historyCompaction.js +76 -21
- package/dist/cli/hooks/historyCompaction.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +90 -23
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/main.bundled.js +718 -297
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/modelDiscovery.js +22 -33
- package/dist/cli/modelDiscovery.js.map +1 -1
- package/dist/cli/oauthDesktop.js +3 -3
- package/dist/cli/provider/anthropic.js +1 -1
- package/dist/cli/provider/anthropic.js.map +1 -1
- package/dist/cli/provider/chatgpt.js +1 -1
- package/dist/cli/provider/chatgpt.js.map +1 -1
- package/dist/cli/provider/openai-compatible.js +12 -2
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/providerConfig.js +5 -5
- package/dist/cli/providerConfig.js.map +1 -1
- package/dist/cli/slashHandlers/provider.js +3 -3
- package/dist/cli/slashHandlers/provider.js.map +1 -1
- package/dist/cli/thinking.js +102 -43
- package/dist/cli/thinking.js.map +1 -1
- package/dist/cli/thinking.test.js +125 -4
- package/dist/cli/thinking.test.js.map +1 -1
- package/dist/cli/thinkingCapability.js +160 -0
- package/dist/cli/thinkingCapability.js.map +1 -0
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -1468,27 +1468,27 @@ function readStore() {
|
|
|
1468
1468
|
}
|
|
1469
1469
|
return { providers: {} };
|
|
1470
1470
|
}
|
|
1471
|
-
function writeStore(
|
|
1471
|
+
function writeStore(store4) {
|
|
1472
1472
|
const file2 = getKeyStorePath();
|
|
1473
1473
|
mkdirSync2(path3.dirname(file2), { recursive: true });
|
|
1474
|
-
writeFileSync2(file2, JSON.stringify(
|
|
1474
|
+
writeFileSync2(file2, JSON.stringify(store4, null, 2), { encoding: "utf-8", mode: 384 });
|
|
1475
1475
|
}
|
|
1476
1476
|
function setApiKey(providerId, key) {
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1479
|
-
writeStore(
|
|
1477
|
+
const store4 = readStore();
|
|
1478
|
+
store4.providers[providerId] = { apiKey: key };
|
|
1479
|
+
writeStore(store4);
|
|
1480
1480
|
}
|
|
1481
1481
|
function clearApiKey(providerId) {
|
|
1482
|
-
const
|
|
1483
|
-
delete
|
|
1484
|
-
writeStore(
|
|
1482
|
+
const store4 = readStore();
|
|
1483
|
+
delete store4.providers[providerId];
|
|
1484
|
+
writeStore(store4);
|
|
1485
1485
|
}
|
|
1486
1486
|
function getStoredApiKey(providerId) {
|
|
1487
|
-
const
|
|
1488
|
-
return
|
|
1487
|
+
const store4 = readStore();
|
|
1488
|
+
return store4.providers[providerId]?.apiKey ?? null;
|
|
1489
1489
|
}
|
|
1490
1490
|
function setOAuthToken(providerId, token) {
|
|
1491
|
-
const
|
|
1491
|
+
const store4 = readStore();
|
|
1492
1492
|
const entry = { apiKey: token.apiKey };
|
|
1493
1493
|
if (typeof token.expiresAt === "number" && Number.isFinite(token.expiresAt)) {
|
|
1494
1494
|
entry.expiresAt = token.expiresAt;
|
|
@@ -1502,12 +1502,12 @@ function setOAuthToken(providerId, token) {
|
|
|
1502
1502
|
if (typeof token.idToken === "string" && token.idToken.length > 0) {
|
|
1503
1503
|
entry.idToken = token.idToken;
|
|
1504
1504
|
}
|
|
1505
|
-
|
|
1506
|
-
writeStore(
|
|
1505
|
+
store4.providers[providerId] = entry;
|
|
1506
|
+
writeStore(store4);
|
|
1507
1507
|
}
|
|
1508
1508
|
function getOAuthToken(providerId) {
|
|
1509
|
-
const
|
|
1510
|
-
return
|
|
1509
|
+
const store4 = readStore();
|
|
1510
|
+
return store4.providers[providerId] ?? null;
|
|
1511
1511
|
}
|
|
1512
1512
|
function resolveApiKey(providerId) {
|
|
1513
1513
|
const spec = getProviderSpec(providerId);
|
|
@@ -1566,8 +1566,8 @@ async function forceRefreshOAuth(providerId, options = {}) {
|
|
|
1566
1566
|
function readStoreDirect() {
|
|
1567
1567
|
return readStore();
|
|
1568
1568
|
}
|
|
1569
|
-
function writeStoreDirect(
|
|
1570
|
-
writeStore(
|
|
1569
|
+
function writeStoreDirect(store4) {
|
|
1570
|
+
writeStore(store4);
|
|
1571
1571
|
}
|
|
1572
1572
|
function maskKey(key) {
|
|
1573
1573
|
if (key.length <= 12) return "****";
|
|
@@ -1620,9 +1620,151 @@ var init_keyStore = __esm({
|
|
|
1620
1620
|
}
|
|
1621
1621
|
});
|
|
1622
1622
|
|
|
1623
|
+
// src/cli/thinkingCapability.ts
|
|
1624
|
+
function thinkingCapabilityFor(id, model) {
|
|
1625
|
+
const base = PROVIDER_THINKING_CAPABILITY[id] ?? {};
|
|
1626
|
+
const efforts = effortLevelsFor(id, model);
|
|
1627
|
+
const budget = supportsBudget(id, model);
|
|
1628
|
+
return {
|
|
1629
|
+
...base,
|
|
1630
|
+
effort: efforts.length > 0 || Boolean(base.effort),
|
|
1631
|
+
budget,
|
|
1632
|
+
efforts: efforts.length > 0 ? efforts : void 0
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
function effortLevelsFor(id, model) {
|
|
1636
|
+
const m = (model ?? "").trim();
|
|
1637
|
+
switch (id) {
|
|
1638
|
+
case "grok":
|
|
1639
|
+
case "openai-compatible":
|
|
1640
|
+
case "custom":
|
|
1641
|
+
if (grokHasXhigh(m)) return [...BASE_EFFORTS, "xhigh"];
|
|
1642
|
+
return [...BASE_EFFORTS];
|
|
1643
|
+
case "chatgpt":
|
|
1644
|
+
if (gptHasMax(m)) return [...BASE_EFFORTS, "xhigh", "max"];
|
|
1645
|
+
if (gptHasXhigh(m)) return [...BASE_EFFORTS, "xhigh"];
|
|
1646
|
+
return [...BASE_EFFORTS];
|
|
1647
|
+
case "deepseek":
|
|
1648
|
+
return ["high", "max"];
|
|
1649
|
+
case "minimax":
|
|
1650
|
+
return [...BASE_EFFORTS];
|
|
1651
|
+
case "glm":
|
|
1652
|
+
if (glmHasEffortScale(m)) return ["low", "high", "max"];
|
|
1653
|
+
return [];
|
|
1654
|
+
case "anthropic":
|
|
1655
|
+
if (claudeHasXhigh(m)) return ["high", "xhigh", "max"];
|
|
1656
|
+
if (claudeHasMax(m)) return ["high", "max"];
|
|
1657
|
+
return [];
|
|
1658
|
+
default:
|
|
1659
|
+
return [];
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
function supportsBudget(id, model) {
|
|
1663
|
+
if (id === "anthropic") return true;
|
|
1664
|
+
if (id === "glm") return !glmHasEffortScale(model);
|
|
1665
|
+
return Boolean(PROVIDER_THINKING_CAPABILITY[id]?.budget);
|
|
1666
|
+
}
|
|
1667
|
+
function grokHasXhigh(model) {
|
|
1668
|
+
const v = parseDottedVersion(model, /grok[-_]?(\d+)(?:[.-](\d+))?/i);
|
|
1669
|
+
if (!v) return false;
|
|
1670
|
+
return v.major > 4 || v.major === 4 && v.minor >= 6;
|
|
1671
|
+
}
|
|
1672
|
+
function gptHasXhigh(model) {
|
|
1673
|
+
const v = parseDottedVersion(model, /gpt[-_]?(\d+)(?:[.-](\d+))?/i);
|
|
1674
|
+
if (!v) return false;
|
|
1675
|
+
return v.major > 5 || v.major === 5 && v.minor >= 4;
|
|
1676
|
+
}
|
|
1677
|
+
function gptHasMax(model) {
|
|
1678
|
+
const v = parseDottedVersion(model, /gpt[-_]?(\d+)(?:[.-](\d+))?/i);
|
|
1679
|
+
if (!v) return false;
|
|
1680
|
+
return v.major > 5 || v.major === 5 && v.minor >= 6;
|
|
1681
|
+
}
|
|
1682
|
+
function claudeHasMax(model) {
|
|
1683
|
+
const v = parseClaudeVersion(model);
|
|
1684
|
+
if (!v) return false;
|
|
1685
|
+
return v.major > 4 || v.major === 4 && v.minor >= 6;
|
|
1686
|
+
}
|
|
1687
|
+
function claudeHasXhigh(model) {
|
|
1688
|
+
const v = parseClaudeVersion(model);
|
|
1689
|
+
if (!v) return false;
|
|
1690
|
+
if (v.major >= 5) return true;
|
|
1691
|
+
return v.major === 4 && v.minor >= 7;
|
|
1692
|
+
}
|
|
1693
|
+
function glmHasEffortScale(model) {
|
|
1694
|
+
const v = parseDottedVersion(model ?? "", /glm[-_]?(\d+)(?:[.-](\d+))?/i);
|
|
1695
|
+
if (!v) return false;
|
|
1696
|
+
return v.major >= 5;
|
|
1697
|
+
}
|
|
1698
|
+
function parseDottedVersion(model, re) {
|
|
1699
|
+
const m = re.exec(model);
|
|
1700
|
+
if (!m) return null;
|
|
1701
|
+
return {
|
|
1702
|
+
major: Number.parseInt(m[1], 10),
|
|
1703
|
+
minor: m[2] ? Number.parseInt(m[2], 10) : 0
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
function parseClaudeVersion(model) {
|
|
1707
|
+
const m = /claude-(?:sonnet|opus|haiku)[-_]?(\d+)(?:[.-](\d+))?/i.exec(model);
|
|
1708
|
+
if (!m) return null;
|
|
1709
|
+
return {
|
|
1710
|
+
major: Number.parseInt(m[1], 10),
|
|
1711
|
+
minor: m[2] ? Number.parseInt(m[2], 10) : 0
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
var THINKING_EFFORTS, BASE_EFFORTS, PROVIDER_THINKING_CAPABILITY;
|
|
1715
|
+
var init_thinkingCapability = __esm({
|
|
1716
|
+
"src/cli/thinkingCapability.ts"() {
|
|
1717
|
+
"use strict";
|
|
1718
|
+
THINKING_EFFORTS = [
|
|
1719
|
+
"low",
|
|
1720
|
+
"medium",
|
|
1721
|
+
"high",
|
|
1722
|
+
"xhigh",
|
|
1723
|
+
"max"
|
|
1724
|
+
];
|
|
1725
|
+
BASE_EFFORTS = ["low", "medium", "high"];
|
|
1726
|
+
PROVIDER_THINKING_CAPABILITY = {
|
|
1727
|
+
"openai-compatible": { effort: true },
|
|
1728
|
+
grok: { effort: true },
|
|
1729
|
+
chatgpt: { effort: true },
|
|
1730
|
+
anthropic: { budget: true },
|
|
1731
|
+
glm: { budget: true },
|
|
1732
|
+
deepseek: { effort: true },
|
|
1733
|
+
minimax: { effort: true },
|
|
1734
|
+
custom: { effort: true }
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1623
1739
|
// src/cli/thinking.ts
|
|
1624
|
-
function
|
|
1625
|
-
|
|
1740
|
+
function clampEffort(id, model, requested) {
|
|
1741
|
+
const native = effortLevelsFor(id, model);
|
|
1742
|
+
if (native.includes(requested)) {
|
|
1743
|
+
return { effort: requested, clamped: false };
|
|
1744
|
+
}
|
|
1745
|
+
if (native.length === 0) {
|
|
1746
|
+
return {
|
|
1747
|
+
effort: requested,
|
|
1748
|
+
clamped: true,
|
|
1749
|
+
note: `thinking '${requested}' is not supported for provider "${id}"`
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
const want = EFFORT_RANK[requested];
|
|
1753
|
+
let best = native[0];
|
|
1754
|
+
let bestDist = Math.abs(EFFORT_RANK[best] - want);
|
|
1755
|
+
for (const level of native) {
|
|
1756
|
+
const dist = Math.abs(EFFORT_RANK[level] - want);
|
|
1757
|
+
if (dist < bestDist || dist === bestDist && EFFORT_RANK[level] > EFFORT_RANK[best]) {
|
|
1758
|
+
best = level;
|
|
1759
|
+
bestDist = dist;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
const label = model ? `${id}/${model}` : id;
|
|
1763
|
+
return {
|
|
1764
|
+
effort: best,
|
|
1765
|
+
clamped: true,
|
|
1766
|
+
note: `'${requested}' is not native on ${label} \u2014 using '${best}'`
|
|
1767
|
+
};
|
|
1626
1768
|
}
|
|
1627
1769
|
function stringifyThinkingSpec(spec) {
|
|
1628
1770
|
if (spec === "auto") return "auto";
|
|
@@ -1634,7 +1776,9 @@ function parseThinkingSpec(raw) {
|
|
|
1634
1776
|
const s = (raw ?? "").trim().toLowerCase();
|
|
1635
1777
|
if (!s || s === "auto") return "auto";
|
|
1636
1778
|
if (s === "off") return { kind: "off" };
|
|
1637
|
-
if (s
|
|
1779
|
+
if (THINKING_EFFORTS.includes(s)) {
|
|
1780
|
+
return { kind: "effort", effort: s };
|
|
1781
|
+
}
|
|
1638
1782
|
const m = /^budget:(\d+)$/.exec(s);
|
|
1639
1783
|
if (m) {
|
|
1640
1784
|
const n = Number.parseInt(m[1], 10);
|
|
@@ -1644,15 +1788,19 @@ function parseThinkingSpec(raw) {
|
|
|
1644
1788
|
}
|
|
1645
1789
|
function isValidThinkingInput(raw) {
|
|
1646
1790
|
const s = raw.trim().toLowerCase();
|
|
1647
|
-
if (s === "auto" || s === "off"
|
|
1791
|
+
if (s === "auto" || s === "off") return true;
|
|
1792
|
+
if (THINKING_EFFORTS.includes(s)) return true;
|
|
1648
1793
|
return /^budget:\d+$/.test(s) && Number.parseInt(s.slice(7), 10) > 0;
|
|
1649
1794
|
}
|
|
1650
1795
|
function degrade(note) {
|
|
1651
1796
|
return { patch: {}, degraded: true, note };
|
|
1652
1797
|
}
|
|
1653
|
-
function
|
|
1798
|
+
function withClampNote(patch, clamped, note) {
|
|
1799
|
+
return { patch, degraded: false, note: clamped ? note : void 0 };
|
|
1800
|
+
}
|
|
1801
|
+
function translateOpenAiCompatibleThinking(providerId, spec, model) {
|
|
1654
1802
|
if (spec === "auto") return { patch: {}, degraded: false };
|
|
1655
|
-
const cap3 = thinkingCapabilityFor(providerId);
|
|
1803
|
+
const cap3 = thinkingCapabilityFor(providerId, model);
|
|
1656
1804
|
switch (spec.kind) {
|
|
1657
1805
|
case "off":
|
|
1658
1806
|
if (providerId === "deepseek" || providerId === "glm") {
|
|
@@ -1660,20 +1808,40 @@ function translateOpenAiCompatibleThinking(providerId, spec) {
|
|
|
1660
1808
|
}
|
|
1661
1809
|
if (cap3.effort) return { patch: { reasoning_effort: "low" }, degraded: false };
|
|
1662
1810
|
return degrade(`thinking 'off' is not supported for provider "${providerId}"`);
|
|
1663
|
-
case "effort":
|
|
1664
|
-
if (!cap3.effort) {
|
|
1811
|
+
case "effort": {
|
|
1812
|
+
if (!cap3.effort && !cap3.efforts?.length) {
|
|
1665
1813
|
return degrade(`thinking 'effort' is not supported for provider "${providerId}"`);
|
|
1666
1814
|
}
|
|
1815
|
+
const resolved = clampEffort(providerId, model, spec.effort);
|
|
1667
1816
|
if (providerId === "deepseek") {
|
|
1668
|
-
return
|
|
1669
|
-
|
|
1817
|
+
return withClampNote(
|
|
1818
|
+
{
|
|
1670
1819
|
thinking: { type: "enabled" },
|
|
1671
|
-
reasoning_effort:
|
|
1820
|
+
reasoning_effort: resolved.effort === "max" ? "max" : "high"
|
|
1672
1821
|
},
|
|
1673
|
-
|
|
1674
|
-
|
|
1822
|
+
resolved.clamped,
|
|
1823
|
+
resolved.note
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
1826
|
+
if (providerId === "glm") {
|
|
1827
|
+
if (!glmHasEffortScale(model)) {
|
|
1828
|
+
return degrade(`thinking 'effort' is not supported for GLM ${model || "4.x"} \u2014 use budget:N`);
|
|
1829
|
+
}
|
|
1830
|
+
return withClampNote(
|
|
1831
|
+
{
|
|
1832
|
+
thinking: { type: "enabled" },
|
|
1833
|
+
reasoning_effort: resolved.effort
|
|
1834
|
+
},
|
|
1835
|
+
resolved.clamped,
|
|
1836
|
+
resolved.note
|
|
1837
|
+
);
|
|
1675
1838
|
}
|
|
1676
|
-
return
|
|
1839
|
+
return withClampNote(
|
|
1840
|
+
{ reasoning_effort: resolved.effort },
|
|
1841
|
+
resolved.clamped,
|
|
1842
|
+
resolved.note
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1677
1845
|
case "budget":
|
|
1678
1846
|
if (!cap3.budget) {
|
|
1679
1847
|
return degrade(`thinking 'budget' is not supported for provider "${providerId}"`);
|
|
@@ -1684,18 +1852,24 @@ function translateOpenAiCompatibleThinking(providerId, spec) {
|
|
|
1684
1852
|
};
|
|
1685
1853
|
}
|
|
1686
1854
|
}
|
|
1687
|
-
function translateResponsesThinking(spec) {
|
|
1855
|
+
function translateResponsesThinking(spec, model) {
|
|
1688
1856
|
if (spec === "auto") return { patch: {}, degraded: false };
|
|
1689
1857
|
switch (spec.kind) {
|
|
1690
1858
|
case "off":
|
|
1691
1859
|
return { patch: { reasoning: { effort: "minimal" } }, degraded: false };
|
|
1692
|
-
case "effort":
|
|
1693
|
-
|
|
1860
|
+
case "effort": {
|
|
1861
|
+
const resolved = clampEffort("chatgpt", model, spec.effort);
|
|
1862
|
+
return withClampNote(
|
|
1863
|
+
{ reasoning: { effort: resolved.effort } },
|
|
1864
|
+
resolved.clamped,
|
|
1865
|
+
resolved.note
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1694
1868
|
case "budget":
|
|
1695
|
-
return degrade('thinking "budget" is not supported for chatgpt \u2014 use low/medium/high');
|
|
1869
|
+
return degrade('thinking "budget" is not supported for chatgpt \u2014 use low/medium/high/xhigh/max');
|
|
1696
1870
|
}
|
|
1697
1871
|
}
|
|
1698
|
-
function translateAnthropicThinking(spec) {
|
|
1872
|
+
function translateAnthropicThinking(spec, model) {
|
|
1699
1873
|
if (spec === "auto") return { patch: {}, degraded: false };
|
|
1700
1874
|
switch (spec.kind) {
|
|
1701
1875
|
case "off":
|
|
@@ -1705,23 +1879,31 @@ function translateAnthropicThinking(spec) {
|
|
|
1705
1879
|
patch: { thinking: { type: "enabled", budget_tokens: spec.budgetTokens } },
|
|
1706
1880
|
degraded: false
|
|
1707
1881
|
};
|
|
1708
|
-
case "effort":
|
|
1709
|
-
|
|
1882
|
+
case "effort": {
|
|
1883
|
+
const levels = effortLevelsFor("anthropic", model);
|
|
1884
|
+
if (levels.length === 0) {
|
|
1885
|
+
return degrade('thinking "effort" is not supported for this Claude model \u2014 use budget:N');
|
|
1886
|
+
}
|
|
1887
|
+
const resolved = clampEffort("anthropic", model, spec.effort);
|
|
1888
|
+
return withClampNote(
|
|
1889
|
+
{ output_config: { effort: resolved.effort } },
|
|
1890
|
+
resolved.clamped,
|
|
1891
|
+
resolved.note
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1710
1894
|
}
|
|
1711
1895
|
}
|
|
1712
|
-
var
|
|
1896
|
+
var EFFORT_RANK;
|
|
1713
1897
|
var init_thinking = __esm({
|
|
1714
1898
|
"src/cli/thinking.ts"() {
|
|
1715
1899
|
"use strict";
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
"minimax": { effort: true },
|
|
1724
|
-
"custom": { effort: true }
|
|
1900
|
+
init_thinkingCapability();
|
|
1901
|
+
EFFORT_RANK = {
|
|
1902
|
+
low: 1,
|
|
1903
|
+
medium: 2,
|
|
1904
|
+
high: 3,
|
|
1905
|
+
xhigh: 4,
|
|
1906
|
+
max: 5
|
|
1725
1907
|
};
|
|
1726
1908
|
}
|
|
1727
1909
|
});
|
|
@@ -1914,14 +2096,14 @@ var init_providerConfig = __esm({
|
|
|
1914
2096
|
DEFAULTS = {
|
|
1915
2097
|
activeProviderId: "openai-compatible",
|
|
1916
2098
|
modelByProvider: {
|
|
1917
|
-
// grok-4.
|
|
1918
|
-
"openai-compatible": "grok-4.
|
|
2099
|
+
// grok-4.6: flagship; native reasoning_effort includes xhigh
|
|
2100
|
+
"openai-compatible": "grok-4.6",
|
|
1919
2101
|
"minimax": "MiniMax-M2.5",
|
|
1920
2102
|
"glm": "glm-4.6",
|
|
1921
|
-
"grok": "grok-4.
|
|
2103
|
+
"grok": "grok-4.6",
|
|
1922
2104
|
"deepseek": "deepseek-v4-pro",
|
|
1923
|
-
"chatgpt": "gpt-5.
|
|
1924
|
-
"anthropic": "claude-sonnet-4-
|
|
2105
|
+
"chatgpt": "gpt-5.6-codex",
|
|
2106
|
+
"anthropic": "claude-sonnet-4-6",
|
|
1925
2107
|
"custom": ""
|
|
1926
2108
|
},
|
|
1927
2109
|
thinkingByProvider: {
|
|
@@ -1948,6 +2130,7 @@ __export(modelDiscovery_exports, {
|
|
|
1948
2130
|
getCachedModels: () => getCachedModels,
|
|
1949
2131
|
getDiscoveredModelIds: () => getDiscoveredModelIds,
|
|
1950
2132
|
getModelsFilePath: () => getModelsFilePath,
|
|
2133
|
+
getStaticFallbackModels: () => getStaticFallbackModels,
|
|
1951
2134
|
isModelsCacheStale: () => isModelsCacheStale,
|
|
1952
2135
|
loadModelsRegistry: () => loadModelsRegistry,
|
|
1953
2136
|
pickDefaultModel: () => pickDefaultModel
|
|
@@ -1955,6 +2138,9 @@ __export(modelDiscovery_exports, {
|
|
|
1955
2138
|
import { promises as fs3, existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
1956
2139
|
import { homedir } from "node:os";
|
|
1957
2140
|
import path5 from "node:path";
|
|
2141
|
+
function getStaticFallbackModels(provider) {
|
|
2142
|
+
return STATIC_FALLBACKS[provider] ? [...STATIC_FALLBACKS[provider]] : [];
|
|
2143
|
+
}
|
|
1958
2144
|
async function resolveDiscoveryBaseUrl(provider, options) {
|
|
1959
2145
|
if (options.baseUrl) return options.baseUrl;
|
|
1960
2146
|
const { getCustomEndpoint: getCustomEndpoint2 } = await Promise.resolve().then(() => (init_providerConfig(), providerConfig_exports));
|
|
@@ -2090,10 +2276,6 @@ async function discoverModelsForProvider(provider, options = {}) {
|
|
|
2090
2276
|
const headers2 = await resolveDiscoveryHeaders(provider, authToken);
|
|
2091
2277
|
response = await fetchImpl(url2, { method: "GET", headers: headers2 });
|
|
2092
2278
|
} catch (err) {
|
|
2093
|
-
const fallback = STATIC_FALLBACKS[provider];
|
|
2094
|
-
if (fallback) {
|
|
2095
|
-
return cacheFallback(provider, baseUrl, fallback, options, `network: ${err instanceof Error ? err.message : String(err)}`);
|
|
2096
|
-
}
|
|
2097
2279
|
throw new ModelDiscoveryError(
|
|
2098
2280
|
`Network error contacting ${url2}: ${err instanceof Error ? err.message : String(err)}`,
|
|
2099
2281
|
"network_error"
|
|
@@ -2101,10 +2283,6 @@ async function discoverModelsForProvider(provider, options = {}) {
|
|
|
2101
2283
|
}
|
|
2102
2284
|
if (!response.ok) {
|
|
2103
2285
|
const body = await response.text().catch(() => "");
|
|
2104
|
-
const fallback = STATIC_FALLBACKS[provider];
|
|
2105
|
-
if (fallback) {
|
|
2106
|
-
return cacheFallback(provider, baseUrl, fallback, options, `HTTP ${response.status}: ${body.slice(0, 80)}`);
|
|
2107
|
-
}
|
|
2108
2286
|
throw new ModelDiscoveryError(
|
|
2109
2287
|
`HTTP ${response.status} from ${url2}: ${body.slice(0, 200)}`,
|
|
2110
2288
|
`http_${response.status}`
|
|
@@ -2114,10 +2292,6 @@ async function discoverModelsForProvider(provider, options = {}) {
|
|
|
2114
2292
|
try {
|
|
2115
2293
|
json2 = await response.json();
|
|
2116
2294
|
} catch (err) {
|
|
2117
|
-
const fallback = STATIC_FALLBACKS[provider];
|
|
2118
|
-
if (fallback) {
|
|
2119
|
-
return cacheFallback(provider, baseUrl, fallback, options, "invalid_json");
|
|
2120
|
-
}
|
|
2121
2295
|
throw new ModelDiscoveryError(
|
|
2122
2296
|
`Invalid JSON from ${url2}: ${err instanceof Error ? err.message : String(err)}`,
|
|
2123
2297
|
"invalid_json"
|
|
@@ -2125,10 +2299,6 @@ async function discoverModelsForProvider(provider, options = {}) {
|
|
|
2125
2299
|
}
|
|
2126
2300
|
const models = provider === "anthropic" ? parseAnthropicModelsResponse(json2) : parseOpenAIModelsResponse(json2, baseUrl);
|
|
2127
2301
|
if (models.length === 0) {
|
|
2128
|
-
const fallback = STATIC_FALLBACKS[provider];
|
|
2129
|
-
if (fallback) {
|
|
2130
|
-
return cacheFallback(provider, baseUrl, fallback, options, "empty_response");
|
|
2131
|
-
}
|
|
2132
2302
|
throw new ModelDiscoveryError(
|
|
2133
2303
|
`Provider ${provider} returned 0 models \u2014 refusing to overwrite cache`,
|
|
2134
2304
|
"empty_response"
|
|
@@ -2148,22 +2318,6 @@ async function discoverModelsForProvider(provider, options = {}) {
|
|
|
2148
2318
|
}
|
|
2149
2319
|
return entry;
|
|
2150
2320
|
}
|
|
2151
|
-
async function cacheFallback(provider, baseUrl, models, options, lastError) {
|
|
2152
|
-
const entry = {
|
|
2153
|
-
models,
|
|
2154
|
-
fetchedAt: Date.now(),
|
|
2155
|
-
baseUrl,
|
|
2156
|
-
lastError
|
|
2157
|
-
};
|
|
2158
|
-
if (!options.skipCacheWrite) {
|
|
2159
|
-
const file2 = getModelsFilePath();
|
|
2160
|
-
await readModifyWriteRegistry((current) => {
|
|
2161
|
-
current[provider] = entry;
|
|
2162
|
-
return current;
|
|
2163
|
-
}, file2);
|
|
2164
|
-
}
|
|
2165
|
-
return entry;
|
|
2166
|
-
}
|
|
2167
2321
|
function discoverModelsInBackground(provider, options = {}) {
|
|
2168
2322
|
discoverModelsForProvider(provider, options).catch((err) => {
|
|
2169
2323
|
if (err instanceof ModelDiscoveryError && options.onError) {
|
|
@@ -2201,6 +2355,9 @@ var init_modelDiscovery = __esm({
|
|
|
2201
2355
|
};
|
|
2202
2356
|
STATIC_FALLBACKS = {
|
|
2203
2357
|
chatgpt: [
|
|
2358
|
+
{ id: "gpt-5.6-codex", displayName: "GPT-5.6 Codex" },
|
|
2359
|
+
{ id: "gpt-5.6", displayName: "GPT-5.6" },
|
|
2360
|
+
{ id: "gpt-5.4", displayName: "GPT-5.4" },
|
|
2204
2361
|
{ id: "gpt-5.2-codex", displayName: "GPT-5.2 Codex" },
|
|
2205
2362
|
{ id: "gpt-5.2", displayName: "GPT-5.2" },
|
|
2206
2363
|
{ id: "gpt-5.1-codex", displayName: "GPT-5.1 Codex" },
|
|
@@ -2209,9 +2366,19 @@ var init_modelDiscovery = __esm({
|
|
|
2209
2366
|
{ id: "o4-mini", displayName: "o4-mini" }
|
|
2210
2367
|
],
|
|
2211
2368
|
anthropic: [
|
|
2369
|
+
{ id: "claude-opus-4-7", displayName: "Claude Opus 4.7" },
|
|
2212
2370
|
{ id: "claude-opus-4-6", displayName: "Claude Opus 4.6" },
|
|
2371
|
+
{ id: "claude-sonnet-4-6", displayName: "Claude Sonnet 4.6" },
|
|
2213
2372
|
{ id: "claude-sonnet-4-5", displayName: "Claude Sonnet 4.5" },
|
|
2214
2373
|
{ id: "claude-haiku-4-5", displayName: "Claude Haiku 4.5" }
|
|
2374
|
+
],
|
|
2375
|
+
grok: [
|
|
2376
|
+
{ id: "grok-4.6", displayName: "Grok 4.6" },
|
|
2377
|
+
{ id: "grok-4.5", displayName: "Grok 4.5" }
|
|
2378
|
+
],
|
|
2379
|
+
glm: [
|
|
2380
|
+
{ id: "glm-5.3", displayName: "GLM-5.3" },
|
|
2381
|
+
{ id: "glm-4.6", displayName: "GLM-4.6" }
|
|
2215
2382
|
]
|
|
2216
2383
|
};
|
|
2217
2384
|
writeChain = Promise.resolve();
|
|
@@ -21047,6 +21214,79 @@ var init_types = __esm({
|
|
|
21047
21214
|
}
|
|
21048
21215
|
});
|
|
21049
21216
|
|
|
21217
|
+
// packages/core/dist/core/requestSnapshot.js
|
|
21218
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
21219
|
+
function stableStringify(value) {
|
|
21220
|
+
if (value === null || typeof value !== "object")
|
|
21221
|
+
return JSON.stringify(value) ?? "null";
|
|
21222
|
+
if (Array.isArray(value)) {
|
|
21223
|
+
const items = value.map((v) => stableStringify(v));
|
|
21224
|
+
return `[${items.join(",")}]`;
|
|
21225
|
+
}
|
|
21226
|
+
const obj = value;
|
|
21227
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
21228
|
+
const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
|
|
21229
|
+
return `{${parts.join(",")}}`;
|
|
21230
|
+
}
|
|
21231
|
+
function sha256Hex(input) {
|
|
21232
|
+
return createHash2("sha256").update(input, "utf8").digest("hex").slice(0, 32);
|
|
21233
|
+
}
|
|
21234
|
+
function cloneMessages(messages) {
|
|
21235
|
+
return structuredClone(messages);
|
|
21236
|
+
}
|
|
21237
|
+
function canonicalTools(tools) {
|
|
21238
|
+
return [...tools].sort((a, b) => a.name.localeCompare(b.name));
|
|
21239
|
+
}
|
|
21240
|
+
function createRoutedRequestSnapshot(params) {
|
|
21241
|
+
let split = 0;
|
|
21242
|
+
while (split < params.messages.length && params.messages[split].role === "system") {
|
|
21243
|
+
split++;
|
|
21244
|
+
}
|
|
21245
|
+
const systemMessages = cloneMessages(params.messages.slice(0, split));
|
|
21246
|
+
const conversation = cloneMessages(params.messages.slice(split));
|
|
21247
|
+
const tools = canonicalTools(params.tools).map((t) => structuredClone(t));
|
|
21248
|
+
const header = stableStringify({
|
|
21249
|
+
provider: params.provider,
|
|
21250
|
+
model: params.model,
|
|
21251
|
+
systemMessages,
|
|
21252
|
+
tools
|
|
21253
|
+
});
|
|
21254
|
+
const request = stableStringify({
|
|
21255
|
+
provider: params.provider,
|
|
21256
|
+
model: params.model,
|
|
21257
|
+
systemMessages,
|
|
21258
|
+
tools,
|
|
21259
|
+
conversation
|
|
21260
|
+
});
|
|
21261
|
+
return {
|
|
21262
|
+
provider: params.provider,
|
|
21263
|
+
model: params.model,
|
|
21264
|
+
systemMessages,
|
|
21265
|
+
conversation,
|
|
21266
|
+
tools,
|
|
21267
|
+
headerFingerprint: sha256Hex(header),
|
|
21268
|
+
requestFingerprint: sha256Hex(request),
|
|
21269
|
+
createdAt: Date.now()
|
|
21270
|
+
};
|
|
21271
|
+
}
|
|
21272
|
+
function compareReplayPrefix(snapshot, messages) {
|
|
21273
|
+
const base = snapshot.conversation;
|
|
21274
|
+
const n = Math.min(base.length, messages.length);
|
|
21275
|
+
let matching = 0;
|
|
21276
|
+
for (let i = 0; i < n; i++) {
|
|
21277
|
+
if (stableStringify(base[i]) !== stableStringify(messages[i])) {
|
|
21278
|
+
return { exact: false, matchingMessages: matching, mismatchIndex: i };
|
|
21279
|
+
}
|
|
21280
|
+
matching++;
|
|
21281
|
+
}
|
|
21282
|
+
return { exact: true, matchingMessages: matching };
|
|
21283
|
+
}
|
|
21284
|
+
var init_requestSnapshot = __esm({
|
|
21285
|
+
"packages/core/dist/core/requestSnapshot.js"() {
|
|
21286
|
+
"use strict";
|
|
21287
|
+
}
|
|
21288
|
+
});
|
|
21289
|
+
|
|
21050
21290
|
// packages/core/dist/core/textLoopDetect.js
|
|
21051
21291
|
function isStatusTheaterUnit(unit) {
|
|
21052
21292
|
const u = normalizeLoopUnit(unit).toLowerCase();
|
|
@@ -21245,10 +21485,10 @@ var init_textLoopDetect = __esm({
|
|
|
21245
21485
|
|
|
21246
21486
|
// packages/core/dist/core/AgentHarness.js
|
|
21247
21487
|
function hashToolCall(toolName, args) {
|
|
21248
|
-
const canonical =
|
|
21488
|
+
const canonical = stableStringify2(args);
|
|
21249
21489
|
return `${toolName}::${canonical}`;
|
|
21250
21490
|
}
|
|
21251
|
-
function
|
|
21491
|
+
function stableStringify2(value) {
|
|
21252
21492
|
return JSON.stringify(value, (_k, v) => {
|
|
21253
21493
|
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
21254
21494
|
const sorted = {};
|
|
@@ -21481,6 +21721,7 @@ var init_AgentHarness = __esm({
|
|
|
21481
21721
|
"packages/core/dist/core/AgentHarness.js"() {
|
|
21482
21722
|
"use strict";
|
|
21483
21723
|
init_events();
|
|
21724
|
+
init_requestSnapshot();
|
|
21484
21725
|
init_textLoopDetect();
|
|
21485
21726
|
init_textLoopDetect();
|
|
21486
21727
|
AgentHarness = class {
|
|
@@ -21917,6 +22158,23 @@ ${shared2.content}`,
|
|
|
21917
22158
|
yield agentEnd;
|
|
21918
22159
|
this.activeController = null;
|
|
21919
22160
|
}
|
|
22161
|
+
/**
|
|
22162
|
+
* v1.36.0: capture a deterministic snapshot of the routed request just
|
|
22163
|
+
* before it goes out. Never throws into the request path.
|
|
22164
|
+
*/
|
|
22165
|
+
emitSnapshot(tools, generation) {
|
|
22166
|
+
if (!this.config.onRequestSnapshot)
|
|
22167
|
+
return;
|
|
22168
|
+
try {
|
|
22169
|
+
this.config.onRequestSnapshot(createRoutedRequestSnapshot({
|
|
22170
|
+
messages: this.config.messages,
|
|
22171
|
+
model: this.config.model,
|
|
22172
|
+
provider: this.config.provider,
|
|
22173
|
+
tools
|
|
22174
|
+
}), generation);
|
|
22175
|
+
} catch {
|
|
22176
|
+
}
|
|
22177
|
+
}
|
|
21920
22178
|
/**
|
|
21921
22179
|
* Run a single provider turn for the current message buffer.
|
|
21922
22180
|
* Streams from the provider, dispatches deltas to events, executes
|
|
@@ -21934,6 +22192,7 @@ ${shared2.content}`,
|
|
|
21934
22192
|
*/
|
|
21935
22193
|
async *runSingleTurn(messageId, finishRef, usageRef) {
|
|
21936
22194
|
try {
|
|
22195
|
+
this.emitSnapshot(this.config.tools);
|
|
21937
22196
|
const stream = this.config.providerStream({
|
|
21938
22197
|
messages: this.config.messages,
|
|
21939
22198
|
model: this.config.model,
|
|
@@ -22224,6 +22483,7 @@ ${cached2}`
|
|
|
22224
22483
|
const finishRef = { value: "stop" };
|
|
22225
22484
|
const usageRef = { value: null };
|
|
22226
22485
|
try {
|
|
22486
|
+
this.emitSnapshot([]);
|
|
22227
22487
|
const stream = this.config.providerStream({
|
|
22228
22488
|
messages: this.config.messages,
|
|
22229
22489
|
model: this.config.model,
|
|
@@ -22804,7 +23064,10 @@ __export(harness_exports, {
|
|
|
22804
23064
|
SessionJsonlWriter: () => SessionJsonlWriter,
|
|
22805
23065
|
TEXT_LOOP_RECOVERY_SYSTEM: () => TEXT_LOOP_RECOVERY_SYSTEM,
|
|
22806
23066
|
TEXT_LOOP_RECOVERY_USER_PROMPT: () => TEXT_LOOP_RECOVERY_USER_PROMPT,
|
|
23067
|
+
canonicalTools: () => canonicalTools,
|
|
22807
23068
|
collapseLoopedAssistantText: () => collapseLoopedAssistantText,
|
|
23069
|
+
compareReplayPrefix: () => compareReplayPrefix,
|
|
23070
|
+
createRoutedRequestSnapshot: () => createRoutedRequestSnapshot,
|
|
22808
23071
|
detectAssistantTextLoop: () => detectAssistantTextLoop,
|
|
22809
23072
|
detectAssistantTextLoopWindow: () => detectAssistantTextLoopWindow,
|
|
22810
23073
|
hashToolCall: () => hashToolCall,
|
|
@@ -22816,6 +23079,8 @@ __export(harness_exports, {
|
|
|
22816
23079
|
parseMinimaxStyleToolCalls: () => parseMinimaxStyleToolCalls,
|
|
22817
23080
|
parseTextToolCalls: () => parseTextToolCalls,
|
|
22818
23081
|
readSession: () => readSession,
|
|
23082
|
+
sha256Hex: () => sha256Hex,
|
|
23083
|
+
stableStringify: () => stableStringify,
|
|
22819
23084
|
toolMatches: () => toolMatches,
|
|
22820
23085
|
wrapLegacyStream: () => wrapLegacyStream
|
|
22821
23086
|
});
|
|
@@ -22824,6 +23089,7 @@ var init_harness = __esm({
|
|
|
22824
23089
|
"use strict";
|
|
22825
23090
|
init_AgentHarness();
|
|
22826
23091
|
init_providerStream();
|
|
23092
|
+
init_requestSnapshot();
|
|
22827
23093
|
init_sessionJsonl();
|
|
22828
23094
|
init_hooks();
|
|
22829
23095
|
}
|
|
@@ -27735,6 +28001,7 @@ __export(dist_exports, {
|
|
|
27735
28001
|
buildSystemPrompt: () => buildSystemPrompt,
|
|
27736
28002
|
buildSystemPromptSplit: () => buildSystemPromptSplit,
|
|
27737
28003
|
canRunParallel: () => canRunParallel,
|
|
28004
|
+
canonicalTools: () => canonicalTools,
|
|
27738
28005
|
captureFailure: () => captureFailure,
|
|
27739
28006
|
checkImplementationCompletion: () => checkImplementationCompletion,
|
|
27740
28007
|
checkImplementationDelivery: () => checkImplementationDelivery,
|
|
@@ -27746,6 +28013,7 @@ __export(dist_exports, {
|
|
|
27746
28013
|
clearCustomTools: () => clearCustomTools,
|
|
27747
28014
|
cliToolToEnhanced: () => cliToolToEnhanced,
|
|
27748
28015
|
collapseLoopedAssistantText: () => collapseLoopedAssistantText,
|
|
28016
|
+
compareReplayPrefix: () => compareReplayPrefix,
|
|
27749
28017
|
computeAgentSkills: () => computeAgentSkills,
|
|
27750
28018
|
computeAgentTools: () => computeAgentTools,
|
|
27751
28019
|
councilModeBanner: () => councilModeBanner,
|
|
@@ -27755,6 +28023,7 @@ __export(dist_exports, {
|
|
|
27755
28023
|
createBrainEvent: () => createBrainEvent,
|
|
27756
28024
|
createDefaultSystemPromptConfig: () => createDefaultSystemPromptConfig,
|
|
27757
28025
|
createGraph: () => createGraph,
|
|
28026
|
+
createRoutedRequestSnapshot: () => createRoutedRequestSnapshot,
|
|
27758
28027
|
defaultPersonaParse: () => defaultPersonaParse,
|
|
27759
28028
|
detectAssistantTextLoop: () => detectAssistantTextLoop,
|
|
27760
28029
|
detectAssistantTextLoopWindow: () => detectAssistantTextLoopWindow,
|
|
@@ -27871,9 +28140,11 @@ __export(dist_exports, {
|
|
|
27871
28140
|
scrubProprietaryLeak: () => scrubProprietaryLeak,
|
|
27872
28141
|
selectParallelWave: () => selectParallelWave,
|
|
27873
28142
|
setWorkspaceStubs: () => setWorkspaceStubs,
|
|
28143
|
+
sha256Hex: () => sha256Hex,
|
|
27874
28144
|
shouldRetryMember: () => shouldRetryMember,
|
|
27875
28145
|
slugify: () => slugify2,
|
|
27876
28146
|
specificityFromAssumptions: () => specificityFromAssumptions,
|
|
28147
|
+
stableStringify: () => stableStringify,
|
|
27877
28148
|
stripClarificationProtocol: () => stripClarificationProtocol,
|
|
27878
28149
|
swapMembers: () => swapMembers,
|
|
27879
28150
|
systemMessagesFromSplit: () => systemMessagesFromSplit,
|
|
@@ -28236,6 +28507,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
28236
28507
|
if (cacheable) messageMappingCache.set(m, mapped);
|
|
28237
28508
|
return mapped;
|
|
28238
28509
|
});
|
|
28510
|
+
const generation = params.generation;
|
|
28239
28511
|
const body = {
|
|
28240
28512
|
// Use `params.model` (per-call override from AgentHarness, e.g. for
|
|
28241
28513
|
// `agentModels` config) rather than the closed-over `config.model`
|
|
@@ -28243,7 +28515,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
28243
28515
|
model: params.model,
|
|
28244
28516
|
messages,
|
|
28245
28517
|
stream: true,
|
|
28246
|
-
temperature: 0.7,
|
|
28518
|
+
temperature: generation?.temperature ?? 0.7,
|
|
28247
28519
|
// Task G.4.2 — request the provider to send real token usage in
|
|
28248
28520
|
// the final chunk (gated by `stream_options.include_usage` on the
|
|
28249
28521
|
// OpenAI-compatible API). Providers that don't honor this (some
|
|
@@ -28251,13 +28523,16 @@ function openaiCompatibleProvider(config2) {
|
|
|
28251
28523
|
// the harness will fall back to the ~4-char/token approximation.
|
|
28252
28524
|
stream_options: { include_usage: true }
|
|
28253
28525
|
};
|
|
28526
|
+
if (typeof generation?.maxTokens === "number" && generation.maxTokens > 0) {
|
|
28527
|
+
body.max_tokens = generation.maxTokens;
|
|
28528
|
+
}
|
|
28254
28529
|
const thinkingSpec = config2.thinking ?? "auto";
|
|
28255
28530
|
if (config2.providerId === "deepseek" && thinkingSpec === "auto") {
|
|
28256
28531
|
const thinking = resolveDeepSeekThinking();
|
|
28257
28532
|
if (thinking.thinking) body.thinking = { type: thinking.thinking };
|
|
28258
28533
|
if (thinking.reasoningEffort) body.reasoning_effort = thinking.reasoningEffort;
|
|
28259
28534
|
} else if (thinkingSpec !== "auto") {
|
|
28260
|
-
const t = translateOpenAiCompatibleThinking(config2.providerId, thinkingSpec);
|
|
28535
|
+
const t = translateOpenAiCompatibleThinking(config2.providerId, thinkingSpec, config2.model);
|
|
28261
28536
|
if (t.degraded) {
|
|
28262
28537
|
console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
|
|
28263
28538
|
} else {
|
|
@@ -28791,7 +29066,7 @@ function anthropicMessagesProvider(config2) {
|
|
|
28791
29066
|
}
|
|
28792
29067
|
const thinkingSpec = config2.thinking ?? "auto";
|
|
28793
29068
|
if (thinkingSpec !== "auto") {
|
|
28794
|
-
const t = translateAnthropicThinking(thinkingSpec);
|
|
29069
|
+
const t = translateAnthropicThinking(thinkingSpec, config2.model);
|
|
28795
29070
|
if (t.degraded) console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
|
|
28796
29071
|
else Object.assign(body, t.patch);
|
|
28797
29072
|
}
|
|
@@ -29003,7 +29278,7 @@ function chatgptResponsesProvider(config2) {
|
|
|
29003
29278
|
}
|
|
29004
29279
|
const thinkingSpec = config2.thinking ?? "auto";
|
|
29005
29280
|
if (thinkingSpec !== "auto") {
|
|
29006
|
-
const t = translateResponsesThinking(thinkingSpec);
|
|
29281
|
+
const t = translateResponsesThinking(thinkingSpec, config2.model);
|
|
29007
29282
|
if (t.degraded) console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
|
|
29008
29283
|
else Object.assign(body, t.patch);
|
|
29009
29284
|
}
|
|
@@ -29145,7 +29420,7 @@ var init_resolveStream = __esm({
|
|
|
29145
29420
|
});
|
|
29146
29421
|
|
|
29147
29422
|
// packages/core/dist/core/tools/toolOutputSpill.js
|
|
29148
|
-
import { createHash as
|
|
29423
|
+
import { createHash as createHash3, randomBytes as randomBytes2 } from "node:crypto";
|
|
29149
29424
|
import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
29150
29425
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
29151
29426
|
import { join as join11 } from "node:path";
|
|
@@ -29175,7 +29450,7 @@ function spillToolOutput(fullText, meta3) {
|
|
|
29175
29450
|
if (!existsSync12(dir)) {
|
|
29176
29451
|
mkdirSync7(dir, { recursive: true });
|
|
29177
29452
|
}
|
|
29178
|
-
const hash3 =
|
|
29453
|
+
const hash3 = createHash3("sha256").update(fullText).digest("hex").slice(0, 12);
|
|
29179
29454
|
const stamp = Date.now().toString(36);
|
|
29180
29455
|
const rnd = randomBytes2(3).toString("hex");
|
|
29181
29456
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
@@ -31582,9 +31857,9 @@ var init_store = __esm({
|
|
|
31582
31857
|
import { promises as fs12, existsSync as existsSync17, readFileSync as readFileSync17 } from "node:fs";
|
|
31583
31858
|
import { homedir as homedir5 } from "node:os";
|
|
31584
31859
|
import path23 from "node:path";
|
|
31585
|
-
import { createHash as
|
|
31860
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
31586
31861
|
function getIndexPath(root) {
|
|
31587
|
-
const hash3 =
|
|
31862
|
+
const hash3 = createHash4("sha1").update(path23.resolve(root)).digest("hex").slice(0, 16);
|
|
31588
31863
|
return process.env.ZELARI_SEMANTIC_FILE ?? path23.join(homedir5(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
|
|
31589
31864
|
}
|
|
31590
31865
|
async function collectSourceFiles(root, maxFiles = 1500) {
|
|
@@ -33065,11 +33340,11 @@ function readStore3() {
|
|
|
33065
33340
|
return DEFAULT_STORE;
|
|
33066
33341
|
}
|
|
33067
33342
|
}
|
|
33068
|
-
function writeStore3(
|
|
33343
|
+
function writeStore3(store4) {
|
|
33069
33344
|
const p3 = trustStorePath();
|
|
33070
33345
|
try {
|
|
33071
33346
|
mkdirSync11(path28.dirname(p3), { recursive: true });
|
|
33072
|
-
writeFileSync13(p3, JSON.stringify(
|
|
33347
|
+
writeFileSync13(p3, JSON.stringify(store4, null, 2), "utf8");
|
|
33073
33348
|
} catch (err) {
|
|
33074
33349
|
throw new Error(
|
|
33075
33350
|
`failed to persist trust store ${p3}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -33093,21 +33368,21 @@ function isFolderTrusted(folderPath) {
|
|
|
33093
33368
|
return readStore3().folders.some((f) => normalize(f.path) === target);
|
|
33094
33369
|
}
|
|
33095
33370
|
function trustFolder(folderPath) {
|
|
33096
|
-
const
|
|
33371
|
+
const store4 = readStore3();
|
|
33097
33372
|
const normalized = path28.resolve(folderPath);
|
|
33098
|
-
if (!
|
|
33099
|
-
|
|
33100
|
-
writeStore3(
|
|
33373
|
+
if (!store4.folders.some((f) => normalize(f.path) === normalize(normalized))) {
|
|
33374
|
+
store4.folders.push({ path: normalized, trustedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
33375
|
+
writeStore3(store4);
|
|
33101
33376
|
}
|
|
33102
33377
|
return { ok: true, path: normalized };
|
|
33103
33378
|
}
|
|
33104
33379
|
function untrustFolder(folderPath) {
|
|
33105
|
-
const
|
|
33380
|
+
const store4 = readStore3();
|
|
33106
33381
|
const target = normalize(folderPath);
|
|
33107
|
-
const before =
|
|
33108
|
-
|
|
33109
|
-
if (
|
|
33110
|
-
writeStore3(
|
|
33382
|
+
const before = store4.folders.length;
|
|
33383
|
+
store4.folders = store4.folders.filter((f) => normalize(f.path) !== target);
|
|
33384
|
+
if (store4.folders.length === before) return { ok: true, removed: false };
|
|
33385
|
+
writeStore3(store4);
|
|
33111
33386
|
return { ok: true, removed: true };
|
|
33112
33387
|
}
|
|
33113
33388
|
function listTrustedFolders() {
|
|
@@ -33208,7 +33483,7 @@ var init_lifecycleHooks = __esm({
|
|
|
33208
33483
|
});
|
|
33209
33484
|
|
|
33210
33485
|
// src/cli/toolResultCache.ts
|
|
33211
|
-
import { createHash as
|
|
33486
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
33212
33487
|
import { promises as fs14 } from "node:fs";
|
|
33213
33488
|
import path29 from "node:path";
|
|
33214
33489
|
function isToolCacheEnabled() {
|
|
@@ -33221,7 +33496,7 @@ function resolveToolCacheTtlMs() {
|
|
|
33221
33496
|
return Number.isFinite(n) && n >= 0 ? n : TOOL_CACHE_DEFAULT_TTL_MS;
|
|
33222
33497
|
}
|
|
33223
33498
|
function hashKey(parts) {
|
|
33224
|
-
return
|
|
33499
|
+
return createHash5("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
|
|
33225
33500
|
}
|
|
33226
33501
|
function resultBytes(result) {
|
|
33227
33502
|
try {
|
|
@@ -33902,7 +34177,7 @@ var init_toolRegistry = __esm({
|
|
|
33902
34177
|
});
|
|
33903
34178
|
|
|
33904
34179
|
// src/cli/state/fileStateStore.ts
|
|
33905
|
-
import { createHash as
|
|
34180
|
+
import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
|
|
33906
34181
|
import { promises as fs15 } from "node:fs";
|
|
33907
34182
|
import * as path30 from "node:path";
|
|
33908
34183
|
function shortId() {
|
|
@@ -33945,16 +34220,16 @@ function isStateEnabled(env = process.env) {
|
|
|
33945
34220
|
}
|
|
33946
34221
|
async function getStateStore(projectRoot, env = process.env) {
|
|
33947
34222
|
if (!isStateEnabled(env)) return new NoopDurableStateStore();
|
|
33948
|
-
const
|
|
34223
|
+
const store4 = new FileDurableStateStore();
|
|
33949
34224
|
try {
|
|
33950
|
-
await
|
|
33951
|
-
return
|
|
34225
|
+
await store4.init(projectRoot);
|
|
34226
|
+
return store4;
|
|
33952
34227
|
} catch {
|
|
33953
34228
|
return new NoopDurableStateStore();
|
|
33954
34229
|
}
|
|
33955
34230
|
}
|
|
33956
34231
|
function hashStablePrompt(stable) {
|
|
33957
|
-
return
|
|
34232
|
+
return createHash6("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
|
|
33958
34233
|
}
|
|
33959
34234
|
var DEFAULT_MATERIALIZE_CHARS, FileDurableStateStore, NoopDurableStateStore;
|
|
33960
34235
|
var init_fileStateStore = __esm({
|
|
@@ -34285,29 +34560,6 @@ function extractiveHistorySummary(dropped, opts) {
|
|
|
34285
34560
|
}
|
|
34286
34561
|
return out;
|
|
34287
34562
|
}
|
|
34288
|
-
function formatDroppedForLlm(dropped) {
|
|
34289
|
-
const lines = [];
|
|
34290
|
-
for (const m of dropped) {
|
|
34291
|
-
if (m.role === "user") {
|
|
34292
|
-
lines.push(`USER: ${oneLine(m.content, 400)}`);
|
|
34293
|
-
} else if (m.role === "assistant") {
|
|
34294
|
-
const tools = m.toolCalls?.map((t) => t.name).join(",") || "";
|
|
34295
|
-
const body2 = oneLine(m.content, 300);
|
|
34296
|
-
lines.push(
|
|
34297
|
-
tools ? `ASSISTANT(tools=${tools}): ${body2}` : `ASSISTANT: ${body2}`
|
|
34298
|
-
);
|
|
34299
|
-
} else if (m.role === "tool") {
|
|
34300
|
-
lines.push(`TOOL(${m.toolCallId ?? "?"}): ${oneLine(m.content, 160)}`);
|
|
34301
|
-
} else if (m.role === "system") {
|
|
34302
|
-
lines.push(`SYSTEM: ${oneLine(m.content, 200)}`);
|
|
34303
|
-
}
|
|
34304
|
-
}
|
|
34305
|
-
let body = lines.join("\n");
|
|
34306
|
-
if (body.length > MAX_LLM_INPUT_CHARS) {
|
|
34307
|
-
body = body.slice(body.length - MAX_LLM_INPUT_CHARS);
|
|
34308
|
-
}
|
|
34309
|
-
return body;
|
|
34310
|
-
}
|
|
34311
34563
|
function oneLine(s, max) {
|
|
34312
34564
|
const t = s.replace(/\s+/g, " ").trim();
|
|
34313
34565
|
if (t.length <= max) return t;
|
|
@@ -34333,12 +34585,11 @@ function collectPathsFromText(text, out) {
|
|
|
34333
34585
|
n += 1;
|
|
34334
34586
|
}
|
|
34335
34587
|
}
|
|
34336
|
-
var MAX_SUMMARY_CHARS
|
|
34588
|
+
var MAX_SUMMARY_CHARS;
|
|
34337
34589
|
var init_historySummary = __esm({
|
|
34338
34590
|
"src/cli/budget/historySummary.ts"() {
|
|
34339
34591
|
"use strict";
|
|
34340
34592
|
MAX_SUMMARY_CHARS = 3500;
|
|
34341
|
-
MAX_LLM_INPUT_CHARS = 24e3;
|
|
34342
34593
|
}
|
|
34343
34594
|
});
|
|
34344
34595
|
|
|
@@ -34348,92 +34599,87 @@ function isLlmCompactEnabled() {
|
|
|
34348
34599
|
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
34349
34600
|
return true;
|
|
34350
34601
|
}
|
|
34351
|
-
|
|
34352
|
-
|
|
34353
|
-
|
|
34354
|
-
|
|
34355
|
-
|
|
34356
|
-
|
|
34357
|
-
|
|
34358
|
-
|
|
34602
|
+
function compactModelOverride() {
|
|
34603
|
+
const v = process.env.ZELARI_COMPACT_MODEL?.trim();
|
|
34604
|
+
return v ? v : void 0;
|
|
34605
|
+
}
|
|
34606
|
+
async function llmSummarizeHistoryReplay(input) {
|
|
34607
|
+
const override = input.overrideModel ?? compactModelOverride();
|
|
34608
|
+
const model = override ?? input.model;
|
|
34609
|
+
const cacheReuseExpected = !override;
|
|
34610
|
+
if (!isLlmCompactEnabled()) return { summary: null, model, cacheReuseExpected };
|
|
34611
|
+
if (input.droppedMessages.length === 0) {
|
|
34612
|
+
return { summary: null, model, cacheReuseExpected };
|
|
34359
34613
|
}
|
|
34360
|
-
|
|
34361
|
-
|
|
34614
|
+
const messages = [
|
|
34615
|
+
...input.systemMessages,
|
|
34616
|
+
...input.droppedMessages,
|
|
34617
|
+
{
|
|
34618
|
+
role: "user",
|
|
34619
|
+
content: COMPACTION_INSTRUCTION
|
|
34620
|
+
}
|
|
34621
|
+
];
|
|
34362
34622
|
const controller = new AbortController();
|
|
34363
|
-
const timeout = setTimeout(() => controller.abort(),
|
|
34623
|
+
const timeout = setTimeout(() => controller.abort(), REPLAY_TIMEOUT_MS);
|
|
34364
34624
|
const onOuterAbort = () => controller.abort();
|
|
34365
34625
|
input.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
34366
34626
|
try {
|
|
34367
|
-
|
|
34368
|
-
|
|
34369
|
-
|
|
34627
|
+
let text = "";
|
|
34628
|
+
let emittedToolCall = false;
|
|
34629
|
+
for await (const delta of input.providerStream({
|
|
34630
|
+
provider: input.provider,
|
|
34631
|
+
model,
|
|
34632
|
+
messages,
|
|
34633
|
+
// Tools stay advertised: dropping them would change the prefix token
|
|
34634
|
+
// sequence and destroy cache reuse (explicit DSH decision). They are
|
|
34635
|
+
// sorted canonically (same discipline as the live routed request and
|
|
34636
|
+
// the snapshot fingerprints) so the replay prefix is byte-identical.
|
|
34637
|
+
tools: [...input.tools].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
|
|
34370
34638
|
signal: controller.signal,
|
|
34371
|
-
|
|
34372
|
-
|
|
34373
|
-
authorization: `Bearer ${config2.apiKey}`
|
|
34374
|
-
},
|
|
34375
|
-
body: JSON.stringify({
|
|
34376
|
-
model,
|
|
34639
|
+
generation: {
|
|
34640
|
+
purpose: "compaction",
|
|
34377
34641
|
temperature: 0.1,
|
|
34378
|
-
|
|
34379
|
-
|
|
34380
|
-
|
|
34381
|
-
|
|
34382
|
-
|
|
34383
|
-
|
|
34384
|
-
|
|
34385
|
-
|
|
34386
|
-
|
|
34387
|
-
Transcript of dropped turns:
|
|
34388
|
-
${input.droppedTranscript}`
|
|
34389
|
-
}
|
|
34390
|
-
]
|
|
34391
|
-
})
|
|
34392
|
-
});
|
|
34393
|
-
if (!res.ok) return null;
|
|
34394
|
-
const json2 = await res.json();
|
|
34395
|
-
const text = json2.choices?.[0]?.message?.content?.trim();
|
|
34396
|
-
if (!text) return null;
|
|
34397
|
-
return "[history-summary \xB7 llm]\n" + text + "\n\nContinue from the recent messages below; honor decisions already made above.";
|
|
34642
|
+
maxTokens: 900
|
|
34643
|
+
}
|
|
34644
|
+
})) {
|
|
34645
|
+
if (delta.kind === "text") text += delta.delta;
|
|
34646
|
+
if (delta.kind === "tool_call") emittedToolCall = true;
|
|
34647
|
+
}
|
|
34648
|
+
if (emittedToolCall) return { summary: null, model, cacheReuseExpected };
|
|
34649
|
+
if (!text.trim()) return { summary: null, model, cacheReuseExpected };
|
|
34650
|
+
return { summary: text.trim(), model, cacheReuseExpected };
|
|
34398
34651
|
} catch {
|
|
34399
|
-
return null;
|
|
34652
|
+
return { summary: null, model, cacheReuseExpected };
|
|
34400
34653
|
} finally {
|
|
34401
34654
|
clearTimeout(timeout);
|
|
34402
34655
|
input.signal?.removeEventListener("abort", onOuterAbort);
|
|
34403
34656
|
}
|
|
34404
34657
|
}
|
|
34405
|
-
|
|
34406
|
-
const active = getProviderConfig().activeProviderId;
|
|
34407
|
-
const meta3 = await resolveApiKeyWithMeta(active);
|
|
34408
|
-
const apiKey = meta3?.apiKey;
|
|
34409
|
-
if (!apiKey) return null;
|
|
34410
|
-
const custom2 = getCustomEndpoint(active);
|
|
34411
|
-
let baseUrl = custom2 || (active === "openai-compatible" || active === "custom" ? process.env.OPENAI_BASE_URL ?? PROVIDER_ENDPOINTS[active] : PROVIDER_ENDPOINTS[active]);
|
|
34412
|
-
if (!baseUrl) return null;
|
|
34413
|
-
const model = getModelForProvider(active);
|
|
34414
|
-
return {
|
|
34415
|
-
apiKey,
|
|
34416
|
-
baseUrl,
|
|
34417
|
-
model,
|
|
34418
|
-
providerId: active
|
|
34419
|
-
};
|
|
34420
|
-
}
|
|
34421
|
-
var COMPACT_SYSTEM;
|
|
34658
|
+
var COMPACTION_INSTRUCTION, REPLAY_TIMEOUT_MS;
|
|
34422
34659
|
var init_llmCompact = __esm({
|
|
34423
34660
|
"src/cli/budget/llmCompact.ts"() {
|
|
34424
34661
|
"use strict";
|
|
34425
|
-
|
|
34426
|
-
|
|
34427
|
-
init_openai_compatible();
|
|
34428
|
-
COMPACT_SYSTEM = `You compress earlier turns of a coding-agent session into a dense continuity brief.
|
|
34429
|
-
Output plain text (no markdown fences) with these sections:
|
|
34430
|
-
1) Goal \u2014 what the user wants
|
|
34431
|
-
2) Decisions \u2014 choices already made
|
|
34432
|
-
3) Done \u2014 completed work / files changed
|
|
34433
|
-
4) Open \u2014 remaining tasks / blockers
|
|
34434
|
-
5) Constraints \u2014 important rules the agent must keep
|
|
34662
|
+
COMPACTION_INSTRUCTION = `
|
|
34663
|
+
You are now acting as a compaction engine for this coding-agent session.
|
|
34435
34664
|
|
|
34436
|
-
|
|
34665
|
+
Condense the conversation ABOVE into a compact checkpoint sufficient to continue the task.
|
|
34666
|
+
|
|
34667
|
+
Preserve:
|
|
34668
|
+
- user's goal and evolving intent
|
|
34669
|
+
- decisions already made
|
|
34670
|
+
- exact file paths and identifiers
|
|
34671
|
+
- code changes already completed
|
|
34672
|
+
- commands/errors that still matter
|
|
34673
|
+
- constraints
|
|
34674
|
+
- unfinished work
|
|
34675
|
+
- the single most likely next action
|
|
34676
|
+
|
|
34677
|
+
Do not call tools.
|
|
34678
|
+
Do not mention this summarization request.
|
|
34679
|
+
Output only the checkpoint.
|
|
34680
|
+
Be concise.
|
|
34681
|
+
`.trim();
|
|
34682
|
+
REPLAY_TIMEOUT_MS = 6e4;
|
|
34437
34683
|
}
|
|
34438
34684
|
});
|
|
34439
34685
|
|
|
@@ -34472,6 +34718,7 @@ function resolveMaxMessages(opts) {
|
|
|
34472
34718
|
turns = Math.min(turns, 3);
|
|
34473
34719
|
}
|
|
34474
34720
|
if (turns <= 0) return 0;
|
|
34721
|
+
if (opts?.force && opts?.maxMessages) return Math.max(1, opts.maxMessages);
|
|
34475
34722
|
return turns * 4;
|
|
34476
34723
|
}
|
|
34477
34724
|
function findValidCutIndex(messages, naiveCut) {
|
|
@@ -34537,12 +34784,18 @@ function pruneToolResultsDetailed(messages, opts) {
|
|
|
34537
34784
|
function compactHistory(messages, opts) {
|
|
34538
34785
|
return compactHistoryDetailed(messages, opts).messages;
|
|
34539
34786
|
}
|
|
34787
|
+
function buildCheckpointMessage(summaryText) {
|
|
34788
|
+
return {
|
|
34789
|
+
role: "user",
|
|
34790
|
+
content: CHECKPOINT_WRAPPER_PREFIX + "\n\n<compacted-summary>\n" + summaryText + "\n</compacted-summary>"
|
|
34791
|
+
};
|
|
34792
|
+
}
|
|
34540
34793
|
function compactHistoryDetailed(messages, opts) {
|
|
34541
34794
|
const maxMessages = resolveMaxMessages(opts);
|
|
34542
34795
|
if (maxMessages === 0) {
|
|
34543
34796
|
return { messages: [], compacted: true, messagesRemoved: messages.length, summary: "" };
|
|
34544
34797
|
}
|
|
34545
|
-
if (messages.length <= maxMessages * 2) {
|
|
34798
|
+
if (messages.length <= maxMessages * 2 && !opts?.force) {
|
|
34546
34799
|
return {
|
|
34547
34800
|
messages,
|
|
34548
34801
|
compacted: false,
|
|
@@ -34550,7 +34803,7 @@ function compactHistoryDetailed(messages, opts) {
|
|
|
34550
34803
|
summary: ""
|
|
34551
34804
|
};
|
|
34552
34805
|
}
|
|
34553
|
-
const naiveCut = messages.length - maxMessages;
|
|
34806
|
+
const naiveCut = Math.max(0, messages.length - maxMessages);
|
|
34554
34807
|
const cut = findValidCutIndex(messages, naiveCut);
|
|
34555
34808
|
if (cut === 0) {
|
|
34556
34809
|
return {
|
|
@@ -34564,10 +34817,9 @@ function compactHistoryDetailed(messages, opts) {
|
|
|
34564
34817
|
const pruned = pruneToolResultsDetailed(messages.slice(cut));
|
|
34565
34818
|
const kept = pruned.messages;
|
|
34566
34819
|
const summaryText = extractiveHistorySummary(droppedMsgs);
|
|
34567
|
-
const summary =
|
|
34568
|
-
|
|
34569
|
-
|
|
34570
|
-
};
|
|
34820
|
+
const summary = buildCheckpointMessage(
|
|
34821
|
+
summaryText || `${COMPACT_MARKER} ${cut} earlier message(s) dropped.`
|
|
34822
|
+
);
|
|
34571
34823
|
return {
|
|
34572
34824
|
messages: [summary, ...kept],
|
|
34573
34825
|
compacted: true,
|
|
@@ -34582,29 +34834,58 @@ async function compactHistoryAsync(messages, opts) {
|
|
|
34582
34834
|
const cut = base.messagesRemoved;
|
|
34583
34835
|
const droppedMsgs = messages.slice(0, cut);
|
|
34584
34836
|
const extractive = extractiveHistorySummary(droppedMsgs);
|
|
34585
|
-
const droppedTranscript = formatDroppedForLlm(droppedMsgs);
|
|
34586
34837
|
let summaryText = extractive;
|
|
34587
|
-
|
|
34588
|
-
|
|
34589
|
-
|
|
34590
|
-
|
|
34591
|
-
|
|
34592
|
-
|
|
34593
|
-
|
|
34594
|
-
|
|
34838
|
+
let cacheReuseExpected;
|
|
34839
|
+
let replayExactPrefix;
|
|
34840
|
+
const canReplay = !!(opts?.providerStream && opts?.requestSnapshot);
|
|
34841
|
+
if (canReplay) {
|
|
34842
|
+
try {
|
|
34843
|
+
const replay = await llmSummarizeHistoryReplay({
|
|
34844
|
+
providerStream: opts.providerStream,
|
|
34845
|
+
provider: opts.requestSnapshot.provider,
|
|
34846
|
+
model: opts.requestSnapshot.model,
|
|
34847
|
+
systemMessages: opts.requestSnapshot.systemMessages,
|
|
34848
|
+
tools: opts.requestSnapshot.tools,
|
|
34849
|
+
droppedMessages: droppedMsgs,
|
|
34850
|
+
signal: opts?.signal
|
|
34851
|
+
});
|
|
34852
|
+
cacheReuseExpected = replay.cacheReuseExpected;
|
|
34853
|
+
if (replay.summary && replay.summary.trim().length > 40) {
|
|
34854
|
+
const sourceTokens = roughTokens(droppedMsgs);
|
|
34855
|
+
const summaryTok = Math.ceil(replay.summary.length / 4);
|
|
34856
|
+
if (summaryTok < sourceTokens) {
|
|
34857
|
+
summaryText = replay.summary.trim();
|
|
34858
|
+
}
|
|
34859
|
+
}
|
|
34860
|
+
} catch {
|
|
34861
|
+
}
|
|
34595
34862
|
}
|
|
34596
34863
|
const pruned = pruneToolResultsDetailed(messages.slice(cut));
|
|
34597
34864
|
const kept = pruned.messages;
|
|
34598
|
-
const summary =
|
|
34865
|
+
const summary = buildCheckpointMessage(summaryText);
|
|
34599
34866
|
return {
|
|
34600
34867
|
messages: [summary, ...kept],
|
|
34601
34868
|
compacted: true,
|
|
34602
34869
|
messagesRemoved: cut,
|
|
34603
34870
|
summary: summaryText,
|
|
34604
|
-
prunedToolResults: pruned.stats.pruned
|
|
34871
|
+
prunedToolResults: pruned.stats.pruned,
|
|
34872
|
+
cacheReuseExpected,
|
|
34873
|
+
replayExactPrefix
|
|
34605
34874
|
};
|
|
34606
34875
|
}
|
|
34607
|
-
|
|
34876
|
+
function roughTokens(msgs) {
|
|
34877
|
+
let n = 0;
|
|
34878
|
+
for (const m of msgs) {
|
|
34879
|
+
n += Math.ceil((m.content ?? "").length / 4);
|
|
34880
|
+
if (m.toolCalls) {
|
|
34881
|
+
for (const tc of m.toolCalls) {
|
|
34882
|
+
n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
|
|
34883
|
+
}
|
|
34884
|
+
}
|
|
34885
|
+
}
|
|
34886
|
+
return Math.max(1, n);
|
|
34887
|
+
}
|
|
34888
|
+
var COMPACT_MARKER, CHECKPOINT_WRAPPER_PREFIX;
|
|
34608
34889
|
var init_historyCompaction = __esm({
|
|
34609
34890
|
"src/cli/hooks/historyCompaction.ts"() {
|
|
34610
34891
|
"use strict";
|
|
@@ -34612,6 +34893,30 @@ var init_historyCompaction = __esm({
|
|
|
34612
34893
|
init_llmCompact();
|
|
34613
34894
|
init_envNumber();
|
|
34614
34895
|
COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
34896
|
+
CHECKPOINT_WRAPPER_PREFIX = "This is an automatically generated checkpoint of earlier conversation. Treat it as established context and continue directly.";
|
|
34897
|
+
}
|
|
34898
|
+
});
|
|
34899
|
+
|
|
34900
|
+
// src/cli/budget/requestSnapshotStore.ts
|
|
34901
|
+
function recordRequestSnapshot(sessionId, snapshot) {
|
|
34902
|
+
store3.set(sessionId, { snapshot });
|
|
34903
|
+
}
|
|
34904
|
+
function recordRequestUsage(sessionId, usage) {
|
|
34905
|
+
const entry = store3.get(sessionId);
|
|
34906
|
+
if (!entry) return;
|
|
34907
|
+
entry.usage = usage;
|
|
34908
|
+
}
|
|
34909
|
+
function getRequestSnapshotWithUsage(sessionId) {
|
|
34910
|
+
return store3.get(sessionId) ?? null;
|
|
34911
|
+
}
|
|
34912
|
+
function clearAllRequestSnapshots() {
|
|
34913
|
+
store3.clear();
|
|
34914
|
+
}
|
|
34915
|
+
var store3;
|
|
34916
|
+
var init_requestSnapshotStore = __esm({
|
|
34917
|
+
"src/cli/budget/requestSnapshotStore.ts"() {
|
|
34918
|
+
"use strict";
|
|
34919
|
+
store3 = /* @__PURE__ */ new Map();
|
|
34615
34920
|
}
|
|
34616
34921
|
});
|
|
34617
34922
|
|
|
@@ -34656,6 +34961,7 @@ function appendMessages(msgs) {
|
|
|
34656
34961
|
function clearHistory() {
|
|
34657
34962
|
history = [];
|
|
34658
34963
|
lastClarification = null;
|
|
34964
|
+
clearAllRequestSnapshots();
|
|
34659
34965
|
clearSessionTodos();
|
|
34660
34966
|
clearSessionPermissionGrants();
|
|
34661
34967
|
}
|
|
@@ -34814,6 +35120,7 @@ var init_conversationContext = __esm({
|
|
|
34814
35120
|
init_toolPermissions();
|
|
34815
35121
|
init_sessionTodos();
|
|
34816
35122
|
init_historyCompaction();
|
|
35123
|
+
init_requestSnapshotStore();
|
|
34817
35124
|
history = [];
|
|
34818
35125
|
lastClarification = null;
|
|
34819
35126
|
SHORT_CONTINUE = /^(procedi|continua|continue|go\s*ahead|go|ok|okay|sì|si|yes|vai|avanti|next|proceed|conferma|confermo|applica|fai|scrivi|esegui|implementa|vai pure|fai pure|ok procedi|sì procedi|si procedi)$/i;
|
|
@@ -35212,7 +35519,7 @@ import {
|
|
|
35212
35519
|
} from "node:fs";
|
|
35213
35520
|
import { join as join18, basename } from "node:path";
|
|
35214
35521
|
import { homedir as homedir9 } from "node:os";
|
|
35215
|
-
import { createHash as
|
|
35522
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
35216
35523
|
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
35217
35524
|
const candidates = [
|
|
35218
35525
|
join18(projectRoot, ".zelari"),
|
|
@@ -35228,7 +35535,7 @@ function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
|
35228
35535
|
return candidates[0];
|
|
35229
35536
|
}
|
|
35230
35537
|
function hashProject(projectPath) {
|
|
35231
|
-
return
|
|
35538
|
+
return createHash7("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
|
|
35232
35539
|
}
|
|
35233
35540
|
function isWritableDir(dir) {
|
|
35234
35541
|
try {
|
|
@@ -35819,8 +36126,8 @@ async function loadDurableContext(projectRoot, opts) {
|
|
|
35819
36126
|
return cache.text;
|
|
35820
36127
|
}
|
|
35821
36128
|
try {
|
|
35822
|
-
const
|
|
35823
|
-
const text = await
|
|
36129
|
+
const store4 = await getStateStore(projectRoot, env);
|
|
36130
|
+
const text = await store4.materializeContext(void 0, opts?.maxChars);
|
|
35824
36131
|
cache = { text: text || "", at: now, projectRoot };
|
|
35825
36132
|
return cache.text;
|
|
35826
36133
|
} catch {
|
|
@@ -37740,7 +38047,7 @@ __export(agentsMd_exports, {
|
|
|
37740
38047
|
updateAgentsMd: () => updateAgentsMd
|
|
37741
38048
|
});
|
|
37742
38049
|
import { existsSync as existsSync32, readFileSync as readFileSync28, writeFileSync as writeFileSync18 } from "node:fs";
|
|
37743
|
-
import { createHash as
|
|
38050
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
37744
38051
|
import { join as join27 } from "node:path";
|
|
37745
38052
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
37746
38053
|
async function readPackageJson2(projectRoot) {
|
|
@@ -37955,7 +38262,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
37955
38262
|
return { changed: true, sections: changedSections };
|
|
37956
38263
|
}
|
|
37957
38264
|
function hash2(s) {
|
|
37958
|
-
return
|
|
38265
|
+
return createHash8("sha256").update(s).digest("hex").slice(0, 16);
|
|
37959
38266
|
}
|
|
37960
38267
|
var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
|
|
37961
38268
|
var init_agentsMd = __esm({
|
|
@@ -38766,7 +39073,7 @@ __export(commitHelpers_exports, {
|
|
|
38766
39073
|
});
|
|
38767
39074
|
async function tryStateCommit(args) {
|
|
38768
39075
|
try {
|
|
38769
|
-
const
|
|
39076
|
+
const store4 = args.store ?? await getStateStore(args.projectRoot, args.env);
|
|
38770
39077
|
let workspaceCheckpointId = args.workspaceCheckpointId;
|
|
38771
39078
|
if (!workspaceCheckpointId && args.withCheckpoint && (args.env ?? process.env).ZELARI_CHECKPOINT !== "0") {
|
|
38772
39079
|
const cp = await createCheckpoint(
|
|
@@ -38775,7 +39082,7 @@ async function tryStateCommit(args) {
|
|
|
38775
39082
|
);
|
|
38776
39083
|
if (cp.ok) workspaceCheckpointId = cp.value.id;
|
|
38777
39084
|
}
|
|
38778
|
-
const meta3 = await
|
|
39085
|
+
const meta3 = await store4.commit({
|
|
38779
39086
|
mode: args.mode,
|
|
38780
39087
|
label: args.label,
|
|
38781
39088
|
layer: args.layer,
|
|
@@ -42783,9 +43090,9 @@ var init_oauthDesktop = __esm({
|
|
|
42783
43090
|
init_chatgptOAuth();
|
|
42784
43091
|
init_anthropicOAuth();
|
|
42785
43092
|
DEFAULT_MODELS = {
|
|
42786
|
-
grok: "grok-4.
|
|
42787
|
-
chatgpt: "gpt-5.
|
|
42788
|
-
anthropic: "claude-sonnet-4-
|
|
43093
|
+
grok: "grok-4.6",
|
|
43094
|
+
chatgpt: "gpt-5.6-codex",
|
|
43095
|
+
anthropic: "claude-sonnet-4-6"
|
|
42789
43096
|
};
|
|
42790
43097
|
}
|
|
42791
43098
|
});
|
|
@@ -43045,12 +43352,12 @@ function handleModelSet(ctx, model) {
|
|
|
43045
43352
|
}
|
|
43046
43353
|
function handleEffortShow(ctx) {
|
|
43047
43354
|
const id = ctx.activeProviderSpec.id;
|
|
43048
|
-
const cap3 = thinkingCapabilityFor(id);
|
|
43355
|
+
const cap3 = thinkingCapabilityFor(id, ctx.activeModel);
|
|
43049
43356
|
const current = stringifyThinkingSpec(getThinkingForProvider(id));
|
|
43050
43357
|
const options = [
|
|
43051
43358
|
"auto",
|
|
43052
43359
|
"off",
|
|
43053
|
-
...cap3.effort ? ["low", "medium", "high"] : [],
|
|
43360
|
+
...cap3.efforts ?? (cap3.effort ? ["low", "medium", "high"] : []),
|
|
43054
43361
|
...cap3.budget ? ["budget:<tokens>"] : []
|
|
43055
43362
|
];
|
|
43056
43363
|
appendSystem(
|
|
@@ -43063,7 +43370,7 @@ function handleEffortSet(ctx, raw) {
|
|
|
43063
43370
|
if (!isValidThinkingInput(raw)) {
|
|
43064
43371
|
appendSystem(
|
|
43065
43372
|
ctx.setMessages,
|
|
43066
|
-
`[effort] invalid spec "${raw}" \u2014 use auto | off | low | medium | high | budget:<tokens>`
|
|
43373
|
+
`[effort] invalid spec "${raw}" \u2014 use auto | off | low | medium | high | xhigh | max | budget:<tokens>`
|
|
43067
43374
|
);
|
|
43068
43375
|
return;
|
|
43069
43376
|
}
|
|
@@ -43624,6 +43931,11 @@ function buildDesktopConfigSnapshot() {
|
|
|
43624
43931
|
const providers = PROVIDERS.map((p3) => {
|
|
43625
43932
|
const cached2 = getCachedModels(p3.id);
|
|
43626
43933
|
const models = cached2?.models.map((m) => m.id) ?? [];
|
|
43934
|
+
if (models.length === 0) {
|
|
43935
|
+
for (const m of getStaticFallbackModels(p3.id)) {
|
|
43936
|
+
if (!models.includes(m.id)) models.push(m.id);
|
|
43937
|
+
}
|
|
43938
|
+
}
|
|
43627
43939
|
const defaultModel = config2.modelByProvider[p3.id] ?? "";
|
|
43628
43940
|
if (defaultModel && !models.includes(defaultModel)) {
|
|
43629
43941
|
models.unshift(defaultModel);
|
|
@@ -43647,7 +43959,7 @@ function buildDesktopConfigSnapshot() {
|
|
|
43647
43959
|
hasRefreshToken: Boolean(stored?.refreshToken),
|
|
43648
43960
|
oauthSupported: isOAuthProvider(p3.id),
|
|
43649
43961
|
thinking: config2.thinkingByProvider[p3.id] ?? "auto",
|
|
43650
|
-
thinkingCapability: thinkingCapabilityFor(p3.id)
|
|
43962
|
+
thinkingCapability: thinkingCapabilityFor(p3.id, defaultModel)
|
|
43651
43963
|
};
|
|
43652
43964
|
});
|
|
43653
43965
|
return {
|
|
@@ -44319,7 +44631,7 @@ import {
|
|
|
44319
44631
|
} from "node:fs";
|
|
44320
44632
|
import { join as join36 } from "node:path";
|
|
44321
44633
|
import { homedir as homedir13 } from "node:os";
|
|
44322
|
-
import { createHash as
|
|
44634
|
+
import { createHash as createHash9, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
|
|
44323
44635
|
function getZelariHome() {
|
|
44324
44636
|
return join36(homedir13(), ".zelari-code");
|
|
44325
44637
|
}
|
|
@@ -44395,8 +44707,8 @@ function loadOrCreateToken(explicit) {
|
|
|
44395
44707
|
}
|
|
44396
44708
|
function tokenMatches(expected, provided) {
|
|
44397
44709
|
if (!provided) return false;
|
|
44398
|
-
const a =
|
|
44399
|
-
const b =
|
|
44710
|
+
const a = createHash9("sha256").update(expected).digest();
|
|
44711
|
+
const b = createHash9("sha256").update(provided).digest();
|
|
44400
44712
|
try {
|
|
44401
44713
|
return timingSafeEqual(a, b);
|
|
44402
44714
|
} catch {
|
|
@@ -49063,34 +49375,58 @@ function phaseKnobs(phase2) {
|
|
|
49063
49375
|
})
|
|
49064
49376
|
};
|
|
49065
49377
|
}
|
|
49066
|
-
|
|
49067
|
-
const estimated = estimateHistoryTokens(hist);
|
|
49068
|
-
const occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
49069
|
-
return { estimated, occupancy };
|
|
49070
|
-
}
|
|
49378
|
+
var RESERVED_OUTPUT_TOKENS = 8192;
|
|
49071
49379
|
async function applyBudgetPolicyAsync(history2, phase2, opts) {
|
|
49072
49380
|
const contextLimit = resolveContextLimit(opts?.model);
|
|
49073
49381
|
const sessionExtra = opts?.sessionTokens ?? 0;
|
|
49074
49382
|
const warnings = [];
|
|
49075
49383
|
let { historyTurns, maxToolLoopIterations } = phaseKnobs(phase2);
|
|
49384
|
+
const envelope = opts?.requestSnapshot ?? null;
|
|
49385
|
+
const replayBase = envelope ? {
|
|
49386
|
+
provider: envelope.snapshot.provider,
|
|
49387
|
+
model: envelope.snapshot.model,
|
|
49388
|
+
systemMessages: envelope.snapshot.systemMessages,
|
|
49389
|
+
tools: envelope.snapshot.tools
|
|
49390
|
+
} : opts?.providerStream ? { provider: "local", model: opts?.model ?? "unknown", systemMessages: [], tools: [] } : null;
|
|
49391
|
+
const headerTokens = envelope ? estimateSystemTokensLite(envelope.snapshot.systemMessages) + estimateToolSchemaTokensLite(envelope.snapshot.tools) : 0;
|
|
49392
|
+
const convTokensOf = (h) => estimateConversationTokensLite(h);
|
|
49076
49393
|
let hist = history2;
|
|
49077
|
-
let
|
|
49394
|
+
let estimated = envelope ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
49395
|
+
let occupancy = Math.min(1, estimated / contextLimit);
|
|
49078
49396
|
let compactSummary = "";
|
|
49079
49397
|
let messagesRemoved = 0;
|
|
49398
|
+
let cacheReuseExpected;
|
|
49399
|
+
let prunedTotal = 0;
|
|
49080
49400
|
if (occupancy >= 0.7 && occupancy < 0.85) {
|
|
49081
49401
|
warnings.push(
|
|
49082
|
-
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated
|
|
49402
|
+
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated}/${contextLimit} tok full-request est.) \u2014 consider /compact or shorter replies.`
|
|
49083
49403
|
);
|
|
49084
49404
|
}
|
|
49405
|
+
if (occupancy >= 0.8) {
|
|
49406
|
+
const pruned = pruneToolResultsDetailed(hist);
|
|
49407
|
+
if (pruned.stats.pruned > 0) {
|
|
49408
|
+
hist = pruned.messages;
|
|
49409
|
+
prunedTotal += pruned.stats.pruned;
|
|
49410
|
+
estimated = envelope ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
49411
|
+
occupancy = Math.min(1, estimated / contextLimit);
|
|
49412
|
+
warnings.push(
|
|
49413
|
+
`[budget] pruned ${pruned.stats.pruned} oversized tool result(s) \u2192 ${Math.round(occupancy * 100)}% (${estimated} tok).`
|
|
49414
|
+
);
|
|
49415
|
+
}
|
|
49416
|
+
}
|
|
49085
49417
|
const fold = (r, label, forcedTurns) => {
|
|
49086
49418
|
hist = r.messages;
|
|
49087
49419
|
if (r.compacted) {
|
|
49088
49420
|
messagesRemoved += r.messagesRemoved;
|
|
49089
49421
|
if (r.summary) compactSummary = r.summary;
|
|
49422
|
+
if (r.cacheReuseExpected !== void 0) {
|
|
49423
|
+
cacheReuseExpected = r.cacheReuseExpected;
|
|
49424
|
+
}
|
|
49090
49425
|
}
|
|
49091
|
-
|
|
49426
|
+
estimated = envelope ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
49427
|
+
occupancy = Math.min(1, estimated / contextLimit);
|
|
49092
49428
|
warnings.push(
|
|
49093
|
-
`[budget] ${label}
|
|
49429
|
+
`[budget] ${label} \u2014 kept ~${forcedTurns} turns (${estimated} tok est.` + (r.messagesRemoved ? `, removed ${r.messagesRemoved} msgs` : "") + (r.cacheReuseExpected === false ? ", cache reuse NOT expected (model override)" : "") + ")."
|
|
49094
49430
|
);
|
|
49095
49431
|
};
|
|
49096
49432
|
if (occupancy >= 0.85) {
|
|
@@ -49100,39 +49436,91 @@ async function applyBudgetPolicyAsync(history2, phase2, opts) {
|
|
|
49100
49436
|
maxToolLoopIterations,
|
|
49101
49437
|
phase2 === "plan" ? 24 : 40
|
|
49102
49438
|
);
|
|
49103
|
-
|
|
49104
|
-
maxMessages: forcedTurns * 4,
|
|
49105
|
-
|
|
49106
|
-
|
|
49107
|
-
|
|
49439
|
+
let r = await compactHistoryAsync(hist, {
|
|
49440
|
+
maxMessages: Math.max(2, forcedTurns * 4),
|
|
49441
|
+
force: true,
|
|
49442
|
+
signal: opts?.signal,
|
|
49443
|
+
...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
|
|
49444
|
+
});
|
|
49445
|
+
if (!r.compacted) {
|
|
49446
|
+
r = await compactHistoryAsync(hist, {
|
|
49447
|
+
maxMessages: 2,
|
|
49448
|
+
force: true,
|
|
49449
|
+
signal: opts?.signal,
|
|
49450
|
+
...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
|
|
49451
|
+
});
|
|
49452
|
+
}
|
|
49453
|
+
const label = r.cacheReuseExpected === false ? "llm-compact (model override)" : "auto-compact";
|
|
49108
49454
|
fold(r, label, forcedTurns);
|
|
49109
49455
|
}
|
|
49110
49456
|
if (occupancy >= 0.95) {
|
|
49111
49457
|
const hard = await compactHistoryAsync(hist, {
|
|
49112
|
-
maxMessages:
|
|
49458
|
+
maxMessages: 2,
|
|
49459
|
+
force: true,
|
|
49113
49460
|
signal: opts?.signal
|
|
49114
49461
|
});
|
|
49115
|
-
fold(hard, hard.summary.includes("\xB7 llm") ? "llm-compact" : "
|
|
49462
|
+
fold(hard, hard.summary.includes("\xB7 llm") ? "llm-compact" : "HARD trim", 2);
|
|
49116
49463
|
historyTurns = 2;
|
|
49117
49464
|
maxToolLoopIterations = Math.min(maxToolLoopIterations, 16);
|
|
49118
49465
|
warnings.push(
|
|
49119
|
-
|
|
49466
|
+
"[budget] HARD context pressure (\u226595%) \u2014 prefer /clear or a new session if quality drops."
|
|
49120
49467
|
);
|
|
49121
49468
|
}
|
|
49469
|
+
const cacheMetricsLine = envelope ? [
|
|
49470
|
+
"compaction meter:",
|
|
49471
|
+
`provider/model: ${envelope.snapshot.provider}/${envelope.snapshot.model}`,
|
|
49472
|
+
`headerFingerprint: ${envelope.snapshot.headerFingerprint.slice(0, 12)}`,
|
|
49473
|
+
`occupancy: ${Math.round(occupancy * 100)}% (${estimated}/${contextLimit})`,
|
|
49474
|
+
...envelope.usage?.cachedPromptTokens !== void 0 ? [`cachedPromptTokens: ${envelope.usage.cachedPromptTokens}`] : [],
|
|
49475
|
+
...cacheReuseExpected !== void 0 ? [`cacheReuseExpected: ${cacheReuseExpected}`] : []
|
|
49476
|
+
].join(" | ") : void 0;
|
|
49122
49477
|
return {
|
|
49123
49478
|
history: hist,
|
|
49124
49479
|
warnings,
|
|
49125
49480
|
maxToolLoopIterations,
|
|
49126
49481
|
historyTurns,
|
|
49127
|
-
estimatedHistoryTokens: estimated,
|
|
49482
|
+
estimatedHistoryTokens: envelope ? convTokensOf(hist) : estimated,
|
|
49128
49483
|
contextLimit,
|
|
49129
49484
|
occupancy,
|
|
49130
49485
|
compactSummary: compactSummary || void 0,
|
|
49131
|
-
messagesRemoved: messagesRemoved || void 0
|
|
49486
|
+
messagesRemoved: messagesRemoved || void 0,
|
|
49487
|
+
...envelope ? { contextPressureTokens: estimated } : {},
|
|
49488
|
+
...cacheReuseExpected !== void 0 ? { cacheReuseExpected } : {},
|
|
49489
|
+
...cacheMetricsLine ? { cacheMetricsLine } : {}
|
|
49132
49490
|
};
|
|
49133
49491
|
}
|
|
49492
|
+
function estimateSystemTokensLite(systemMessages) {
|
|
49493
|
+
let n = 0;
|
|
49494
|
+
for (const m of systemMessages) n += 4 + Math.ceil((m.content ?? "").length / 4);
|
|
49495
|
+
return n;
|
|
49496
|
+
}
|
|
49497
|
+
function estimateToolSchemaTokensLite(tools) {
|
|
49498
|
+
let n = 0;
|
|
49499
|
+
for (const t of tools) {
|
|
49500
|
+
n += Math.ceil((t.name ?? "").length / 4);
|
|
49501
|
+
n += Math.ceil((t.description ?? "").length / 4);
|
|
49502
|
+
n += Math.ceil(JSON.stringify(t.parameters ?? {}).length / 4);
|
|
49503
|
+
}
|
|
49504
|
+
return n + tools.length * 4;
|
|
49505
|
+
}
|
|
49506
|
+
function estimateConversationTokensLite(messages) {
|
|
49507
|
+
let n = 0;
|
|
49508
|
+
for (const m of messages) {
|
|
49509
|
+
n += 4 + Math.ceil((m.content ?? "").length / 4);
|
|
49510
|
+
if (m.toolCalls) {
|
|
49511
|
+
for (const tc of m.toolCalls) {
|
|
49512
|
+
n += Math.ceil(tc.name.length / 4) + Math.ceil(tc.id.length / 4);
|
|
49513
|
+
n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
|
|
49514
|
+
}
|
|
49515
|
+
}
|
|
49516
|
+
if (m.reasoningContent) n += Math.ceil(m.reasoningContent.length / 4);
|
|
49517
|
+
if (m.toolCallId) n += Math.ceil(m.toolCallId.length / 4);
|
|
49518
|
+
}
|
|
49519
|
+
return n;
|
|
49520
|
+
}
|
|
49134
49521
|
|
|
49135
49522
|
// src/cli/hooks/useChatTurn.ts
|
|
49523
|
+
init_requestSnapshotStore();
|
|
49136
49524
|
function useChatTurn(params) {
|
|
49137
49525
|
const {
|
|
49138
49526
|
sessionId,
|
|
@@ -49159,17 +49547,9 @@ function useChatTurn(params) {
|
|
|
49159
49547
|
let envConfig;
|
|
49160
49548
|
let harness;
|
|
49161
49549
|
let historySeedLen = 0;
|
|
49550
|
+
let systemPrefixLen = 0;
|
|
49162
49551
|
let turnSucceeded = false;
|
|
49163
49552
|
try {
|
|
49164
|
-
compactInPlace();
|
|
49165
|
-
const budget = await applyBudgetPolicyAsync(getHistory(), getPhase(), {
|
|
49166
|
-
model: getActiveModel()
|
|
49167
|
-
});
|
|
49168
|
-
setHistory(budget.history);
|
|
49169
|
-
for (const w of budget.warnings) {
|
|
49170
|
-
appendSystem(setMessages, w, Date.now());
|
|
49171
|
-
}
|
|
49172
|
-
historySeedLen = getHistory().length;
|
|
49173
49553
|
const anchored = maybeAnchorShortAnswer(userText);
|
|
49174
49554
|
const effectiveUserText = anchored ?? userText;
|
|
49175
49555
|
const localCli = (process.env.ZELARI_LOCAL_CLI ?? "").trim();
|
|
@@ -49264,6 +49644,33 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49264
49644
|
});
|
|
49265
49645
|
}
|
|
49266
49646
|
const cwd = process.cwd();
|
|
49647
|
+
const budget = await applyBudgetPolicyAsync(getHistory(), getPhase(), {
|
|
49648
|
+
model: getActiveModel(),
|
|
49649
|
+
sessionId,
|
|
49650
|
+
// v1.36.0: envelope for full-request metering + cache-aware
|
|
49651
|
+
// compaction replay (last warm prefix + provider usage anchor).
|
|
49652
|
+
requestSnapshot: getRequestSnapshotWithUsage(sessionId),
|
|
49653
|
+
providerStream
|
|
49654
|
+
});
|
|
49655
|
+
setHistory(budget.history);
|
|
49656
|
+
for (const w of budget.warnings) {
|
|
49657
|
+
appendSystem(setMessages, w, Date.now());
|
|
49658
|
+
}
|
|
49659
|
+
if ((budget.messagesRemoved ?? 0) > 0) {
|
|
49660
|
+
const envelope = getRequestSnapshotWithUsage(sessionId);
|
|
49661
|
+
const compactionEvent = createBrainEvent("session_compacted", sessionId, {
|
|
49662
|
+
summary: budget.compactSummary ?? "",
|
|
49663
|
+
messagesRemoved: budget.messagesRemoved ?? 0,
|
|
49664
|
+
...envelope ? {
|
|
49665
|
+
sourceRequestFingerprint: envelope.snapshot.requestFingerprint,
|
|
49666
|
+
headerFingerprint: envelope.snapshot.headerFingerprint
|
|
49667
|
+
} : {},
|
|
49668
|
+
...budget.contextPressureTokens !== void 0 ? { sourceEstimatedTokens: budget.contextPressureTokens } : {},
|
|
49669
|
+
...budget.cacheReuseExpected !== void 0 ? { cacheReuseExpected: budget.cacheReuseExpected } : {}
|
|
49670
|
+
});
|
|
49671
|
+
void writerRef.current?.append(compactionEvent);
|
|
49672
|
+
}
|
|
49673
|
+
historySeedLen = getHistory().length;
|
|
49267
49674
|
let composedWorkspace = "";
|
|
49268
49675
|
let composedInstructions = "";
|
|
49269
49676
|
let hasPlan = false;
|
|
@@ -49421,6 +49828,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49421
49828
|
lastStableHash = hashStablePrompt(fallback);
|
|
49422
49829
|
systemMessages = [{ role: "system", content: fallback }];
|
|
49423
49830
|
}
|
|
49831
|
+
systemPrefixLen = systemMessages.length;
|
|
49424
49832
|
const maxToolCallsPerTurn = envNumber(process.env.ZELARI_MAX_TOOL_CALLS, {
|
|
49425
49833
|
default: 25,
|
|
49426
49834
|
min: 1
|
|
@@ -49433,7 +49841,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49433
49841
|
});
|
|
49434
49842
|
const harness2 = new AgentHarness({
|
|
49435
49843
|
model: envConfig.model,
|
|
49436
|
-
|
|
49844
|
+
// v1.36.0 (P0.2): real provider identity — the harness used to
|
|
49845
|
+
// hardcode "openai-compatible" (the transport family) so snapshots
|
|
49846
|
+
// and telemetry mislabeled deepseek/glm/minimax routing.
|
|
49847
|
+
provider: envConfig.providerId,
|
|
49437
49848
|
messages: [
|
|
49438
49849
|
...systemMessages,
|
|
49439
49850
|
// v1.8.0: shared rolling history (agent/council/zelari) so short
|
|
@@ -49452,6 +49863,9 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49452
49863
|
cwd,
|
|
49453
49864
|
maxToolCallsPerTurn,
|
|
49454
49865
|
maxToolLoopIterations,
|
|
49866
|
+
// v1.36.0: routed-request snapshots feed the meter (occupancy) and
|
|
49867
|
+
// the cache-aware compaction replay (last warm prefix).
|
|
49868
|
+
onRequestSnapshot: (snap) => recordRequestSnapshot(sessionId, snap),
|
|
49455
49869
|
...maxToolLoopHardCap > 0 ? { maxToolLoopHardCap } : {}
|
|
49456
49870
|
});
|
|
49457
49871
|
harnessRef.current = harness2;
|
|
@@ -49466,6 +49880,14 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49466
49880
|
for await (const event of harness2.run()) {
|
|
49467
49881
|
if (event.type === "message_end") {
|
|
49468
49882
|
if (event.usage) realUsage = event.usage;
|
|
49883
|
+
if (event.usage) {
|
|
49884
|
+
recordRequestUsage(sessionId, {
|
|
49885
|
+
promptTokens: event.usage.promptTokens,
|
|
49886
|
+
completionTokens: event.usage.completionTokens,
|
|
49887
|
+
totalTokens: event.usage.totalTokens,
|
|
49888
|
+
cachedPromptTokens: event.usage.cachedPromptTokens
|
|
49889
|
+
});
|
|
49890
|
+
}
|
|
49469
49891
|
if (streamContent) {
|
|
49470
49892
|
const sealed = streamScrub.finalize(streamContent);
|
|
49471
49893
|
if (useLiveModel) setStreaming(commitStreaming, sealed, event.ts);
|
|
@@ -49625,18 +50047,17 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49625
50047
|
const h = harnessRef.current;
|
|
49626
50048
|
if (h && turnSucceeded) {
|
|
49627
50049
|
const all = h.getMessages();
|
|
49628
|
-
const seedLen =
|
|
50050
|
+
const seedLen = systemPrefixLen + historySeedLen + 1;
|
|
49629
50051
|
if (all.length > seedLen) {
|
|
49630
50052
|
appendMessages(
|
|
49631
|
-
all.slice(seedLen).map(
|
|
49632
|
-
(m
|
|
49633
|
-
|
|
49634
|
-
|
|
49635
|
-
|
|
49636
|
-
|
|
49637
|
-
|
|
49638
|
-
|
|
49639
|
-
)
|
|
50053
|
+
all.slice(seedLen).map((m) => {
|
|
50054
|
+
if (m.role !== "assistant" || !m.content) return m;
|
|
50055
|
+
const cleaned = cleanAgentContent(m.content, {
|
|
50056
|
+
stripQuestion: false,
|
|
50057
|
+
stripThink: false
|
|
50058
|
+
});
|
|
50059
|
+
return cleaned === m.content ? m : { ...m, content: cleaned };
|
|
50060
|
+
})
|
|
49640
50061
|
);
|
|
49641
50062
|
}
|
|
49642
50063
|
}
|
|
@@ -51579,12 +52000,12 @@ init_fileStateStore();
|
|
|
51579
52000
|
async function restoreDurableState(opts) {
|
|
51580
52001
|
const restoreTree = opts.restoreTree !== false;
|
|
51581
52002
|
try {
|
|
51582
|
-
const
|
|
52003
|
+
const store4 = opts.store ?? await getStateStore(opts.projectRoot);
|
|
51583
52004
|
let meta3;
|
|
51584
52005
|
if (opts.commitId) {
|
|
51585
|
-
meta3 = await
|
|
52006
|
+
meta3 = await store4.setHead(opts.commitId);
|
|
51586
52007
|
} else {
|
|
51587
|
-
meta3 = await
|
|
52008
|
+
meta3 = await store4.head();
|
|
51588
52009
|
if (!meta3) {
|
|
51589
52010
|
return {
|
|
51590
52011
|
ok: false,
|
|
@@ -51637,8 +52058,8 @@ function ago2(ms) {
|
|
|
51637
52058
|
return `${Math.round(s / 3600)}h ago`;
|
|
51638
52059
|
}
|
|
51639
52060
|
async function handleStateStatus(ctx) {
|
|
51640
|
-
const
|
|
51641
|
-
const head = await
|
|
52061
|
+
const store4 = await getStateStore(ctx.cwd);
|
|
52062
|
+
const head = await store4.head();
|
|
51642
52063
|
if (!head) {
|
|
51643
52064
|
appendSystem(
|
|
51644
52065
|
ctx.setMessages,
|
|
@@ -51646,9 +52067,9 @@ async function handleStateStatus(ctx) {
|
|
|
51646
52067
|
);
|
|
51647
52068
|
return;
|
|
51648
52069
|
}
|
|
51649
|
-
const discoveries = await
|
|
52070
|
+
const discoveries = await store4.loadDiscoveries(head.id);
|
|
51650
52071
|
const reusable = discoveries.filter((d) => d.reusable).length;
|
|
51651
|
-
const recent = await
|
|
52072
|
+
const recent = await store4.list(8);
|
|
51652
52073
|
const lines = recent.map((c, i) => {
|
|
51653
52074
|
const ver2 = c.verification.ran ? c.verification.ok ? "ok" : "fail" : "n/a";
|
|
51654
52075
|
return ` ${i === 0 ? "\u2192" : " "} ${c.id} ${ago2(c.createdAt)} ${c.label} ver=${ver2}` + (c.layer ? ` [${c.layer}]` : "") + (c.stablePromptHash ? ` hash=${c.stablePromptHash.slice(0, 8)}` : "");
|
|
@@ -51667,9 +52088,9 @@ async function handleStateStatus(ctx) {
|
|
|
51667
52088
|
);
|
|
51668
52089
|
}
|
|
51669
52090
|
async function handleStateCommit(ctx, label) {
|
|
51670
|
-
const
|
|
52091
|
+
const store4 = await getStateStore(ctx.cwd);
|
|
51671
52092
|
try {
|
|
51672
|
-
const meta3 = await
|
|
52093
|
+
const meta3 = await store4.commit({
|
|
51673
52094
|
mode: "agent",
|
|
51674
52095
|
label: label?.trim() || "manual state commit",
|
|
51675
52096
|
layer: "manual",
|
|
@@ -51696,8 +52117,8 @@ async function handleStateCommit(ctx, label) {
|
|
|
51696
52117
|
}
|
|
51697
52118
|
}
|
|
51698
52119
|
async function handleStateShow(ctx, id) {
|
|
51699
|
-
const
|
|
51700
|
-
const meta3 = id ? await
|
|
52120
|
+
const store4 = await getStateStore(ctx.cwd);
|
|
52121
|
+
const meta3 = id ? await store4.get(id) : await store4.head();
|
|
51701
52122
|
if (!meta3) {
|
|
51702
52123
|
appendSystem(
|
|
51703
52124
|
ctx.setMessages,
|
|
@@ -51705,7 +52126,7 @@ async function handleStateShow(ctx, id) {
|
|
|
51705
52126
|
);
|
|
51706
52127
|
return;
|
|
51707
52128
|
}
|
|
51708
|
-
const text = await
|
|
52129
|
+
const text = await store4.materializeContext(meta3.id, 6e3);
|
|
51709
52130
|
appendSystem(ctx.setMessages, `[state] show ${meta3.id}
|
|
51710
52131
|
${text}`);
|
|
51711
52132
|
}
|
|
@@ -53214,14 +53635,14 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
53214
53635
|
}
|
|
53215
53636
|
function handleCouncilFeedback(ctx, memberId, score, note) {
|
|
53216
53637
|
try {
|
|
53217
|
-
const
|
|
53218
|
-
const entry =
|
|
53638
|
+
const store4 = new FeedbackStore();
|
|
53639
|
+
const entry = store4.record({
|
|
53219
53640
|
memberId,
|
|
53220
53641
|
score,
|
|
53221
53642
|
...note ? { note } : {},
|
|
53222
53643
|
...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
|
|
53223
53644
|
});
|
|
53224
|
-
const stats =
|
|
53645
|
+
const stats = store4.getStats(memberId);
|
|
53225
53646
|
appendSystem(
|
|
53226
53647
|
ctx.setMessages,
|
|
53227
53648
|
`[council-feedback] ${memberId} rated ${entry.score}/5 \u2014 running avg ${stats.avg.toFixed(2)} over ${stats.count} rating(s).`
|