Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/zcode-context/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 6 additions & 5 deletions external_plugins/zcode-context/.zcode-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "zcode-context",
"version": "0.3.9",
"description": "只读查看会话用量、模型输入组成、请求差异和压缩事件。",
"version": "0.3.10",
"description": "只读查看会话用量、模型输入组成、请求差异和压缩事件。装好后点右上角「切换面板」,在会话侧边栏打开「上下文」面板;建议同时在「设置 → 通用」打开「完整保留模型 I/O」——默认记录会丢弃更早的请求,且无法事后恢复。",
"description_i18n": {
"en": "Read-only session usage, model input composition, request differences and compaction events.",
"zh-CN": "只读查看会话用量、模型输入组成、请求差异和压缩事件。"
"en": "Read-only session usage, model input composition, request differences and compaction events. After installing, open the Context panel from the session sidebar (top-right panel switcher); also turn on full Model I/O retention in Settings → General — the default recording drops older requests and cannot recover them later.",
"zh-CN": "只读查看会话用量、模型输入组成、请求差异和压缩事件。装好后点右上角「切换面板」,在会话侧边栏打开「上下文」面板;建议同时在「设置 → 通用」打开「完整保留模型 I/O」——默认记录会丢弃更早的请求,且无法事后恢复。"
},
"author": {
"name": "Kevinzzz6"
Expand Down Expand Up @@ -52,5 +52,6 @@
"availability": "session"
}
]
}
},
"homepage": "https://github.com/Kevinzzz6/zcode-context#默认记录与完整保留"
}
14 changes: 13 additions & 1 deletion external_plugins/zcode-context/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,15 @@ Clicking an event opens the exact item it brought into the context; a compaction

## Install

Install from the ZCode plugin marketplace, then enable **zcode-context**. A **Context** side pane becomes available for sessions in local workspaces.
Install from the ZCode plugin marketplace, then enable **zcode-context**. In a local workspace session, use the top-right panel switcher to open **Context** in the session sidebar.

## Recording and retention

For complete future request inputs, enable **full Model I/O retention** in **Settings → General**. Default recording keeps only a trailing portion of each request's messages plus later deltas, and older session record files are periodically pruned. Missing historical content cannot be recovered by enabling the setting later.

The overview shows a reminder about this setting. Closing it saves the dismissal in local storage; the reminder does not detect whether full retention is already enabled.

Full retention does not remove the plugin's own reading and cache limits. When first loading a record file, the plugin reads only its last 24 MiB and keeps a bounded request cache. A history-window notice can therefore remain even when newer requests are fully recorded; check each request's completeness indicator to distinguish incomplete input from unloaded history.

## Components

Expand Down Expand Up @@ -55,6 +63,10 @@ The plugin reads ZCode CLI's local records only — the session database (`db/db

Source lives in [`ui-plugins/zcode-context/`](../../ui-plugins/zcode-context). Build with `pnpm install` and `pnpm build` from the repository root; tests run with `node --test test/*.test.mjs`. Upstream project: <https://github.com/Kevinzzz6/zcode-context>.

## Acknowledgements

UI and interaction design draws on [dsh-context](https://github.com/bowenliang123/dsh-context), reimplemented for ZCode. Thanks to its author for sharing the project.

## License

[MIT](./LICENSE)
14 changes: 13 additions & 1 deletion external_plugins/zcode-context/README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,15 @@

## 安装

在 ZCode 插件市场安装并启用 **zcode-context**,本地工作区的会话即可使用「上下文」侧栏面板。
在 ZCode 插件市场安装并启用 **zcode-context**。进入本地工作区的会话后,点击右上角「切换面板」,在会话侧边栏打开「上下文」。

## 记录与保留

建议在「设置 → 通用」开启「完整保留模型 I/O」,让之后的请求完整记录输入。默认记录只保留每个请求末尾的一段消息及之后的增量,更早会话的记录文件也会定期清理。开启设置无法补回历史上没有记录或已经清理的内容。

概览上方会显示相关提醒。点击关闭后,关闭状态保存在本地存储中;这条提醒不会检测你是否已经开启完整保留。

完整保留不会解除插件自身的读取和缓存限制。首次加载记录文件时,插件只读取末尾 24 MiB,并仅缓存一定范围的请求。因此,即使新的请求已经完整记录,历史读取范围的提示仍可能存在;请结合每条请求的完整性标记,区分「请求输入没有记录全」和「更早的历史没有加载」。

## 组件

Expand Down Expand Up @@ -55,6 +63,10 @@

源码位于 [`ui-plugins/zcode-context/`](../../ui-plugins/zcode-context)。在仓库根目录执行 `pnpm install` 和 `pnpm build` 构建;测试用 `node --test test/*.test.mjs` 运行。上游项目:<https://github.com/Kevinzzz6/zcode-context>。

## 致谢

界面与交互设计参考了 [dsh-context](https://github.com/bowenliang123/dsh-context),并针对 ZCode 重新实现。感谢原作者开源分享。

## 许可证

[MIT](./LICENSE)
22 changes: 7 additions & 15 deletions marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -1142,34 +1142,26 @@
{
"name": "zcode-context",
"source": "./external_plugins/zcode-context",
"description": "Read-only session usage, model input composition, request differences and compaction events.",
"description": "Read-only session usage, model input composition, request differences and compaction events. After installing, open the Context panel from the session sidebar (top-right panel switcher); also turn on full Model I/O retention in Settings → General — the default recording drops older requests and cannot recover them later.",
"description_i18n": {
"en": "Read-only session usage, model input composition, request differences and compaction events.",
"zh-CN": "只读查看会话用量、模型输入组成、请求差异和压缩事件。"
"en": "Read-only session usage, model input composition, request differences and compaction events. After installing, open the Context panel from the session sidebar (top-right panel switcher); also turn on full Model I/O retention in Settings → General — the default recording drops older requests and cannot recover them later.",
"zh-CN": "只读查看会话用量、模型输入组成、请求差异和压缩事件。装好后点右上角「切换面板」,在会话侧边栏打开「上下文」面板;建议同时在「设置 → 通用」打开「完整保留模型 I/O」——默认记录会丢弃更早的请求,且无法事后恢复。"
},
"version": "0.3.9",
"homepage": "https://github.com/Kevinzzz6/zcode-context#默认记录与完整保留",
"version": "0.3.10",
"author": {
"name": "Kevinzzz6",
"url": "https://github.com/Kevinzzz6"
},
"category": "developer-tools",
"icon": "https://cdn-zcode.z.ai/zcode/official-plugin/assets/zcode-context/icon.png",
"keywords": [
"context",
"tokens",
"usage",
"session",
"mcp"
],
"examplePrompts": [
"Show the context composition of this session and which category takes the most room",
"What did the last compaction summarize away?"
],
"examplePrompts_i18n": {
"zh-CN": [
"看看这个会话的上下文组成,哪一类内容占得最多",
"上一次压缩摘要掉了哪些内容?"
]
}
]
},
{
"name": "jupyter-notebook",
Expand Down
2 changes: 1 addition & 1 deletion ui-plugins/zcode-context/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zcode-context",
"version": "0.3.9",
"version": "0.3.10",
"private": true,
"type": "module",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion ui-plugins/zcode-context/src/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { createReader } from "./data/reader.mjs";
const HTML_URI = "ui://zcode-context/panel.html";
const reader = createReader(() => new Worker(new URL("./worker.mjs", import.meta.url), { workerData: { paths: resolveDataPaths() }, resourceLimits: { maxOldGenerationSizeMb: 256 } }));
export function createContextServer() {
const server = new McpServer({name:"zcode-context",version:"0.3.9"});
const server = new McpServer({name:"zcode-context",version:"0.3.10"});
server.registerResource("context_panel", HTML_URI, { mimeType:"text/html;profile=mcp-app",
_meta:{ui:{csp:{connectDomains:[],resourceDomains:[]}}} },
async () => ({contents:[{uri:HTML_URI,mimeType:"text/html;profile=mcp-app",text:await readFile(new URL("./context.html",import.meta.url),"utf8")}]})
Expand Down
7 changes: 5 additions & 2 deletions ui-plugins/zcode-context/ui/app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { translator, ORDER } from "./i18n.mjs";
import { renderApp, isAgent, CHUNK, layoutTrend, highlight, anchors, olderUnloaded, BOUNDARY, eventId, expandedRoles } from "./components.mjs";
import { demoCall } from "./demo.mjs";
const root=document.getElementById("root");
const INFO={name:"zcode-context",version:"0.3.9"};
const INFO={name:"zcode-context",version:"0.3.10"};
const RETENTION_KEY="zcode-context:retention-dismissed";
let retentionDismissed=false;try{retentionDismissed=localStorage.getItem(RETENTION_KEY)==="1";}catch{}
const demo=new URLSearchParams(location.search).get("demo")==="1";
let app=null,t=translator(),view=initialView(),boundSession=null,overview=null,items=[],hasOlder=false,historyPage=0;
let detail=null,content=null,more={},notice=null,busy=false,activeLoads=0;
Expand Down Expand Up @@ -47,7 +49,7 @@ function render(background=false) {
const y=window.scrollY,agents=items.filter(isAgent),index=agents.findIndex(x=>x.key===view.selectedKey);
const selected=items.find(x=>x.key===view.selectedKey)??(detail?.summary?.key===view.selectedKey?detail.summary:null);
const newerCount=index>=0?agents.length-1-index:selected?agents.filter(x=>x.startedAt>selected.startedAt).length:0;
root.replaceChildren(renderApp({t,view,lens:lens(view),version:INFO.version,overview,items,selected,detail,content,more,eventDetails,hasOlder,notice,busy,demo,latestKey:latestKey(),newerCount,session:boundSession,actions}));
root.replaceChildren(renderApp({t,view,lens:lens(view),version:INFO.version,overview,items,selected,detail,content,more,eventDetails,hasOlder,notice,busy,demo,latestKey:latestKey(),newerCount,session:boundSession,actions,retentionDismissed}));
for(const el of root.querySelectorAll("[data-scroll]")) {
const saved=scrolls.get(el.dataset.scroll);if(!saved)continue;
el.scrollTop=saved[1];el.scrollLeft=keepRightEdge&&el.dataset.scroll==="trend"?el.scrollWidth-keepRightEdge:saved[0];
Expand Down Expand Up @@ -352,6 +354,7 @@ const actions={
view={...view,eventKinds:next.length&&next.length<kinds.length?next:null};persist();render();
},
refresh(force=true){refresh(force);},
dismissRetention(){try{localStorage.setItem(RETENTION_KEY,"1");}catch{}retentionDismissed=true;render();},
page(page){view={...view,page};persist();render();window.scrollTo(0,0);},
set(patch){view={...view,...patch};persist();render();},
remember(patch){view={...view,...patch};persist();},
Expand Down
18 changes: 13 additions & 5 deletions ui-plugins/zcode-context/ui/components.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,20 @@ export function renderApp(ctx) {
if(!ctx.overview){add(node("section",{class:"card"},node("p",{class:"muted",id:"loading"},ctx.t("loading"))));return page;}
if(view.page==="usage"){add(usagePage(ctx),about(ctx));return page;}
if(!ctx.items.length) {
add(node("section",{class:"card empty"},node("h2",{},ctx.t("empty")),node("p",{class:"muted"},ctx.t("emptyHint"))),eventsCard(ctx),about(ctx));
add(retentionBanner(ctx),node("section",{class:"card empty"},node("h2",{},ctx.t("empty")),node("p",{class:"muted"},ctx.t("emptyHint"))),eventsCard(ctx),about(ctx));
return page;
}
add(pinnedBar(ctx),summaryCard(ctx),trendCard(ctx),browserCard(ctx),eventsCard(ctx),about(ctx));
add(retentionBanner(ctx),pinnedBar(ctx),summaryCard(ctx),trendCard(ctx),browserCard(ctx),eventsCard(ctx),about(ctx));
return page;
}
// Default recording drops older messages irrecoverably, so the panel shows where the retention setting is on first
// open; dismissing it once hides it for good.
function retentionBanner(ctx) {
const {t,actions:a}=ctx;
if(ctx.retentionDismissed)return null;
return node("section",{class:"card retention",id:"retention"},node("p",{class:"note"},node("span",{},t("retentionBanner")),spacer(),
button("×",()=>a.dismissRetention(),{class:"icon-btn",id:"retention-dismiss",title:t("dismiss"),ariaLabel:t("dismiss")})));
}

function toolbar(ctx) {
const {t,view,items,actions:a}=ctx,bar=node("div",{class:"toolbar"});
Expand Down Expand Up @@ -187,7 +195,7 @@ function noticeBar(ctx) {
}

function summaryCard(ctx) {
const {t,view,selected:s,detail:d,actions:a,latestKey}=ctx;
const {t,view,selected:s,actions:a,latestKey}=ctx;
if(!s)return null;
const buckets=bucketsOf(s),sum=buckets.reduce((n,x)=>n+x.estimatedTokens,0)||1;
const live=view.follow&&view.selectedKey===latestKey;
Expand Down Expand Up @@ -603,9 +611,9 @@ function bodyText(ctx,c) {
return el;
}
function bodyFoot(ctx,c) {
const {t,view,actions:a}=ctx,p=view.open,parts=Math.ceil(c.totalChars/CHUNK),part=Math.floor(c.offset/CHUNK)+1;
const {t,view,actions:a}=ctx,p=view.open,parts=Math.ceil(c.totalChars/CHUNK);
const keys=e=>{
if(e.key==="Enter"){e.preventDefault();e.shiftKey?a.prevMatch():a.nextMatch();}
if(e.key==="Enter"){e.preventDefault();if(e.shiftKey)a.prevMatch();else a.nextMatch();}
else if(e.key==="Escape"&&e.target.value){e.preventDefault();e.target.value="";a.bodySearch("");}
};
const position=p.matchIndex>=0&&c.matchCount?t("matchOf",{i:p.matchIndex+1,n:full(c.matchCount)}):t("matches",{n:full(c.matchCount)});
Expand Down
4 changes: 3 additions & 1 deletion ui-plugins/zcode-context/ui/i18n.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ const messages={
unrecorded:["更早消息(未记录)","Earlier messages (not recorded)"],
unrecordedTip:["实际输入减去已记录部分的估算;这部分内容没有被记录,无法查看或按类别拆分","Actual input minus the recorded estimate; this content was not recorded and cannot be viewed or split by category"],
actualOnly:["记录不完整,只能显示实际输入的变化,无法按类别拆分","Partial record: only the change in actual input is known, not per category"],
partialHint:["这条记录只保留了最近 {n} 条消息(共 {total} 条),更早的内容无法查看,变化也无法按类别对比。在 ZCode「设置 → 通用」打开「完整保留模型 I/O」后,之后的请求会记录完整内容。","This record keeps only the latest {n} of {total} messages, so earlier content cannot be viewed or compared by category. Turn on complete Model I/O retention in ZCode Settings → General to record full input for later requests."],vsPrevious:["较上一步","vs previous"],notComparable:["无法对比","Not comparable"],
partialHint:["这条记录只保留了最近 {n} 条消息(共 {total} 条),更早的内容无法查看,变化也无法按类别对比。在 ZCode「设置 → 通用」打开「完整保留模型 I/O」后,之后的请求会记录完整内容。","This record keeps only the latest {n} of {total} messages, so earlier content cannot be viewed or compared by category. Turn on complete Model I/O retention in ZCode Settings → General to record full input for later requests."],
retentionBanner:["ZCode 默认只记录每个请求末尾的一段消息和之后的增量,更早的部分不会被记录;更早会话的记录文件也会定期清理,丢失的内容无法事后恢复。建议在「设置 → 通用」打开「完整保留模型 I/O」,之后的请求才会完整记录。","By default ZCode records only a trailing window of each request's messages plus later deltas, so earlier content is never recorded; older sessions' record files are also pruned and what is dropped cannot be recovered later. Consider turning on full Model I/O retention in Settings → General so later requests are recorded in full."],
dismiss:["知道了","Dismiss"],vsPrevious:["较上一步","vs previous"],notComparable:["无法对比","Not comparable"],
actualIn:["实际输入","Actual in"],output:["输出","Output"],cache:["缓存","Cache"],
briefTurn:["本轮","Turn"],briefInput:["输入","In"],briefReply:["回复","Reply"],noInputs:["(无新增)","(nothing new)"],moreItems:["还有 {n} 项","+{n} more"],
briefTurnTip:["这一轮开场时用户发送的消息;同一轮的每一步都显示这一条","The message that opened this turn; every step of the turn shows it"],
Expand Down
3 changes: 2 additions & 1 deletion ui-plugins/zcode-context/ui/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ h1,h2,h3{margin:0}
.btn:hover:not(:disabled){background:var(--bg2)}.btn:disabled{opacity:.4}
.btn.small{height:24px;padding:0 8px;font-size:11.5px}
.btn.ghost{border-color:transparent;background:transparent;color:var(--muted)}.btn.ghost:hover:not(:disabled){color:var(--fg)}
.btn.accent{border-color:transparent;background:var(--accent);color:#fff;font-weight:600}.btn.accent:hover{background:var(--accent);filter:brightness(1.05)}
.btn.accent{border-color:transparent;background:var(--accent);color:var(--bg);font-weight:600}.btn.accent:hover{background:var(--accent);filter:brightness(1.05)}
.btn.on{border-color:var(--accent);color:var(--accent)}

.pinned,.notice{display:flex;align-items:center;gap:8px;padding:6px 6px 6px 10px;border-radius:9px;font-size:12px}
Expand Down Expand Up @@ -127,6 +127,7 @@ html[lang^=zh] .brief-label{width:28px}
.mini-select{height:24px;border:1px solid var(--border);border-radius:6px;background:var(--bg);font-size:11.5px;padding:0 4px;max-width:160px}
.note{margin:0 0 8px;padding:6px 9px;border-radius:7px;background:var(--bg2);color:var(--muted);font-size:11.5px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.el-body .note{margin:0}
.retention>.note{margin:0;align-items:flex-start}
.pad{margin:6px 10px;font-size:12px}
.categories{display:flex;flex-direction:column;gap:6px}
.category{border:1px solid var(--border);border-radius:9px;overflow:hidden}
Expand Down
Loading