@serkanalgur/opencode-slim 1.1.0 → 1.2.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/index.d.ts.map +1 -1
- package/dist/index.js +338 -11
- package/dist/index.js.map +1 -1
- package/dist/lib/prompts.d.ts +1 -0
- package/dist/lib/prompts.d.ts.map +1 -1
- package/dist/lib/tui.d.ts +36 -0
- package/dist/lib/tui.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;;;;;AAqYjD,wBAA8C"}
|
package/dist/index.js
CHANGED
|
@@ -13663,6 +13663,251 @@ function getNudgeMessage(reason, tokenCount, maxTokens) {
|
|
|
13663
13663
|
}
|
|
13664
13664
|
}
|
|
13665
13665
|
|
|
13666
|
+
// lib/types.ts
|
|
13667
|
+
var COST_PROFILES = {
|
|
13668
|
+
// Anthropic
|
|
13669
|
+
"anthropic/claude-sonnet-4-20250514": {
|
|
13670
|
+
inputPricePer1k: 3e-3,
|
|
13671
|
+
outputPricePer1k: 0.015,
|
|
13672
|
+
cacheReadPricePer1k: 3e-4,
|
|
13673
|
+
cacheWritePricePer1k: 375e-5
|
|
13674
|
+
},
|
|
13675
|
+
"anthropic/claude-3-5-sonnet-20241022": {
|
|
13676
|
+
inputPricePer1k: 3e-3,
|
|
13677
|
+
outputPricePer1k: 0.015,
|
|
13678
|
+
cacheReadPricePer1k: 3e-4,
|
|
13679
|
+
cacheWritePricePer1k: 375e-5
|
|
13680
|
+
},
|
|
13681
|
+
// OpenAI
|
|
13682
|
+
"openai/gpt-4o": {
|
|
13683
|
+
inputPricePer1k: 25e-4,
|
|
13684
|
+
outputPricePer1k: 0.01,
|
|
13685
|
+
cacheReadPricePer1k: 125e-5,
|
|
13686
|
+
cacheWritePricePer1k: 25e-4
|
|
13687
|
+
},
|
|
13688
|
+
"openai/gpt-4o-mini": {
|
|
13689
|
+
inputPricePer1k: 15e-5,
|
|
13690
|
+
outputPricePer1k: 6e-4,
|
|
13691
|
+
cacheReadPricePer1k: 75e-6,
|
|
13692
|
+
cacheWritePricePer1k: 15e-5
|
|
13693
|
+
},
|
|
13694
|
+
// Default
|
|
13695
|
+
"default": {
|
|
13696
|
+
inputPricePer1k: 3e-3,
|
|
13697
|
+
outputPricePer1k: 0.015,
|
|
13698
|
+
cacheReadPricePer1k: 3e-4,
|
|
13699
|
+
cacheWritePricePer1k: 375e-5
|
|
13700
|
+
}
|
|
13701
|
+
};
|
|
13702
|
+
|
|
13703
|
+
// lib/tui.ts
|
|
13704
|
+
async function buildPanelData(sessionId, messages, state, config2, modelId) {
|
|
13705
|
+
const modelContextLimit = state.modelContextLimit || 2e5;
|
|
13706
|
+
const maxTokens = resolveTokenLimit(config2.compress.maxContextLimit, modelContextLimit);
|
|
13707
|
+
let currentTokens = 0;
|
|
13708
|
+
const tokensByRole = { user: 0, assistant: 0, tools: 0, system: 0 };
|
|
13709
|
+
let userMessages = 0;
|
|
13710
|
+
let assistantMessages = 0;
|
|
13711
|
+
let toolCalls = 0;
|
|
13712
|
+
let toolResults = 0;
|
|
13713
|
+
for (const msg of messages) {
|
|
13714
|
+
const role = msg.info.role;
|
|
13715
|
+
const text = getMessageText(msg);
|
|
13716
|
+
const toolContent = getToolResultContent(msg);
|
|
13717
|
+
const msgTokens = await countTokens(text + toolContent);
|
|
13718
|
+
currentTokens += msgTokens;
|
|
13719
|
+
if (role === "user") {
|
|
13720
|
+
tokensByRole.user += msgTokens;
|
|
13721
|
+
userMessages++;
|
|
13722
|
+
} else if (role === "assistant") {
|
|
13723
|
+
tokensByRole.assistant += msgTokens;
|
|
13724
|
+
assistantMessages++;
|
|
13725
|
+
}
|
|
13726
|
+
for (const part of msg.parts) {
|
|
13727
|
+
if (part.type === "tool") {
|
|
13728
|
+
const toolPart = part;
|
|
13729
|
+
if (toolPart.state?.status === "completed") {
|
|
13730
|
+
toolResults++;
|
|
13731
|
+
} else {
|
|
13732
|
+
toolCalls++;
|
|
13733
|
+
}
|
|
13734
|
+
}
|
|
13735
|
+
}
|
|
13736
|
+
}
|
|
13737
|
+
tokensByRole.tools = tokensByRole.user + tokensByRole.assistant - tokensByRole.user - tokensByRole.assistant;
|
|
13738
|
+
tokensByRole.system = Math.max(0, currentTokens - tokensByRole.user - tokensByRole.assistant - tokensByRole.tools);
|
|
13739
|
+
const usagePercent = currentTokens / maxTokens * 100;
|
|
13740
|
+
let status = "healthy";
|
|
13741
|
+
if (usagePercent > 90) status = "critical";
|
|
13742
|
+
else if (usagePercent > 70) status = "warning";
|
|
13743
|
+
const compressionCount = state.compressionCount;
|
|
13744
|
+
const averageRatio = state.averageCompressionRatio;
|
|
13745
|
+
let totalTokensSaved = 0;
|
|
13746
|
+
for (const record2 of state.compressionHistory) {
|
|
13747
|
+
if (record2.success) {
|
|
13748
|
+
totalTokensSaved += record2.inputTokens - record2.outputTokens;
|
|
13749
|
+
}
|
|
13750
|
+
}
|
|
13751
|
+
const lastCompression = state.compressionHistory.length > 0 ? state.compressionHistory[state.compressionHistory.length - 1] : null;
|
|
13752
|
+
const profile = COST_PROFILES[modelId || "default"] || COST_PROFILES.default;
|
|
13753
|
+
const estimatedCost = currentTokens / 1e3 * profile.inputPricePer1k;
|
|
13754
|
+
const costSaved = totalTokensSaved / 1e3 * profile.inputPricePer1k;
|
|
13755
|
+
const topicMap = /* @__PURE__ */ new Map();
|
|
13756
|
+
for (const msg of messages) {
|
|
13757
|
+
const text = getMessageText(msg);
|
|
13758
|
+
const topics2 = extractTopics(text);
|
|
13759
|
+
for (const topic of topics2) {
|
|
13760
|
+
const existing = topicMap.get(topic) || { count: 0, tokens: 0 };
|
|
13761
|
+
existing.count++;
|
|
13762
|
+
existing.tokens += await countTokens(text);
|
|
13763
|
+
topicMap.set(topic, existing);
|
|
13764
|
+
}
|
|
13765
|
+
}
|
|
13766
|
+
const topics = Array.from(topicMap.entries()).map(([topic, data]) => ({ topic, ...data })).sort((a, b) => b.tokens - a.tokens).slice(0, 10);
|
|
13767
|
+
const recommendations = generateRecommendations(
|
|
13768
|
+
usagePercent,
|
|
13769
|
+
compressionCount,
|
|
13770
|
+
averageRatio,
|
|
13771
|
+
messages.length,
|
|
13772
|
+
config2
|
|
13773
|
+
);
|
|
13774
|
+
return {
|
|
13775
|
+
sessionId,
|
|
13776
|
+
timestamp: Date.now(),
|
|
13777
|
+
currentTokens,
|
|
13778
|
+
maxTokens,
|
|
13779
|
+
usagePercent,
|
|
13780
|
+
status,
|
|
13781
|
+
messageCount: messages.length,
|
|
13782
|
+
userMessages,
|
|
13783
|
+
assistantMessages,
|
|
13784
|
+
toolCalls,
|
|
13785
|
+
toolResults,
|
|
13786
|
+
tokensByRole,
|
|
13787
|
+
compressionCount,
|
|
13788
|
+
averageRatio,
|
|
13789
|
+
totalTokensSaved,
|
|
13790
|
+
lastCompression,
|
|
13791
|
+
estimatedCost,
|
|
13792
|
+
costSaved,
|
|
13793
|
+
model: modelId || "unknown",
|
|
13794
|
+
topics,
|
|
13795
|
+
recommendations
|
|
13796
|
+
};
|
|
13797
|
+
}
|
|
13798
|
+
var TOPIC_KEYWORDS = {
|
|
13799
|
+
"authentication": ["auth", "login", "password", "token", "session", "jwt"],
|
|
13800
|
+
"database": ["database", "db", "sql", "query", "migration", "schema"],
|
|
13801
|
+
"api": ["api", "endpoint", "route", "request", "response", "http"],
|
|
13802
|
+
"testing": ["test", "spec", "assert", "expect", "describe", "jest"],
|
|
13803
|
+
"configuration": ["config", "settings", "env", "environment", "variable"],
|
|
13804
|
+
"deployment": ["deploy", "docker", "kubernetes", "ci", "cd", "pipeline"],
|
|
13805
|
+
"ui": ["ui", "component", "render", "display", "style", "css"],
|
|
13806
|
+
"error": ["error", "exception", "catch", "throw", "debug", "fix"],
|
|
13807
|
+
"performance": ["performance", "optimize", "cache", "speed", "slow"],
|
|
13808
|
+
"security": ["security", "encrypt", "decrypt", "hash", "sanitize"]
|
|
13809
|
+
};
|
|
13810
|
+
function extractTopics(text) {
|
|
13811
|
+
const lower = text.toLowerCase();
|
|
13812
|
+
const topics = [];
|
|
13813
|
+
for (const [topic, keywords] of Object.entries(TOPIC_KEYWORDS)) {
|
|
13814
|
+
if (keywords.some((kw) => lower.includes(kw))) {
|
|
13815
|
+
topics.push(topic);
|
|
13816
|
+
}
|
|
13817
|
+
}
|
|
13818
|
+
return topics.length > 0 ? topics : ["general"];
|
|
13819
|
+
}
|
|
13820
|
+
function generateRecommendations(usagePercent, compressionCount, averageRatio, messageCount, config2) {
|
|
13821
|
+
const recs = [];
|
|
13822
|
+
if (usagePercent > 80) {
|
|
13823
|
+
recs.push("Context usage is high. Consider compressing older messages.");
|
|
13824
|
+
}
|
|
13825
|
+
if (usagePercent > 90) {
|
|
13826
|
+
recs.push("Context nearly full! Run compress immediately to avoid truncation.");
|
|
13827
|
+
}
|
|
13828
|
+
if (compressionCount === 0 && messageCount > 20) {
|
|
13829
|
+
recs.push("No compressions yet with many messages. Consider running compress.");
|
|
13830
|
+
}
|
|
13831
|
+
if (averageRatio < 0.3 && compressionCount > 0) {
|
|
13832
|
+
recs.push("Compression ratio is low. Summaries may be too verbose.");
|
|
13833
|
+
}
|
|
13834
|
+
if (messageCount > 50 && usagePercent < 50) {
|
|
13835
|
+
recs.push("Many messages but low usage. Deduplication may help further.");
|
|
13836
|
+
}
|
|
13837
|
+
if (recs.length === 0) {
|
|
13838
|
+
recs.push("Context is healthy. No action needed.");
|
|
13839
|
+
}
|
|
13840
|
+
return recs;
|
|
13841
|
+
}
|
|
13842
|
+
function renderPanel(data) {
|
|
13843
|
+
const lines = [];
|
|
13844
|
+
lines.push("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
13845
|
+
lines.push("\u2502 SLIM CONTEXT PANEL \u2502");
|
|
13846
|
+
lines.push("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
13847
|
+
const statusIcon = data.status === "healthy" ? "\u{1F7E2}" : data.status === "warning" ? "\u{1F7E1}" : "\u{1F534}";
|
|
13848
|
+
lines.push(`\u2502 Status: ${statusIcon} ${data.status.toUpperCase().padEnd(10)} \u2502`);
|
|
13849
|
+
lines.push("");
|
|
13850
|
+
const barLength = 30;
|
|
13851
|
+
const filledLength = Math.round(data.usagePercent / 100 * barLength);
|
|
13852
|
+
const emptyLength = barLength - filledLength;
|
|
13853
|
+
const bar = "\u2588".repeat(filledLength) + "\u2591".repeat(emptyLength);
|
|
13854
|
+
lines.push(`\u2502 Context: [${bar}] ${data.usagePercent.toFixed(1)}%`);
|
|
13855
|
+
lines.push(`\u2502 ${formatTokens(data.currentTokens)} / ${formatTokens(data.maxTokens)} tokens`);
|
|
13856
|
+
lines.push("");
|
|
13857
|
+
lines.push("\u2502 Messages:");
|
|
13858
|
+
lines.push(`\u2502 User: ${data.userMessages} Assistant: ${data.assistantMessages}`);
|
|
13859
|
+
lines.push(`\u2502 Tool calls: ${data.toolCalls} Results: ${data.toolResults}`);
|
|
13860
|
+
lines.push("");
|
|
13861
|
+
lines.push("\u2502 Token Distribution:");
|
|
13862
|
+
lines.push(`\u2502 User: ${formatTokens(data.tokensByRole.user)}`);
|
|
13863
|
+
lines.push(`\u2502 Assistant: ${formatTokens(data.tokensByRole.assistant)}`);
|
|
13864
|
+
lines.push("");
|
|
13865
|
+
lines.push("\u2502 Compression Stats:");
|
|
13866
|
+
lines.push(`\u2502 Count: ${data.compressionCount}`);
|
|
13867
|
+
lines.push(`\u2502 Avg ratio: ${(data.averageRatio * 100).toFixed(1)}%`);
|
|
13868
|
+
lines.push(`\u2502 Tokens saved: ${formatTokens(data.totalTokensSaved)}`);
|
|
13869
|
+
if (data.lastCompression) {
|
|
13870
|
+
const ago = Date.now() - data.lastCompression.timestamp;
|
|
13871
|
+
lines.push(`\u2502 Last: ${formatTimeAgo(ago)} ago`);
|
|
13872
|
+
}
|
|
13873
|
+
lines.push("");
|
|
13874
|
+
lines.push("\u2502 Cost Estimate:");
|
|
13875
|
+
lines.push(`\u2502 Current: $${data.estimatedCost.toFixed(4)}`);
|
|
13876
|
+
lines.push(`\u2502 Saved: $${data.costSaved.toFixed(4)}`);
|
|
13877
|
+
lines.push(`\u2502 Model: ${data.model}`);
|
|
13878
|
+
lines.push("");
|
|
13879
|
+
if (data.topics.length > 0) {
|
|
13880
|
+
lines.push("\u2502 Top Topics:");
|
|
13881
|
+
for (const topic of data.topics.slice(0, 5)) {
|
|
13882
|
+
lines.push(`\u2502 ${topic.topic}: ${topic.count} msgs (${formatTokens(topic.tokens)})`);
|
|
13883
|
+
}
|
|
13884
|
+
lines.push("");
|
|
13885
|
+
}
|
|
13886
|
+
lines.push("\u2502 Recommendations:");
|
|
13887
|
+
for (const rec of data.recommendations) {
|
|
13888
|
+
lines.push(`\u2502 \u2022 ${rec}`);
|
|
13889
|
+
}
|
|
13890
|
+
lines.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
13891
|
+
return lines.join("\n");
|
|
13892
|
+
}
|
|
13893
|
+
function formatTokens(tokens) {
|
|
13894
|
+
if (tokens >= 1e6) {
|
|
13895
|
+
return `${(tokens / 1e6).toFixed(1)}M`;
|
|
13896
|
+
}
|
|
13897
|
+
if (tokens >= 1e3) {
|
|
13898
|
+
return `${(tokens / 1e3).toFixed(1)}K`;
|
|
13899
|
+
}
|
|
13900
|
+
return String(tokens);
|
|
13901
|
+
}
|
|
13902
|
+
function formatTimeAgo(ms) {
|
|
13903
|
+
const seconds = Math.floor(ms / 1e3);
|
|
13904
|
+
if (seconds < 60) return `${seconds}s`;
|
|
13905
|
+
const minutes = Math.floor(seconds / 60);
|
|
13906
|
+
if (minutes < 60) return `${minutes}m`;
|
|
13907
|
+
const hours = Math.floor(minutes / 60);
|
|
13908
|
+
return `${hours}h`;
|
|
13909
|
+
}
|
|
13910
|
+
|
|
13666
13911
|
// index.ts
|
|
13667
13912
|
var sessionStates = /* @__PURE__ */ new Map();
|
|
13668
13913
|
var sessionConfigs = /* @__PURE__ */ new Map();
|
|
@@ -13682,7 +13927,12 @@ var server = async (ctx) => {
|
|
|
13682
13927
|
const compressTool = tool({
|
|
13683
13928
|
description: getCompressToolDescription(),
|
|
13684
13929
|
args: {
|
|
13685
|
-
focus: external_exports.string().describe("
|
|
13930
|
+
focus: external_exports.string().describe("What to compress (e.g., 'old exploration', 'completed tasks')"),
|
|
13931
|
+
mode: external_exports.enum(["auto", "range", "topic"]).default("auto").describe("Compression mode"),
|
|
13932
|
+
start: external_exports.number().optional().describe("Start message index (for range mode)"),
|
|
13933
|
+
end: external_exports.number().optional().describe("End message index (for range mode)"),
|
|
13934
|
+
topic: external_exports.string().optional().describe("Topic to compress (for topic mode)"),
|
|
13935
|
+
keepRecent: external_exports.number().default(5).describe("Number of recent messages to always keep")
|
|
13686
13936
|
},
|
|
13687
13937
|
async execute(args, context) {
|
|
13688
13938
|
const config2 = getConfig(context.sessionID);
|
|
@@ -13699,17 +13949,42 @@ var server = async (ctx) => {
|
|
|
13699
13949
|
info: m.info,
|
|
13700
13950
|
parts: m.parts
|
|
13701
13951
|
}));
|
|
13952
|
+
let targetIndices = [];
|
|
13702
13953
|
let inputTokens = 0;
|
|
13703
|
-
|
|
13704
|
-
const
|
|
13705
|
-
|
|
13954
|
+
if (args.mode === "range" && args.start !== void 0 && args.end !== void 0) {
|
|
13955
|
+
const start = Math.max(0, args.start);
|
|
13956
|
+
const end = Math.min(messageWithParts.length, args.end);
|
|
13957
|
+
for (let i = start; i < end; i++) {
|
|
13958
|
+
targetIndices.push(i);
|
|
13959
|
+
const text = getMessageText(messageWithParts[i]) + getToolResultContent(messageWithParts[i]);
|
|
13960
|
+
inputTokens += await countTokens(text);
|
|
13961
|
+
}
|
|
13962
|
+
} else if (args.mode === "topic" && args.topic) {
|
|
13963
|
+
const topicLower = args.topic.toLowerCase();
|
|
13964
|
+
for (let i = 0; i < messageWithParts.length - args.keepRecent; i++) {
|
|
13965
|
+
const msg = messageWithParts[i];
|
|
13966
|
+
const text = getMessageText(msg) + getToolResultContent(msg);
|
|
13967
|
+
if (text.toLowerCase().includes(topicLower)) {
|
|
13968
|
+
targetIndices.push(i);
|
|
13969
|
+
inputTokens += await countTokens(text);
|
|
13970
|
+
}
|
|
13971
|
+
}
|
|
13972
|
+
} else {
|
|
13973
|
+
const keepRecent = args.keepRecent;
|
|
13974
|
+
for (let i = 0; i < messageWithParts.length - keepRecent; i++) {
|
|
13975
|
+
const msg = messageWithParts[i];
|
|
13976
|
+
const text = getMessageText(msg) + getToolResultContent(msg);
|
|
13977
|
+
const tokens = await countTokens(text);
|
|
13978
|
+
if (tokens < 100) continue;
|
|
13979
|
+
targetIndices.push(i);
|
|
13980
|
+
inputTokens += tokens;
|
|
13981
|
+
}
|
|
13706
13982
|
}
|
|
13707
|
-
|
|
13708
|
-
if (targets.length === 0) {
|
|
13983
|
+
if (targetIndices.length === 0) {
|
|
13709
13984
|
return "Nothing to compress - context is already efficient";
|
|
13710
13985
|
}
|
|
13711
|
-
const
|
|
13712
|
-
const summary = buildCompressionSummary(
|
|
13986
|
+
const targetMessages = targetIndices.map((i) => messageWithParts[i]);
|
|
13987
|
+
const summary = buildCompressionSummary(targetMessages, args.focus);
|
|
13713
13988
|
const outputTokens = await countTokens(summary);
|
|
13714
13989
|
const ratio = inputTokens > 0 ? 1 - outputTokens / inputTokens : 0;
|
|
13715
13990
|
addCompressionRecord(
|
|
@@ -13719,19 +13994,20 @@ var server = async (ctx) => {
|
|
|
13719
13994
|
inputTokens,
|
|
13720
13995
|
outputTokens,
|
|
13721
13996
|
ratio,
|
|
13722
|
-
messageCount:
|
|
13997
|
+
messageCount: targetMessages.length,
|
|
13723
13998
|
success: true
|
|
13724
13999
|
},
|
|
13725
14000
|
config2.adaptive.learningRate
|
|
13726
14001
|
);
|
|
13727
14002
|
saveSessionState(state, config2.persistence.directory);
|
|
13728
14003
|
return {
|
|
13729
|
-
title: `Compressed ${
|
|
14004
|
+
title: `Compressed ${targetMessages.length} messages`,
|
|
13730
14005
|
output: summary,
|
|
13731
14006
|
metadata: {
|
|
13732
14007
|
inputTokens,
|
|
13733
14008
|
outputTokens,
|
|
13734
14009
|
ratio: Math.round(ratio * 100) + "%",
|
|
14010
|
+
mode: args.mode,
|
|
13735
14011
|
focus: args.focus
|
|
13736
14012
|
}
|
|
13737
14013
|
};
|
|
@@ -13740,6 +14016,55 @@ var server = async (ctx) => {
|
|
|
13740
14016
|
}
|
|
13741
14017
|
}
|
|
13742
14018
|
});
|
|
14019
|
+
const panelTool = tool({
|
|
14020
|
+
description: `Display a rich context usage panel showing:
|
|
14021
|
+
- Current token usage vs model limit
|
|
14022
|
+
- Message breakdown (user/assistant/tools)
|
|
14023
|
+
- Token distribution by role
|
|
14024
|
+
- Compression history and savings
|
|
14025
|
+
- Cost estimate
|
|
14026
|
+
- Topic distribution
|
|
14027
|
+
- Smart recommendations`,
|
|
14028
|
+
args: {},
|
|
14029
|
+
async execute(_args, context) {
|
|
14030
|
+
const config2 = getConfig(context.sessionID);
|
|
14031
|
+
const state = getState(context.sessionID, config2);
|
|
14032
|
+
try {
|
|
14033
|
+
const response = await ctx.client.session.messages({
|
|
14034
|
+
path: { id: context.sessionID }
|
|
14035
|
+
});
|
|
14036
|
+
if (!response.data || response.error) {
|
|
14037
|
+
return "Failed to fetch messages";
|
|
14038
|
+
}
|
|
14039
|
+
const messageList = response.data;
|
|
14040
|
+
const messageWithParts = messageList.map((m) => ({
|
|
14041
|
+
info: m.info,
|
|
14042
|
+
parts: m.parts
|
|
14043
|
+
}));
|
|
14044
|
+
const modelId = context.model?.id || "unknown";
|
|
14045
|
+
const panelData = await buildPanelData(
|
|
14046
|
+
context.sessionID,
|
|
14047
|
+
messageWithParts,
|
|
14048
|
+
state,
|
|
14049
|
+
config2,
|
|
14050
|
+
modelId
|
|
14051
|
+
);
|
|
14052
|
+
const panel = renderPanel(panelData);
|
|
14053
|
+
return {
|
|
14054
|
+
title: "Context Panel",
|
|
14055
|
+
output: panel,
|
|
14056
|
+
metadata: {
|
|
14057
|
+
usagePercent: panelData.usagePercent,
|
|
14058
|
+
status: panelData.status,
|
|
14059
|
+
currentTokens: panelData.currentTokens,
|
|
14060
|
+
maxTokens: panelData.maxTokens
|
|
14061
|
+
}
|
|
14062
|
+
};
|
|
14063
|
+
} catch (error45) {
|
|
14064
|
+
return `Error generating panel: ${error45 instanceof Error ? error45.message : "Unknown error"}`;
|
|
14065
|
+
}
|
|
14066
|
+
}
|
|
14067
|
+
});
|
|
13743
14068
|
return {
|
|
13744
14069
|
config: async (opencodeConfig) => {
|
|
13745
14070
|
if (!opencodeConfig.permission) {
|
|
@@ -13747,9 +14072,11 @@ var server = async (ctx) => {
|
|
|
13747
14072
|
}
|
|
13748
14073
|
;
|
|
13749
14074
|
opencodeConfig.permission.compress = globalConfig2.compress.permission;
|
|
14075
|
+
opencodeConfig.permission.panel = "allow";
|
|
13750
14076
|
},
|
|
13751
14077
|
tool: {
|
|
13752
|
-
compress: compressTool
|
|
14078
|
+
compress: compressTool,
|
|
14079
|
+
panel: panelTool
|
|
13753
14080
|
},
|
|
13754
14081
|
"experimental.chat.system.transform": async (input, output) => {
|
|
13755
14082
|
const config2 = getConfig(input.sessionID || "");
|