minecodex 1.2.2 → 2.0.1
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/README.md +9 -3
- package/features/images/README.md +16 -98
- package/features/images/codex-feature.json +6 -23
- package/features/images/src/library-prompt-page.mjs +239 -0
- package/features/model-slider/THIRD_PARTY_NOTICES.md +1 -1
- package/features/notes/DESIGN.md +66 -0
- package/features/notes/README.md +18 -99
- package/features/notes/assets/icons/book-open-nav.svg +15 -0
- package/features/notes/assets/icons/book-open.svg +16 -0
- package/features/notes/assets/native-emojis.json +1 -0
- package/features/notes/assets/native-prompt-icons.json +28 -0
- package/features/notes/codex-feature.json +12 -70
- package/features/notes/src/main.mjs +2 -2
- package/features/notes/src/product.mjs +287 -613
- package/features/notes/src/prompt-schema.mjs +117 -0
- package/features/notes/src/quick-prompts-page.mjs +1559 -0
- package/features/notes/src/quick-prompts-server.mjs +167 -0
- package/features/session-status/DESIGN.md +11 -3
- package/features/session-status/README.md +8 -4
- package/features/session-status/src/session-status.mjs +272 -25
- package/features/sidebar-focus/DESIGN.md +16 -0
- package/features/sidebar-focus/codex-feature.json +20 -0
- package/features/sidebar-focus/src/sidebar-focus.mjs +134 -0
- package/features/sidebar-focus/vendor/lucide-LICENSE +43 -0
- package/features/unread-inbox/DESIGN.md +36 -0
- package/features/unread-inbox/codex-feature.json +20 -0
- package/features/unread-inbox/src/unread-inbox.mjs +446 -0
- package/features/usage-meter/DESIGN.md +49 -0
- package/features/usage-meter/README.md +18 -0
- package/features/usage-meter/codex-feature.json +13 -0
- package/features/usage-meter/src/usage-meter.mjs +200 -0
- package/package.json +17 -5
- package/packages/cli/src/config.mjs +4 -1
- package/packages/cli/src/control-server.mjs +2 -2
- package/packages/runtime-host/src/codex-injection.mjs +38 -3
- package/packages/runtime-host/src/codex-runtime.mjs +41 -20
- package/packages/runtime-host/src/feature-registry.mjs +1 -0
- package/packages/runtime-host/src/session-status-reader.mjs +40 -16
- package/features/notes/assets/icons/circle.svg +0 -15
- package/features/notes/assets/icons/ellipsis.svg +0 -17
- package/features/notes/assets/icons/external-link.svg +0 -17
- package/features/notes/assets/icons/list-todo.svg +0 -19
- package/features/notes/assets/icons/message-square-quote.svg +0 -17
- package/features/notes/assets/icons/paperclip.svg +0 -15
- package/features/notes/assets/icons/star-off.svg +0 -17
- package/features/notes/assets/icons/star.svg +0 -15
- package/features/notes/assets/icons/sticky-note.svg +0 -16
- package/features/notes/assets/icons/trash-2.svg +0 -19
- package/features/notes/src/http-server.mjs +0 -580
- package/features/notes/web/app.js +0 -1202
- package/features/notes/web/codex-artifact-document.svg +0 -1
- package/features/notes/web/codex-attachment.svg +0 -1
- package/features/notes/web/codex-build.svg +0 -1
- package/features/notes/web/codex-code.svg +0 -1
- package/features/notes/web/codex-cplusplus.svg +0 -1
- package/features/notes/web/codex-css.svg +0 -1
- package/features/notes/web/codex-document.svg +0 -1
- package/features/notes/web/codex-file.svg +0 -1
- package/features/notes/web/codex-folder.svg +0 -1
- package/features/notes/web/codex-hashes.svg +0 -1
- package/features/notes/web/codex-html.svg +0 -1
- package/features/notes/web/codex-image.svg +0 -1
- package/features/notes/web/codex-java.svg +0 -1
- package/features/notes/web/codex-javascript.svg +0 -1
- package/features/notes/web/codex-json.svg +0 -1
- package/features/notes/web/codex-notebook.svg +0 -1
- package/features/notes/web/codex-pdf.svg +0 -1
- package/features/notes/web/codex-pencil.svg +0 -3
- package/features/notes/web/codex-php.svg +0 -1
- package/features/notes/web/codex-plus.svg +0 -3
- package/features/notes/web/codex-presentation.svg +0 -1
- package/features/notes/web/codex-python.svg +0 -1
- package/features/notes/web/codex-react.svg +0 -1
- package/features/notes/web/codex-rust.svg +0 -1
- package/features/notes/web/codex-shell.svg +0 -1
- package/features/notes/web/codex-skill.svg +0 -1
- package/features/notes/web/codex-spreadsheet.svg +0 -1
- package/features/notes/web/codex-task.svg +0 -3
- package/features/notes/web/codex-terminal.svg +0 -1
- package/features/notes/web/codex-toml.svg +0 -1
- package/features/notes/web/codex-typescript.svg +0 -1
- package/features/notes/web/codex-view-all.svg +0 -3
- package/features/notes/web/file-icons.mjs +0 -68
- package/features/notes/web/i18n.mjs +0 -237
- package/features/notes/web/index.html +0 -15
- package/features/notes/web/styles.css +0 -1155
- package/features/notes/web/todo-drag.mjs +0 -18
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import nativeIcons from "../assets/native-prompt-icons.json" with { type: "json" };
|
|
3
|
+
import nativeEmojis from "../assets/native-emojis.json" with { type: "json" };
|
|
4
|
+
import { ProductError } from "./product.mjs";
|
|
5
|
+
|
|
6
|
+
// 沿用已有提示词存储;不再暴露 Todo、Files 或旧版网页。
|
|
7
|
+
export function createQuickPromptsServer({
|
|
8
|
+
product,
|
|
9
|
+
host = "127.0.0.1",
|
|
10
|
+
port = 47832,
|
|
11
|
+
instanceId = process.env.MINECODEX_FEATURE_INSTANCE_ID ?? "unconfigured",
|
|
12
|
+
} = {}) {
|
|
13
|
+
if (!product) throw new Error("product is required");
|
|
14
|
+
if (!["127.0.0.1", "localhost", "::1"].includes(host))
|
|
15
|
+
throw new Error("Loopback host required");
|
|
16
|
+
const hostname = host === "::1" ? "[::1]" : host;
|
|
17
|
+
const json = (response, status, value) => {
|
|
18
|
+
response.writeHead(status, {
|
|
19
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
20
|
+
"Cache-Control": "no-store",
|
|
21
|
+
});
|
|
22
|
+
response.end(JSON.stringify(value));
|
|
23
|
+
};
|
|
24
|
+
async function body(request) {
|
|
25
|
+
if (!request.headers["content-type"]?.startsWith("application/json")) {
|
|
26
|
+
throw new ProductError("JSON required", { status: 415 });
|
|
27
|
+
}
|
|
28
|
+
let size = 0;
|
|
29
|
+
const chunks = [];
|
|
30
|
+
for await (const chunk of request) {
|
|
31
|
+
size += chunk.length;
|
|
32
|
+
if (size > 1024 * 1024)
|
|
33
|
+
throw new ProductError("Request too large", { status: 413 });
|
|
34
|
+
chunks.push(chunk);
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(Buffer.concat(chunks).toString() || "{}");
|
|
38
|
+
} catch {
|
|
39
|
+
throw new ProductError("Invalid JSON");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const server = createServer(async (request, response) => {
|
|
43
|
+
try {
|
|
44
|
+
const origin = `http://${hostname}:${server.address().port}`;
|
|
45
|
+
if (
|
|
46
|
+
request.headers.host !== new URL(origin).host ||
|
|
47
|
+
(request.headers.origin &&
|
|
48
|
+
![origin, "app://-"].includes(request.headers.origin))
|
|
49
|
+
) {
|
|
50
|
+
json(response, 403, { error: { message: "Origin not allowed" } });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (request.headers.origin === "app://-") {
|
|
54
|
+
response.setHeader("Access-Control-Allow-Origin", "app://-");
|
|
55
|
+
response.setHeader("Vary", "Origin");
|
|
56
|
+
response.setHeader(
|
|
57
|
+
"Access-Control-Allow-Methods",
|
|
58
|
+
"GET, POST, PATCH, DELETE, OPTIONS",
|
|
59
|
+
);
|
|
60
|
+
response.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
61
|
+
response.setHeader("Access-Control-Allow-Private-Network", "true");
|
|
62
|
+
}
|
|
63
|
+
if (request.method === "OPTIONS") {
|
|
64
|
+
response.writeHead(204);
|
|
65
|
+
response.end();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const url = new URL(request.url, origin);
|
|
69
|
+
if (request.method === "GET" && url.pathname === "/api/health") {
|
|
70
|
+
json(response, 200, {
|
|
71
|
+
ok: true,
|
|
72
|
+
service: "notes",
|
|
73
|
+
protocolVersion: 1,
|
|
74
|
+
instanceId,
|
|
75
|
+
revision: product.revision,
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (request.method === "GET" && url.pathname === "/api/state") {
|
|
80
|
+
const state = await product.getState();
|
|
81
|
+
json(response, 200, {
|
|
82
|
+
revision: state.revision,
|
|
83
|
+
prompts: state.prompts,
|
|
84
|
+
categories: state.categories,
|
|
85
|
+
icons: nativeIcons,
|
|
86
|
+
emojis: nativeEmojis,
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (request.method === "POST" && url.pathname === "/api/prompts") {
|
|
91
|
+
json(response, 201, product.createPrompt(await body(request)));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (request.method === "POST" && url.pathname === "/api/categories") {
|
|
95
|
+
json(
|
|
96
|
+
response,
|
|
97
|
+
201,
|
|
98
|
+
product.savePromptCategory(null, await body(request)),
|
|
99
|
+
);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (request.method === "POST" && url.pathname === "/api/reorder") {
|
|
103
|
+
json(response, 200, product.reorderPrompts(await body(request)));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const category = url.pathname.match(
|
|
107
|
+
/^\/api\/categories\/([a-zA-Z0-9-]+)$/,
|
|
108
|
+
);
|
|
109
|
+
if (category && request.method === "PATCH") {
|
|
110
|
+
json(
|
|
111
|
+
response,
|
|
112
|
+
200,
|
|
113
|
+
product.savePromptCategory(category[1], await body(request)),
|
|
114
|
+
);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (category && request.method === "DELETE") {
|
|
118
|
+
json(response, 200, product.deletePromptCategory(category[1]));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const match = url.pathname.match(/^\/api\/prompts\/([^/]+)(\/use)?$/);
|
|
122
|
+
if (match) {
|
|
123
|
+
const id = decodeURIComponent(match[1]);
|
|
124
|
+
if (match[2] && request.method === "POST") {
|
|
125
|
+
json(response, 200, product.markPromptUsed(id, await body(request)));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!match[2] && request.method === "PATCH") {
|
|
129
|
+
json(response, 200, product.updatePrompt(id, await body(request)));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!match[2] && request.method === "DELETE") {
|
|
133
|
+
product.deletePrompt(id);
|
|
134
|
+
response.writeHead(204);
|
|
135
|
+
response.end();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
json(response, 404, { error: { message: "Not found" } });
|
|
140
|
+
} catch (error) {
|
|
141
|
+
json(response, error instanceof ProductError ? error.status : 500, {
|
|
142
|
+
error: {
|
|
143
|
+
message:
|
|
144
|
+
error instanceof ProductError ? error.message : "Request failed",
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
get origin() {
|
|
151
|
+
return `http://${hostname}:${server.address()?.port ?? port}`;
|
|
152
|
+
},
|
|
153
|
+
listen: () =>
|
|
154
|
+
new Promise((resolve, reject) => {
|
|
155
|
+
server.once("error", reject);
|
|
156
|
+
server.listen(port, host, () => {
|
|
157
|
+
server.off("error", reject);
|
|
158
|
+
resolve();
|
|
159
|
+
});
|
|
160
|
+
}),
|
|
161
|
+
close: () =>
|
|
162
|
+
new Promise((resolve, reject) => {
|
|
163
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
164
|
+
server.closeIdleConnections();
|
|
165
|
+
}),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -18,16 +18,24 @@
|
|
|
18
18
|
| 字体 | `--font-sans-default`、`--text-xs` | 与原生辅助信息一致 |
|
|
19
19
|
| 圆角与阴影 | `--radius-xl`、`--shadow-lg` | 明细弹层 |
|
|
20
20
|
|
|
21
|
-
状态行和输入框共用内容宽度,单行高度为 24px,顶部留 2px 内边距;每个状态按钮高 22px,内边距为上下 3px、左右
|
|
21
|
+
状态行和输入框共用内容宽度,单行高度为 24px,顶部留 2px 内边距;每个状态按钮高 22px,内边距为上下 3px、左右 6px。按钮之间 gap 为 4px,两组可见内容间距为 16px(6 + 4 + 6);按钮内图标与文字 gap 为 6px。状态行展开且可见时,将包含它的原生 `.bottom-0.pb-4` 容器底部 padding 从 16px 覆盖为 4px;收起、隐藏或卸载后自动恢复原生留白,展开的状态行使用 -4px 顶部外边距,将输入框下方原生 8px gap 的实际距离收为4px,收起时恢复。宽度不足时自然换行,不覆盖输入框或悬浮控件。统计图标采用 Lucide Gauge,用量图标采用 Phosphor Database 单路径版本,两者以 14px 绘制;上下文使用与 Codex 原生相同参数的 12px 百分环。弹层由按钮锚定,键盘可聚焦,Escape 和点击外部关闭。遵守系统减少动态效果设置,不添加持续动画。
|
|
22
22
|
|
|
23
|
-
状态行以当前输入框所在的会话为准,不依赖侧栏中可能因项目折叠而卸载的 Thread 行。新聊天没有会话 ID
|
|
23
|
+
状态行以当前输入框所在的会话为准,不依赖侧栏中可能因项目折叠而卸载的 Thread 行。新聊天没有会话 ID 时隐藏旧数据;首条消息建立会话后默认展开详细状态行;若用户曾收起,则沿用其选择。尚未取得的指标用 `—` 占位,不等待日志或 Token 用量;切换会话时立即清空旧数值,详情展开状态保持不变。
|
|
24
24
|
|
|
25
25
|
## 数据呈现
|
|
26
26
|
|
|
27
27
|
界面文案跟随 Codex 的 `html[lang]`:中文语言使用简体中文,其他语言使用英文;运行中切换语言时同步更新状态行和已打开的明细浮窗。MineCodex Web 使用同样的中英文规则提供本功能开关。
|
|
28
28
|
|
|
29
|
-
TPS 根据 Codex
|
|
29
|
+
TPS 根据 Codex 会话事件计算有效样本的会话平均约值,以 `N tok/s` 呈现;分母扣除工具执行区间(含迟到事件,重叠区间仅扣一次),不将其视为实时解码速度;缺少有效计时样本时显示 `—`。累计 token 与缓存比例来自 Codex 用量记录。上下文使用原生 Codex 桌面版的口径:最近请求 `last.totalTokens / modelContextWindow`,直接订阅原生会话的 `latestTokenUsageInfo`(`thread/tokenUsage/updated` 更新),压缩后与原生同步刷新,不依赖日志轮询;原生数据不可用时显示占位;状态栏显示百分环和数字,弹层显示用量、窗口容量和压缩次数。二级浮窗的 token 数按十进制换算为 K/M,省略 `tok` 后缀;TPS 仍保留 `tok/s`。日志可见历史只列有数据的指令、消息、工具交互三个短标签,估算值以 `~` 开头。6px 进度条使用 Codex 图标选择器的紫、蓝、绿配色,标签圆点保持同色;当前页面未暴露对应变量时使用 Codex 亮/暗主题原值。剩余的原生占用以浅灰表示;若估算合计超过原生占用,按比例缩放彩色段,让进度条终点始终对应原生总占用。不显示无法取得的工具定义,也不放解释性长文案。
|
|
30
30
|
|
|
31
31
|
上下文弹层按视觉归属排列:进度条下紧接三个可见历史分段,不加分段副标题;分隔线之后列原生占用合计、窗口容量和压缩次数。合计取原生最近请求用量,不是三个估算分段的求和。
|
|
32
32
|
|
|
33
33
|
会话统计浮窗只显示回合、步骤、首字延迟和 TPS。Token 用量浮窗以 Total 为主项,紧接比例条和输出(紫)、其他输入(蓝)、缓存输入(绿)三项,顺序与上下文弹层一致;分隔线后才显示缓存命中率。其他输入为总输入减缓存输入,包含缓存写入。三项使用同一分母。
|
|
34
|
+
|
|
35
|
+
### 折叠入口
|
|
36
|
+
- 首次安装默认展开;用户收起后在模型选择左侧显示 12px context 环,24px 热区;原生 context 环开启时隐藏原生重复入口,卸载时恢复。
|
|
37
|
+
- Tooltip 沿用原生 context 内容:上下文窗口、使用/剩余比例、已用/容量(k tokens);背景、边框、文字、圆角与阴影使用原生 tooltip tokens。
|
|
38
|
+
- 点击环展开输入框下方详情,右侧 chevron-up 收起;200ms grid 高度与160ms透明度过渡,尊重 reduced-motion。展开状态跨会话、主聊天和 side chat 共享并持久保存。展开时隐藏输入框内的 context 环;收起动效完成后容器 display:none,不保留父级 gap,收起内容不参与键盘焦点。每个输入框订阅自己的会话上下文和统计。
|
|
39
|
+
- Cache 按原始 cachedInputTokens / inputTokens 计算:大于99%显示一位小数,其余取整数,状态栏和详情共用格式。
|
|
40
|
+
|
|
41
|
+
输入框实例仅绑定可见的 thread composer。全屏标签页中的隐藏聊天、浮动 Quick Chat(含 right-panel composer overlay)、inert 区域不注入;原生移动输入框或切换可见性时销毁实例并取消订阅,返回聊天后重建。
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
在 Codex 输入框下方显示当前任务的回合、模型响应步骤、观测 TPS、累计 token 和缓存命中。点击状态行可查看统计、用量及上下文详情。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Context 直接订阅 Codex 原生会话状态。其他统计由 Runtime Host 只读解析本机 `~/.codex/sessions` 中的 Codex 会话事件,再通过已有 CDP 连接向注入脚本发送数值快照;页面只向 Host 请求刷新,不发送对话内容,也不修改 Codex 会话日志。远程任务或缺少本机日志时,无法取得的指标显示 `—`。
|
|
6
6
|
|
|
7
7
|
新聊天没有会话 ID 时隐藏;发送首条消息、原生输入框出现会话 ID 后立即显示占位并请求数据,无需等待日志落盘、回答完成或 Token 用量返回。切换聊天时立即清除旧数值,拒绝 ID 不匹配的迟到快照;折叠侧栏不影响当前会话识别。输入框会话标记的属性变化也会触发更新。
|
|
8
8
|
|
|
9
|
-
生成中依据日志的 `task_started` 与完成/中断事件,每 500ms 请求统计;空闲时由 Runtime 每 2 秒刷新兜底,页面不重复轮询。首次找不到日志按 500ms、1s、2s、随后 10s
|
|
9
|
+
生成中依据日志的 `task_started` 与完成/中断事件,每 500ms 请求统计;空闲时由 Runtime 每 2 秒刷新兜底,页面不重复轮询。首次找不到日志按 500ms、1s、2s、随后 10s 重试;持续缺失时不会高频扫描目录。并发请求按页面合并,统计刷新不占用图片等用户操作的串行队列。页面卸载或功能关闭会取消定时器、观察器与原生状态订阅。
|
|
10
10
|
|
|
11
11
|
统计读取当前会话 ID、推送快照的 CDP 请求分别有 3 秒超时;超时会清理待处理请求、忽略迟到响应,并关闭失效连接,由 Runtime 重新连接。共享 CDP 请求默认上限为 30 秒、连接握手为 10 秒,页面发现 HTTP 请求为 3 秒;超时本身不会自动重放发送消息、附件等操作。
|
|
12
12
|
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
- 回合:唯一 `task_started` / `turn_context` 的 `turn_id`。
|
|
16
16
|
- 步骤:有 `token_usage_record` 的唯一 `response_id`,即已记录的模型响应次数。
|
|
17
17
|
- Token:优先采用逐响应记录的 `thread_token_usage`,旧日志缺少该记录时回退到 `token_count.total_token_usage`;缓存命中为 `cached_input_tokens / input_tokens`。未缓存输入为总输入减缓存读与缓存写。
|
|
18
|
-
- TPS:仅对可配对模型输出条目起点与逐次用量记录终点的响应,计算 `Σ output_tokens / Σ(观测时长 −
|
|
18
|
+
- TPS:仅对可配对模型输出条目起点与逐次用量记录终点的响应,计算 `Σ output_tokens / Σ(观测时长 − 其中工具执行时长)`;重叠工具区间只扣一次,迟到的工具完成事件会回补已有样本,中断或结束回合会清空未配对计时。它是有效样本的会话平均约值,显示为 `N tok/s`,不是实时解码速度。缺少有效计时样本时显示 `—`。
|
|
19
19
|
- Token 构成:Total 下的比例条按输出(紫)、其他输入(蓝)、缓存输入(绿)显示三项在会话总用量中的占比;缓存命中率独立放在末尾,仍以总输入为分母。其他输入为总输入减缓存输入,包含缓存写入。
|
|
20
|
-
- 上下文:与当前 Codex
|
|
20
|
+
- 上下文:与当前 Codex 桌面版原生百分环相同,采用原生状态的 `last.totalTokens / modelContextWindow`,裁切至 0–100%。即使关闭原生百分环,本功能仍订阅其用量数据。二级浮窗在进度条下显示日志可见历史的指令、消息、工具交互本地估算,随后显示原生占用合计、窗口容量和压缩次数;`~` 表示估算,合计并非估算分段之和。进度条采用 Codex 原生配色,以紫、蓝、绿分别表示三类,未分类的占用为浅灰;彩色段按需缩放,整条终点对应原生总占用。工具定义因未写入日志而不显示。
|
|
21
21
|
- 压缩次数:读取会话日志中的 `compacted` 记录及其 `window_number`,兼容旧版 `context_compacted` 和 `ContextCompaction` 完成事件;同一次压缩的多种记录只计一次。若 `compacted` 含 `replacement_history`,分段估算以它替换压缩前历史,再累计后续记录。
|
|
22
22
|
|
|
23
23
|
统计图标使用 Lucide Static v1.31.0 的 Gauge;用量图标使用 Phosphor Icons 的 Database,单路径填充避免半透明色在路径交叠处加深。许可见 [`vendor/lucide-LICENSE`](vendor/lucide-LICENSE) 和 [`vendor/phosphor-LICENSE`](vendor/phosphor-LICENSE)。
|
|
@@ -33,3 +33,7 @@
|
|
|
33
33
|
相关实现调研:[`picoSols/codex-usage-statusline`](https://github.com/picoSols/codex-usage-statusline) 也用 Codex `token_count` 计算总占用;[`auditt98/context-viewer`](https://github.com/auditt98/context-viewer) 用 `tiktoken` 给可见日志文本估算分类,但其 Codex 解析不能还原每次实际请求中的完整提示词和工具定义。[`HarnessDesk` 的 DSH 接入](https://github.com/HarnessDesk/HarnessDesk/blob/main/docs/context-usage.md) 从 DSH 自己的 token meter 接收分类投影,其 Codex 接入没有这项投影。[Codex App Server 用量协议](https://github.com/openai/codex/blob/main/codex-rs/app-server-protocol/schema/json/v2/ThreadTokenUsageUpdatedNotification.json) 只提供输入、缓存、输出、推理等用量种类和上下文窗口,没有按提示词、工具和消息拆分的字段。
|
|
34
34
|
|
|
35
35
|
会话 JSONL 和 Codex DOM 均属版本相关接口,升级 Codex 后需用隔离窗口重新验证。关闭本功能会移除注入节点和观察器。
|
|
36
|
+
|
|
37
|
+
Context 直接订阅原生会话的 `latestTokenUsageInfo`,与原生指示器共用 `thread/tokenUsage/updated` 数据源;压缩完成后随原生状态刷新,不等待日志轮询。切换会话时取消旧订阅,原生接口或用量不可用时显示 `—`,日志快照不覆盖原生占用。日志仍提供累计统计、压缩次数和历史分段估算;日志读取器也接受压缩后的最新重新计数。
|
|
38
|
+
|
|
39
|
+
首次安装默认展开输入框下方的会话统计,之后记住用户的展开或收起选择。详情最右侧箭头可收起,收起后点击模型选择左侧的 context 环重新展开;展开时隐藏输入框内的环,展开状态跨会话及 side chat 共享并记住;收起后不保留布局占位。悬浮显示原生同口径的上下文用量。Cache 超过99%时显示一位小数。
|