@michengai/dsh-codex-ui 1.1.6 → 1.1.8
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/CHANGELOG.md +45 -0
- package/CHANGELOG.zh-CN.md +45 -0
- package/cordis.patch.yml +2 -0
- package/lib/client.js +6 -19
- package/lib/index.mjs +204 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,51 @@
|
|
|
4
4
|
|
|
5
5
|
This changelog records recent releases of DSH Codex UI and its one-click installer. Earlier changes remain available in the [Git history](https://github.com/MichengAI/dsh-codex-ui/commits/main).
|
|
6
6
|
|
|
7
|
+
## 1.1.8 - 2026-09-15
|
|
8
|
+
|
|
9
|
+
- After the first user message, new chats are titled once as emoji, type, and theme, for example `⚡ 优化|批次文字显示`. Dates are not written.
|
|
10
|
+
- A manual rename is pinned by the host and later messages do not overwrite it.
|
|
11
|
+
|
|
12
|
+
## suite-installer-v1.0.10 - 2026-09-14
|
|
13
|
+
|
|
14
|
+
Install or maintain 11 first-party plugins in one step. Included plugins and versions:
|
|
15
|
+
|
|
16
|
+
- `@michengai/dsh-archive-manager@0.1.42`
|
|
17
|
+
- `@michengai/dsh-codex-ui@1.1.7`
|
|
18
|
+
- `@michengai/dsh-skills-manager@0.1.50`
|
|
19
|
+
- `@michengai/dsh-agency-agents@0.1.42`
|
|
20
|
+
- `@michengai/dsh-im-connect@0.1.49`
|
|
21
|
+
- `@michengai/dsh-automation@0.1.42`
|
|
22
|
+
- `@michengai/dsh-btw@0.1.7`
|
|
23
|
+
- `@michengai/dsh-simplify@0.1.4`
|
|
24
|
+
- `@michengai/dsh-pua@0.3.11`
|
|
25
|
+
- `@michengai/dsh-code-review@0.1.0`
|
|
26
|
+
- `@michengai/dsh-codex-pet@0.1.5`
|
|
27
|
+
|
|
28
|
+
The installer uses these exact versions for reproducible installation.
|
|
29
|
+
|
|
30
|
+
## 1.1.7 - 2026-09-14
|
|
31
|
+
|
|
32
|
+
- Restore the billing plugin's native sidebar entry styles by removing Codex UI overrides for its card, icons, text, and expanded/collapsed appearance. Clicking the entry still opens the existing usage statistics settings page.
|
|
33
|
+
|
|
34
|
+
## suite-installer-v1.0.9 - 2026-09-14
|
|
35
|
+
|
|
36
|
+
Install or maintain 11 first-party plugins in one step. Included plugins and versions:
|
|
37
|
+
|
|
38
|
+
- `@michengai/dsh-archive-manager@0.1.41`
|
|
39
|
+
- `@michengai/dsh-codex-ui@1.1.6`
|
|
40
|
+
- `@michengai/dsh-skills-manager@0.1.50`
|
|
41
|
+
- `@michengai/dsh-agency-agents@0.1.42`
|
|
42
|
+
- `@michengai/dsh-im-connect@0.1.48`
|
|
43
|
+
- `@michengai/dsh-automation@0.1.41`
|
|
44
|
+
- `@michengai/dsh-btw@0.1.7`
|
|
45
|
+
- `@michengai/dsh-simplify@0.1.4`
|
|
46
|
+
- `@michengai/dsh-pua@0.3.11`
|
|
47
|
+
- `@michengai/dsh-code-review@0.1.0`
|
|
48
|
+
- `@michengai/dsh-codex-pet@0.1.5`
|
|
49
|
+
|
|
50
|
+
The installer uses these exact versions for reproducible installation.
|
|
51
|
+
|
|
7
52
|
## 1.1.6 - 2026-09-14
|
|
8
53
|
|
|
9
54
|
- Fix the Web page freezing on initial load or refresh in a narrow window when the sidebar starts collapsed.
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -4,6 +4,51 @@
|
|
|
4
4
|
|
|
5
5
|
本日志记录 DSH Codex UI 及其一键安装器的最近发布;更早的变更可查看 [Git 提交历史](https://github.com/MichengAI/dsh-codex-ui/commits/main)。
|
|
6
6
|
|
|
7
|
+
## 1.1.8 - 2026-09-15
|
|
8
|
+
|
|
9
|
+
- 新会话在第一条用户消息后,按「表情 + 类型|主题」自动命名一次,例如「⚡ 优化|批次文字显示」。不再写入日期。
|
|
10
|
+
- 手动改名后由宿主钉死,后续消息不会再覆盖。
|
|
11
|
+
|
|
12
|
+
## suite-installer-v1.0.10 - 2026-09-14
|
|
13
|
+
|
|
14
|
+
一键维护安装 11 个自研插件。包含以下插件及版本:
|
|
15
|
+
|
|
16
|
+
- `@michengai/dsh-archive-manager@0.1.42`
|
|
17
|
+
- `@michengai/dsh-codex-ui@1.1.7`
|
|
18
|
+
- `@michengai/dsh-skills-manager@0.1.50`
|
|
19
|
+
- `@michengai/dsh-agency-agents@0.1.42`
|
|
20
|
+
- `@michengai/dsh-im-connect@0.1.49`
|
|
21
|
+
- `@michengai/dsh-automation@0.1.42`
|
|
22
|
+
- `@michengai/dsh-btw@0.1.7`
|
|
23
|
+
- `@michengai/dsh-simplify@0.1.4`
|
|
24
|
+
- `@michengai/dsh-pua@0.3.11`
|
|
25
|
+
- `@michengai/dsh-code-review@0.1.0`
|
|
26
|
+
- `@michengai/dsh-codex-pet@0.1.5`
|
|
27
|
+
|
|
28
|
+
安装器使用这些精确版本执行安装,确保可复现。
|
|
29
|
+
|
|
30
|
+
## 1.1.7 - 2026-09-14
|
|
31
|
+
|
|
32
|
+
- 恢复侧栏用量入口的插件原生样式,移除 Codex UI 对卡片、图标、文字和宽窄态外观的覆盖;点击入口仍打开现有用量统计设置页。
|
|
33
|
+
|
|
34
|
+
## suite-installer-v1.0.9 - 2026-09-14
|
|
35
|
+
|
|
36
|
+
一键维护安装 11 个自研插件。包含以下插件及版本:
|
|
37
|
+
|
|
38
|
+
- `@michengai/dsh-archive-manager@0.1.41`
|
|
39
|
+
- `@michengai/dsh-codex-ui@1.1.6`
|
|
40
|
+
- `@michengai/dsh-skills-manager@0.1.50`
|
|
41
|
+
- `@michengai/dsh-agency-agents@0.1.42`
|
|
42
|
+
- `@michengai/dsh-im-connect@0.1.48`
|
|
43
|
+
- `@michengai/dsh-automation@0.1.41`
|
|
44
|
+
- `@michengai/dsh-btw@0.1.7`
|
|
45
|
+
- `@michengai/dsh-simplify@0.1.4`
|
|
46
|
+
- `@michengai/dsh-pua@0.3.11`
|
|
47
|
+
- `@michengai/dsh-code-review@0.1.0`
|
|
48
|
+
- `@michengai/dsh-codex-pet@0.1.5`
|
|
49
|
+
|
|
50
|
+
安装器使用这些精确版本执行安装,确保可复现。
|
|
51
|
+
|
|
7
52
|
## 1.1.6 - 2026-09-14
|
|
8
53
|
|
|
9
54
|
- 修复窄窗口下首次打开或刷新 Web 时,侧栏初始收起可能导致页面卡住的问题。
|
package/cordis.patch.yml
CHANGED
package/lib/client.js
CHANGED
|
@@ -5894,9 +5894,12 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
5894
5894
|
if (handle !== null && handle.style.left !== contentWidth) handle.style.left = contentWidth;
|
|
5895
5895
|
return changed;
|
|
5896
5896
|
}
|
|
5897
|
+
/** 自动宽度调整与初始化过渡共用前置条件;手动拖拽不受此限制。 */
|
|
5898
|
+
function canApplySlimSidebar(frame) {
|
|
5899
|
+
return !frame.hasAttribute("data-dragging") && !frame.hasAttribute("data-sidebar-collapsed") && parseSidebarGrid(frame.style.gridTemplateColumns) !== void 0;
|
|
5900
|
+
}
|
|
5897
5901
|
function applySlimSidebar(frame) {
|
|
5898
|
-
if (frame
|
|
5899
|
-
if (parseSidebarGrid(frame.style.gridTemplateColumns) === void 0 || frame.hasAttribute("data-sidebar-collapsed")) return false;
|
|
5902
|
+
if (!canApplySlimSidebar(frame)) return false;
|
|
5900
5903
|
const initialized = frame.hasAttribute("data-dcu-codex-sidebar-initialized");
|
|
5901
5904
|
const changed = applySidebarWidth(frame, initialized ? visibleSidebarWidths.get(frame) ?? 240 : 240);
|
|
5902
5905
|
if (!initialized) frame.setAttribute("data-dcu-codex-sidebar-initialized", "");
|
|
@@ -5942,7 +5945,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
5942
5945
|
try {
|
|
5943
5946
|
if (frame === void 0 || !frame.isConnected) watchFrame(findSidebarFrame(document));
|
|
5944
5947
|
if (frame !== void 0) {
|
|
5945
|
-
if (
|
|
5948
|
+
if (!canApplySlimSidebar(frame)) return;
|
|
5946
5949
|
const restoreTransition = frame.hasAttribute("data-dcu-codex-sidebar-initialized") ? void 0 : pauseInitialSidebarTransition(frame);
|
|
5947
5950
|
const changed = applySlimSidebar(frame);
|
|
5948
5951
|
if (restoreTransition !== void 0) {
|
|
@@ -6405,24 +6408,8 @@ body[data-we-sidebar-glass] .dcu-expanded-shell,body[data-we-sidebar-glass] .dcu
|
|
|
6405
6408
|
.dcu-menu-icon{display:grid;place-items:center start;width:20px;height:20px}.dcu-menu-icon svg,.dcu-footer-link svg{display:block;width:16px;height:16px;color:var(--dcu-sidebar-icon)}.dcu-menu button:disabled{color:var(--dcu-sidebar-secondary);cursor:default;opacity:1}.dcu-menu button:disabled svg{color:var(--dcu-sidebar-secondary)}
|
|
6406
6409
|
.dcu-extensions-group{display:grid}.dcu-extension-leading{position:relative;display:block;width:16px;height:16px}.dcu-extension-leading svg{position:absolute;inset:0;transition:opacity 140ms ease-out,transform 220ms cubic-bezier(.16,1,.3,1)}.dcu-extension-default-icon{opacity:1}.dcu-extension-state-arrow{opacity:0;transform:rotate(0)}.dcu-extensions-group:hover .dcu-extension-default-icon,.dcu-extensions-toggle:focus-visible .dcu-extension-default-icon{opacity:0}.dcu-extensions-group:hover .dcu-extension-state-arrow,.dcu-extensions-toggle:focus-visible .dcu-extension-state-arrow{opacity:1}.dcu-extensions-toggle[aria-expanded=true] .dcu-extension-state-arrow{transform:rotate(90deg)}.dcu-extension-panel{display:grid;grid-template-rows:1fr;opacity:1;transition:grid-template-rows 220ms cubic-bezier(.16,1,.3,1),opacity 160ms ease-out}.dcu-extension-panel[data-open=false]{grid-template-rows:0fr;opacity:0;pointer-events:none}.dcu-extension-panel-inner{position:relative;min-height:0;overflow:hidden}.dcu-extension-items{position:relative;display:grid;gap:1px;margin:1px 0 4px 28px}.dcu-extension-items::before{content:"";position:absolute;left:-16px;top:0;bottom:4px;width:1px;background:var(--dcu-sidebar-border)}.dcu-extension-items button{grid-template-columns:minmax(0,1fr);min-height:32px;color:var(--dcu-sidebar-secondary);font-size:13px;font-weight:400}.dcu-extension-items .dcu-menu-icon{display:none}
|
|
6407
6410
|
.dcu-workspaces{display:flex;min-height:0;flex:1;flex-direction:column;margin-top:2px;padding-top:8px;border-top:1px solid var(--dcu-sidebar-border)}.dcu-workspaces.dcu-workspaces-tabs{padding-top:0;border-top:0}.dcu-im-tabs{display:flex;gap:16px;margin:0 8px 12px;padding:0;border-bottom:1px solid var(--dcu-sidebar-border)}.dcu-im-tab{appearance:none;border:0;background:transparent;color:var(--dcu-sidebar-secondary);padding:8px 0 7px;font:14px/22px var(--dcu-font);font-weight:500;cursor:pointer}.dcu-im-tab[data-on=true]{color:var(--dcu-sidebar-primary);font-weight:600;box-shadow:inset 0 -2px 0 currentColor}.dcu-native-workspaces{display:flex;min-height:0;flex:1}.dcu-native-workspaces>*{min-width:0;flex:1}.dcu-native-workspaces .ima-tabs,.dcu-native-workspaces [role=tablist]{display:none!important}.dcu-schedule-browser{display:flex;min-height:0;flex:1;flex-direction:column}.dcu-native-workspaces .dcu-schedule-views{display:flex!important;flex:none;min-height:30px;margin:0 8px 8px;padding:2px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;background:rgba(255,255,255,.025)}.dcu-schedule-views button{appearance:none;flex:1;min-width:0;height:24px;border:0;border-radius:6px;background:transparent;color:var(--dcu-sidebar-secondary);font:600 12px/18px var(--dcu-font);cursor:pointer}.dcu-schedule-views button[aria-selected=true]{background:var(--dcu-sidebar-hover);color:var(--dcu-sidebar-primary);box-shadow:inset 0 0 0 1px var(--dcu-sidebar-border)}.dcu-schedule-pane{display:flex;min-height:0;flex:1}.dcu-schedule-pane>*{min-width:0;flex:1}.dcu-schedule-pane>[data-slot="sidebar.schedule"]{display:flex!important;width:100%;min-width:0;flex:1}.dcu-schedule-pane>[data-slot="sidebar.schedule"]>.dsh-st-rail{width:100%;min-width:0;padding-right:8px;scrollbar-gutter:auto}.dcu-schedule-pane .dsh-st-overview{padding-right:8px}.dcu-foot{display:grid;width:100%;gap:4px;padding:8px 6px 12px;border-top:1px solid var(--dcu-sidebar-border);transition:opacity 500ms cubic-bezier(.16,1,.3,1),transform 500ms cubic-bezier(.16,1,.3,1)}.dcu-root.dcu-collapsing>.dcu-foot{opacity:0;transform:translateX(-4px);pointer-events:none}.dcu-footer-actions:empty,.dcu-settings-seat:empty{display:none}.dcu-settings-seat>button{width:100%;min-height:36px;padding-left:4px!important;color:var(--dcu-sidebar-navigation);font:14px/20px var(--dcu-font);font-weight:400}.dcu-compact{width:100%;align-items:flex-start;overflow:hidden;padding:10px 0 8px}.dcu-compact-nav{display:flex;flex:1;min-height:0;flex-direction:column;align-items:center;gap:2px;overflow:auto;padding:6px 0}.dcu-compact .dcu-icon{width:36px;height:36px;flex:none}.dcu-compact .dcu-foot{width:36px;margin-top:auto;margin-left:10px;padding:8px 0;border-top:0}.dcu-compact .dcu-settings-seat{width:36px;overflow:hidden}.dcu-compact .dcu-settings-seat>button{display:grid;place-items:center;width:36px;min-height:36px;padding:0!important;font-size:0!important;line-height:0}.dcu-compact .dcu-settings-seat>button svg{width:16px;height:16px}.dcu-compact .dcu-footer-link{display:flex;justify-content:center;width:36px;padding:0;font-size:0}.dcu-compact .dcu-footer-link svg{width:16px;height:16px}
|
|
6408
|
-
/* 仅适配费用插件入口;通过标记及入口结构定位,保留原生统计、浮层和容器查询。 */
|
|
6409
|
-
.dcu-footer-actions [data-testid="billing-trigger"]{box-sizing:border-box;width:100%;min-width:0;min-height:52px;padding:8px 6px;gap:8px;border:0;border-radius:6px;background:transparent;color:var(--dcu-sidebar-primary);font-family:var(--dcu-font);box-shadow:none;transform:none;transition:background-color 120ms ease-out,color 120ms ease-out}
|
|
6410
|
-
.dcu-footer-actions :is([data-testid="billing-trigger"],[data-testid="billing-rail-button"]):hover{border-color:transparent;background:var(--dcu-sidebar-hover);box-shadow:none;transform:none}
|
|
6411
|
-
.dcu-footer-actions :is([data-testid="billing-trigger"],[data-testid="billing-rail-button"]):focus-visible{background:var(--dcu-sidebar-hover);outline:2px solid var(--dcu-sidebar-icon);outline-offset:-2px;box-shadow:none;transform:none}
|
|
6412
|
-
.dcu-footer-actions [data-testid="billing-trigger-icon"]{flex:none;width:16px;height:20px;border:0;border-radius:0;background:transparent;color:var(--dcu-sidebar-icon);opacity:1}
|
|
6413
|
-
/* 费用图形仅占 24px 画布中的 16px;补偿内部留白,使可见宽度与设置图标一致。 */
|
|
6414
|
-
.dcu-footer-actions [data-testid="billing-trigger-icon"] svg{flex:none;width:24px;height:24px;stroke-width:1.5px}
|
|
6415
|
-
.dcu-footer-actions [data-testid="billing-trigger-icon"]+span{min-width:0;gap:2px;margin-left:4px}
|
|
6416
|
-
.dcu-footer-actions [data-testid="billing-trigger-icon"]+span>span:first-child{gap:4px;min-width:0}
|
|
6417
|
-
.dcu-footer-actions [data-testid="billing-trigger-icon"]+span>span:first-child>span{color:var(--dcu-sidebar-navigation);font-size:12px;line-height:20px;letter-spacing:0}
|
|
6418
|
-
.dcu-footer-actions [data-testid="billing-trigger-icon"]+span>span:first-child>span:last-child{color:var(--dcu-sidebar-navigation);font-size:16px;font-weight:600;line-height:20px;letter-spacing:0;font-variant-numeric:tabular-nums}
|
|
6419
|
-
.dcu-footer-actions [data-testid="billing-trigger-today"]{color:var(--dcu-sidebar-navigation);font-size:12px;line-height:16px}
|
|
6420
|
-
.dcu-footer-actions [data-testid="billing-trigger-spark"]>span{background:var(--dcu-sidebar-secondary)}
|
|
6421
|
-
.dcu-footer-actions [data-testid="billing-rail-button"]{box-sizing:border-box;display:grid;place-items:center;width:36px;height:36px;min-width:0;padding:0;border:0;border-radius:8px;background:transparent;color:var(--dcu-sidebar-icon);box-shadow:none;transform:none;transition:background-color 120ms ease-out,color 120ms ease-out}
|
|
6422
|
-
.dcu-footer-actions [data-testid="billing-rail-button"] svg{flex:none;width:24px;height:24px;stroke-width:1.5px}
|
|
6423
6411
|
.dcu-settings-seat [data-slot="settings.trigger"]{color:var(--dcu-sidebar-navigation)}
|
|
6424
6412
|
.dcu-settings-seat [data-slot="settings.trigger"]>svg{color:var(--dcu-sidebar-icon)}
|
|
6425
|
-
@media (prefers-reduced-motion:reduce){.dcu-footer-actions :is([data-testid="billing-trigger"],[data-testid="billing-rail-button"]){transition:none}}
|
|
6426
6413
|
/* 宿主列负责缩放,宽态内容保持展开宽度,避免中文竖排与工作区逐帧重排。 */
|
|
6427
6414
|
.dcu-expanded-shell,.dcu-root:not(.dcu-compact)>.dcu-foot{width:var(--dcu-sidebar-expanded-width,240px);flex-shrink:0}
|
|
6428
6415
|
/* 对齐 Codex 的半秒布局节奏;显式声明避免依赖宿主主题的动画 token。 */
|
package/lib/index.mjs
CHANGED
|
@@ -1555,6 +1555,209 @@ async function moveSessionToWorkspace(services, sessionId, targetWorkspaceId, op
|
|
|
1555
1555
|
movingSessions.delete(sessionId);
|
|
1556
1556
|
}
|
|
1557
1557
|
}
|
|
1558
|
+
//#endregion
|
|
1559
|
+
//#region src/session-title.ts
|
|
1560
|
+
const SESSION_TITLE_TYPES = [
|
|
1561
|
+
"功能",
|
|
1562
|
+
"设计",
|
|
1563
|
+
"修复",
|
|
1564
|
+
"优化",
|
|
1565
|
+
"发布",
|
|
1566
|
+
"探索",
|
|
1567
|
+
"文档",
|
|
1568
|
+
"研究"
|
|
1569
|
+
];
|
|
1570
|
+
const SESSION_TITLE_EMOJI = {
|
|
1571
|
+
功能: "✨",
|
|
1572
|
+
设计: "🎨",
|
|
1573
|
+
修复: "🐛",
|
|
1574
|
+
优化: "⚡",
|
|
1575
|
+
发布: "🚀",
|
|
1576
|
+
探索: "🔍",
|
|
1577
|
+
文档: "📝",
|
|
1578
|
+
研究: "🔬"
|
|
1579
|
+
};
|
|
1580
|
+
const TITLE_TYPES = new Set(SESSION_TITLE_TYPES);
|
|
1581
|
+
const LEADING_EMOJI = /^(?:\p{Extended_Pictographic}(?:\uFE0F|\u200D\p{Extended_Pictographic})*)+\s*/u;
|
|
1582
|
+
const TITLE_SEPARATORS = /[||│]/g;
|
|
1583
|
+
const GENERIC_THEME_SUFFIXES = [
|
|
1584
|
+
"类型",
|
|
1585
|
+
"分类",
|
|
1586
|
+
"相关",
|
|
1587
|
+
"需求",
|
|
1588
|
+
"问题",
|
|
1589
|
+
"会话",
|
|
1590
|
+
"标题",
|
|
1591
|
+
"主题",
|
|
1592
|
+
"内容"
|
|
1593
|
+
];
|
|
1594
|
+
function stripLeadingNoise(part) {
|
|
1595
|
+
const trimmed = part.trim();
|
|
1596
|
+
if (/^\d{4}$/.test(trimmed)) return "";
|
|
1597
|
+
return trimmed.replace(/^\d{4}\s*/, "").replace(LEADING_EMOJI, "").trim();
|
|
1598
|
+
}
|
|
1599
|
+
function parseTypeAndTheme(raw) {
|
|
1600
|
+
const parts = raw.trim().replace(/^["'`]+|["'`]+$/g, "").trim().replace(TITLE_SEPARATORS, "|").split("|").map(stripLeadingNoise).filter((part) => part !== "");
|
|
1601
|
+
const type = parts[0];
|
|
1602
|
+
const theme = parts[1];
|
|
1603
|
+
if (type === void 0 || theme === void 0 || !TITLE_TYPES.has(type)) return void 0;
|
|
1604
|
+
return {
|
|
1605
|
+
type,
|
|
1606
|
+
theme
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
function isRedundantTheme(type, theme) {
|
|
1610
|
+
const normalized = theme.replace(/[\s·.\-_/]/g, "");
|
|
1611
|
+
if (normalized === type || normalized === `新${type}`) return true;
|
|
1612
|
+
if (type !== "" && normalized.length >= type.length * 2 && normalized.length % type.length === 0 && normalized === type.repeat(normalized.length / type.length)) return true;
|
|
1613
|
+
return GENERIC_THEME_SUFFIXES.some((suffix) => normalized === suffix || normalized === `${type}${suffix}`);
|
|
1614
|
+
}
|
|
1615
|
+
function assembleSessionTitle(type, theme) {
|
|
1616
|
+
const parsed = parseTypeAndTheme(`${type}|${theme}`);
|
|
1617
|
+
if (parsed === void 0) return void 0;
|
|
1618
|
+
const title = isRedundantTheme(parsed.type, parsed.theme) ? `${SESSION_TITLE_EMOJI[parsed.type]} ${parsed.type}` : `${SESSION_TITLE_EMOJI[parsed.type]} ${parsed.type}|${parsed.theme}`;
|
|
1619
|
+
if (Buffer.byteLength(title, "utf8") > 80) return void 0;
|
|
1620
|
+
return title;
|
|
1621
|
+
}
|
|
1622
|
+
function shouldSkipAutoTitle(session) {
|
|
1623
|
+
const origin = session.origin ?? session.header?.origin;
|
|
1624
|
+
return session.id.startsWith("dsh-automation-session-") || session.id.startsWith("im:") || origin === "subagent" || session.header?.parentSession !== void 0 && session.header.parentSession !== "";
|
|
1625
|
+
}
|
|
1626
|
+
//#endregion
|
|
1627
|
+
//#region src/session-title-provider.ts
|
|
1628
|
+
const SESSION_TITLE_PROVIDER_ID = "michengai-codex-ui-session-title";
|
|
1629
|
+
const MAX_INPUT_BYTES = 4096;
|
|
1630
|
+
const MAX_OUTPUT_TOKENS = 64;
|
|
1631
|
+
const TIMEOUT_MS = 6e4;
|
|
1632
|
+
const SYSTEM_PROMPT = [
|
|
1633
|
+
"为编程助手会话生成标题。",
|
|
1634
|
+
"只输出一行,恰好两段:类型|主题",
|
|
1635
|
+
"类型必须是其中一个:功能、设计、修复、优化、发布、探索、文档、研究",
|
|
1636
|
+
"主题必须是具体事项,不能重复类型,不要写“功能”“功能类型”这类空主题。不要写日期或表情,不要引号、前缀、解释或 Markdown。",
|
|
1637
|
+
"使用用户消息的语言。"
|
|
1638
|
+
].join("\n");
|
|
1639
|
+
function asTitleService(value) {
|
|
1640
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
1641
|
+
return typeof value.register === "function" ? value : void 0;
|
|
1642
|
+
}
|
|
1643
|
+
function asLlm(value) {
|
|
1644
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
1645
|
+
return typeof value.stream === "function" ? value : void 0;
|
|
1646
|
+
}
|
|
1647
|
+
function abortableSignal(signal) {
|
|
1648
|
+
const timeout = AbortSignal.timeout(TIMEOUT_MS);
|
|
1649
|
+
return signal === void 0 ? timeout : AbortSignal.any([signal, timeout]);
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* Assemble the streamed text the way the host's `BlockAssembler` does: deltas
|
|
1653
|
+
* accumulate per block index and a closing `block-end` replaces them, so the
|
|
1654
|
+
* adapters that send both channels cannot duplicate the title text.
|
|
1655
|
+
*/
|
|
1656
|
+
function collectText(chunks) {
|
|
1657
|
+
return (async () => {
|
|
1658
|
+
const parts = /* @__PURE__ */ new Map();
|
|
1659
|
+
let finish = "stop";
|
|
1660
|
+
const part = (index) => {
|
|
1661
|
+
let existing = parts.get(index);
|
|
1662
|
+
if (existing === void 0) {
|
|
1663
|
+
existing = {
|
|
1664
|
+
text: "",
|
|
1665
|
+
closed: false
|
|
1666
|
+
};
|
|
1667
|
+
parts.set(index, existing);
|
|
1668
|
+
}
|
|
1669
|
+
return existing;
|
|
1670
|
+
};
|
|
1671
|
+
for await (const chunk of chunks) {
|
|
1672
|
+
if (chunk === null || typeof chunk !== "object") continue;
|
|
1673
|
+
const row = chunk;
|
|
1674
|
+
if (row.type === "tool-call-delta" || row.block?.type === "tool-call") throw new Error("codex-ui session title output must be text");
|
|
1675
|
+
if (row.type === "finish") {
|
|
1676
|
+
finish = typeof row.reason?.kind === "string" ? row.reason.kind : "unknown";
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1679
|
+
if (row.type !== "text-delta" && row.type !== "block-end") continue;
|
|
1680
|
+
if (typeof row.index !== "number" || !Number.isSafeInteger(row.index)) continue;
|
|
1681
|
+
if (row.type === "text-delta") {
|
|
1682
|
+
if (typeof row.text !== "string") continue;
|
|
1683
|
+
const target = part(row.index);
|
|
1684
|
+
if (!target.closed) target.text += row.text;
|
|
1685
|
+
continue;
|
|
1686
|
+
}
|
|
1687
|
+
if (row.block?.type !== "text" || typeof row.block.text !== "string") continue;
|
|
1688
|
+
const target = part(row.index);
|
|
1689
|
+
if (target.closed) continue;
|
|
1690
|
+
target.text = row.block.text;
|
|
1691
|
+
target.closed = true;
|
|
1692
|
+
}
|
|
1693
|
+
if (finish !== "stop") throw new Error(`codex-ui session title output finished with "${finish}"`);
|
|
1694
|
+
return [...parts.values()].map((segment) => segment.text).join("").trim().split(/\r?\n/, 1)[0] ?? "";
|
|
1695
|
+
})();
|
|
1696
|
+
}
|
|
1697
|
+
async function generateCodexSessionTitle(llm, request) {
|
|
1698
|
+
request.signal?.throwIfAborted();
|
|
1699
|
+
const session = request.session;
|
|
1700
|
+
if (session === void 0 || shouldSkipAutoTitle(session)) throw new Error("codex-ui session title skipped");
|
|
1701
|
+
const first = request.messages?.[0];
|
|
1702
|
+
const text = first?.text?.trim() ?? "";
|
|
1703
|
+
if (first === void 0 || text === "") throw new Error("codex-ui session title missing message");
|
|
1704
|
+
const route = request.route;
|
|
1705
|
+
if (route === void 0 || route.provider === "" || route.model === "") throw new Error("codex-ui session title missing route");
|
|
1706
|
+
const framed = `根据这条用户消息生成会话标题。只返回“类型|主题”一行,不要第三段,不要重复主题。\n${JSON.stringify([{
|
|
1707
|
+
seq: first.seq,
|
|
1708
|
+
text
|
|
1709
|
+
}])}`;
|
|
1710
|
+
if (Buffer.byteLength(framed, "utf8") > MAX_INPUT_BYTES) throw new Error("codex-ui session title input too large");
|
|
1711
|
+
const signal = abortableSignal(request.signal);
|
|
1712
|
+
const raw = await collectText(llm.stream({
|
|
1713
|
+
provider: route.provider,
|
|
1714
|
+
model: route.model,
|
|
1715
|
+
messages: [{
|
|
1716
|
+
id: crypto.randomUUID(),
|
|
1717
|
+
role: "user",
|
|
1718
|
+
content: [{
|
|
1719
|
+
type: "text",
|
|
1720
|
+
text: framed
|
|
1721
|
+
}],
|
|
1722
|
+
source: {
|
|
1723
|
+
kind: "plugin",
|
|
1724
|
+
plugin: "michengai-codex-ui"
|
|
1725
|
+
}
|
|
1726
|
+
}],
|
|
1727
|
+
system: SYSTEM_PROMPT,
|
|
1728
|
+
maxTokens: MAX_OUTPUT_TOKENS,
|
|
1729
|
+
sessionId: session.id,
|
|
1730
|
+
purpose: "session-title",
|
|
1731
|
+
signal
|
|
1732
|
+
}));
|
|
1733
|
+
signal.throwIfAborted();
|
|
1734
|
+
const parsed = parseTypeAndTheme(raw);
|
|
1735
|
+
if (parsed === void 0) throw new Error("codex-ui session title invalid model output");
|
|
1736
|
+
const title = assembleSessionTitle(parsed.type, parsed.theme);
|
|
1737
|
+
if (title === void 0) throw new Error("codex-ui session title rejected");
|
|
1738
|
+
return {
|
|
1739
|
+
title,
|
|
1740
|
+
messageSeqs: typeof first.seq === "number" ? [first.seq] : [],
|
|
1741
|
+
model: route
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
function registerSessionTitleProvider(ctx) {
|
|
1745
|
+
const sessionTitle = asTitleService(ctx.get("sessionTitle"));
|
|
1746
|
+
const llm = asLlm(ctx.get("llm"));
|
|
1747
|
+
if (sessionTitle === void 0 || llm === void 0) return void 0;
|
|
1748
|
+
try {
|
|
1749
|
+
const dispose = sessionTitle.register({
|
|
1750
|
+
id: SESSION_TITLE_PROVIDER_ID,
|
|
1751
|
+
automatic: "first-prompt",
|
|
1752
|
+
generate: (request) => generateCodexSessionTitle(llm, request)
|
|
1753
|
+
});
|
|
1754
|
+
return typeof dispose === "function" ? dispose : void 0;
|
|
1755
|
+
} catch (error) {
|
|
1756
|
+
const reason = error instanceof Error ? error.message : typeof error;
|
|
1757
|
+
ctx.logger?.warn(`Codex UI 未能注册会话标题提供方:${reason}`);
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1558
1761
|
/** 校验结构和归属;存储兼容模式保留旧 locale 下合法的大小写变体名称。 */
|
|
1559
1762
|
function parseGroups(value, preserveCaseVariants) {
|
|
1560
1763
|
if (!Array.isArray(value) || value.length > 100) return void 0;
|
|
@@ -1773,6 +1976,7 @@ function sessionMoveAuthenticationError(status, error) {
|
|
|
1773
1976
|
/** 提供不泄露地址、命令和凭证的连接器目录。 */
|
|
1774
1977
|
function apply(ctx) {
|
|
1775
1978
|
apply$1(ctx);
|
|
1979
|
+
ctx.effect(() => registerSessionTitleProvider(ctx) ?? (() => {}), "michengai-codex-ui: session title");
|
|
1776
1980
|
const host = hostServices(ctx);
|
|
1777
1981
|
ctx.effect(() => {
|
|
1778
1982
|
const disposers = USAGE_FRAME_ROUTES.map((path) => host.webServer.register({
|