diff --git a/.oxlintrc.json b/.oxlintrc.json index bb16b9a..bb94f66 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -194,6 +194,12 @@ "error", "always" ], + "@stylistic/object-property-newline": [ + "error", + { + "allowAllPropertiesOnSameLine": true + } + ], "@stylistic/arrow-parens": [ "error", "as-needed", diff --git a/src/client/components/statsTiming.tsx b/src/client/components/statsTiming.tsx index 86b7b68..731216e 100644 --- a/src/client/components/statsTiming.tsx +++ b/src/client/components/statsTiming.tsx @@ -145,7 +145,10 @@ export function makeStatsTiming(kit: ViewKit, Donut: (props: DonutProps) => Reac key: slice.key, color: slice.color, value: share(modelRings[index]), })) const toolSlice: Slice = { - key: 'tools', color: COLOR.tools, label: t('timing.tools'), ms: timing.toolsMs, + key: 'tools', + color: COLOR.tools, + label: t('timing.tools'), + ms: timing.toolsMs, times: t('timing.toolTimes', { n: fmt(timing.toolCalls) }), } const otherSlice: Slice = { key: 'other', color: COLOR.other, label: t('timing.other'), ms: other } @@ -155,7 +158,10 @@ export function makeStatsTiming(kit: ViewKit, Donut: (props: DonutProps) => Reac { key: 'other', color: COLOR.other, value: share(other) }, ] const toRow = (slice: Slice): SliceRow => ({ - key: slice.key, color: slice.color, label: slice.label, dim: slice.ms === 0, + key: slice.key, + color: slice.color, + label: slice.label, + dim: slice.ms === 0, pct: fmtShare(slice.ms, wall), count: countOf(slice.ms, slice.times), }) diff --git a/src/client/fileActivity.ts b/src/client/fileActivity.ts index 8d07fe6..2244333 100644 --- a/src/client/fileActivity.ts +++ b/src/client/fileActivity.ts @@ -274,8 +274,12 @@ function aggregateOps(ops: readonly FileOp[], before: number | null): FileActivi entry = { path: op.path, form: formOf(op.tool, op.path), - reads: 0, writes: 0, searches: 0, - added: 0, removed: 0, errs: 0, + reads: 0, + writes: 0, + searches: 0, + added: 0, + removed: 0, + errs: 0, ops: [], ...(op.pattern === true ? { pattern: true as const } : {}), } diff --git a/src/client/i18n.ts b/src/client/i18n.ts index 8fca6ff..2472442 100644 --- a/src/client/i18n.ts +++ b/src/client/i18n.ts @@ -8,8 +8,13 @@ export type Translate = (key: string, params?: Record) export const DICT_ZH: Record = { 'tab': '上下文', 'sidebar.guideDescription': '查看上下文的构成、统计、演变。', - 'cat.system': '系统提示词', 'cat.tools': '工具定义', 'cat.user': '用户消息', - 'cat.inject': '注入内容', 'cat.skill': '技能注入', 'cat.assistant': '助手消息', 'cat.tool': '工具结果', + 'cat.system': '系统提示词', + 'cat.tools': '工具定义', + 'cat.user': '用户消息', + 'cat.inject': '注入内容', + 'cat.skill': '技能注入', + 'cat.assistant': '助手消息', + 'cat.tool': '工具结果', 'overview.title': '当前上下文', 'overview.estimate': 'tokens(估算)', 'overview.free': '剩余窗口', @@ -17,7 +22,8 @@ export const DICT_ZH: Record = { 'overview.ofUsed': '占已用上下文', 'overview.compactReserve': '自动压缩预留:占用达 {pct}% 窗口时触发压缩,此区域一般不实际占用', 'stats.title': '上下文统计', - 'stats.turns': '轮次', 'stats.steps': '步数', + 'stats.turns': '轮次', + 'stats.steps': '步数', 'stats.humanInputs': '用户输入', 'stats.humanInputsTip': '当前会话中的用户消息与问题回答次数。', 'stats.toolCalls': '工具调用', @@ -31,7 +37,10 @@ export const DICT_ZH: Record = { 'stats.subCostTip': '所有子 Agent 会话各自累计费用(同口径估算)的合计,不含当前 Agent 自身。', 'stats.costPriceHead': '每百万 Token 价格:', 'stats.costPriceBy': '按 {p} · {m} 在 models.dev 的刊登价格如上。', - 'stats.costHit': '缓存输入', 'stats.costMiss': '未缓存输入', 'stats.costWrite': '缓存写入', 'stats.costOut': '输出', + 'stats.costHit': '缓存输入', + 'stats.costMiss': '未缓存输入', + 'stats.costWrite': '缓存写入', + 'stats.costOut': '输出', 'stats.costUnavailable': '模型价格暂不可用(自动重试中),费用暂无法估算。', 'timing.title': '耗时统计', 'timing.total': '活跃时长', @@ -42,7 +51,9 @@ export const DICT_ZH: Record = { 'timing.toolArgs': '工具参数', 'timing.tools': '工具执行', 'timing.other': '其他开销', - 'timing.callTimes': '{n}次', 'timing.toolTimes': '{n}次', 'timing.blockTimes': '{n}次', + 'timing.callTimes': '{n}次', + 'timing.toolTimes': '{n}次', + 'timing.blockTimes': '{n}次', 'timing.empty': '暂无耗时数据 · 对话开始后这里会显示时长分布', 'timing.tps': '{tps} tok/s', 'timing.tpsTip': '输出速度(TPS):累计解码输出 tokens ÷ 累计解码时长,与 DeepSeek Harness 会话统计同口径。', @@ -57,19 +68,26 @@ export const DICT_ZH: Record = { 'plugin.settings': '设置', 'plugin.settingsOpen': '打开插件设置', 'trend.title': '上下文趋势', - 'gran.step': '步骤', 'gran.turn': '轮次', - 'settings.title': '上下文', 'settings.desc': 'dsh-context 插件中上下文面板的偏好设置', + 'gran.step': '步骤', + 'gran.turn': '轮次', + 'settings.title': '上下文', + 'settings.desc': 'dsh-context 插件中上下文面板的偏好设置', 'settings.placement': '入口位置', - 'placement.tab': '标签页', 'placement.sidebar': '侧边栏', 'placement.all': '全部', + 'placement.tab': '标签页', + 'placement.sidebar': '侧边栏', + 'placement.all': '全部', 'settings.insightsEntry': '上下文洞察入口', - 'insightsEntry.show': '显示', 'insightsEntry.hide': '隐藏', + 'insightsEntry.show': '显示', + 'insightsEntry.hide': '隐藏', 'settings.gran': '趋势图 · 默认粒度', 'settings.mode': '趋势图 · 默认展示方式', 'settings.toolSort': '工具定义 · 默认排序', 'settings.fileSort': '文件活动 · 默认排序', - 'settings.expand': '展开', 'settings.collapse': '收起', + 'settings.expand': '展开', + 'settings.collapse': '收起', 'settings.readOnly': '当前环境的设置为只读', - 'gran.total': '全量', 'gran.delta': '增量', + 'gran.total': '全量', + 'gran.delta': '增量', 'gran.modeHint': '全量:累计构成;增量:相对上一条请求的变化量', 'trend.focus': '仅展示 {cat}', 'trend.adaptive': '自适应', @@ -97,14 +115,23 @@ export const DICT_ZH: Record = { 'events.at': '第 {t} 轮 · 第 {s} 步', 'events.range': '第 {t} 轮 · 第 {a}→{b} 步', 'events.rangeTo': '第 {a} 轮 · 第 {as} 步 → 第 {b} 轮 · 第 {bs} 步', - 'kind.inject': '注入', 'kind.compaction': '压缩', 'kind.prune': '剪枝', 'kind.model': '切换', 'kind.mode': '模式', + 'kind.inject': '注入', + 'kind.compaction': '压缩', + 'kind.prune': '剪枝', + 'kind.model': '切换', + 'kind.mode': '模式', 'files.title': '文件活动', 'files.scopeLatest': '截至最新 · 跟随趋势图的选择', - 'files.kind.all': '全部', 'files.kind.read': '读取', 'files.kind.write': '写入', - 'files.kind.search': '搜索', 'files.kind.image': '图片', + 'files.kind.all': '全部', + 'files.kind.read': '读取', + 'files.kind.write': '写入', + 'files.kind.search': '搜索', + 'files.kind.image': '图片', 'files.chipTip': '{files} 个文件 · {ops} 次操作', 'files.search': '按路径过滤…', - 'files.sort.count': '按次数', 'files.sort.latest': '按最新', 'files.sort.path': '按路径', + 'files.sort.count': '按次数', + 'files.sort.latest': '按最新', + 'files.sort.path': '按路径', 'files.sortTip': '文件排序:按操作次数(选中分类时按该分类的次数)、最近操作时间或路径', 'files.files': '{n} 个文件', 'files.deltaTip': '按 edit/write 调用参数估算的累计行数变化:+ 新增 / − 删除', @@ -117,23 +144,53 @@ export const DICT_ZH: Record = { 'files.preview': '在侧边栏预览', 'files.readEst': '按调用参数 limit 估算的读取行数', 'files.errs': '{n} 次失败', - 'files.form.image': '图片', 'files.form.dir': '目录', 'files.form.text': '文件', - 'files.glyph.root': '工作区根', 'files.glyph.tests': '测试', 'files.glyph.docs': '文档目录', - 'files.glyph.deps': '依赖', 'files.glyph.build': '构建产物', 'files.glyph.scripts': '脚本', - 'files.glyph.config': '配置', 'files.glyph.assets': '静态资源', 'files.glyph.hidden': '元数据目录', - 'files.glyph.lock': '锁文件', 'files.glyph.docker': 'Docker', 'files.glyph.ignore': '忽略规则', - 'files.glyph.license': '许可证', 'files.glyph.python': 'Python', 'files.glyph.notebook': 'Notebook', - 'files.glyph.shell': 'Shell', 'files.glyph.style': '样式', - 'files.glyph.database': '数据库', 'files.glyph.data': '数据清单', - 'files.glyph.markdown': '标记文档', 'files.glyph.log': '日志', 'files.glyph.sheet': '表格', - 'files.glyph.document': '文档', 'files.glyph.archive': '压缩包', 'files.glyph.font': '字体', + 'files.form.image': '图片', + 'files.form.dir': '目录', + 'files.form.text': '文件', + 'files.glyph.root': '工作区根', + 'files.glyph.tests': '测试', + 'files.glyph.docs': '文档目录', + 'files.glyph.deps': '依赖', + 'files.glyph.build': '构建产物', + 'files.glyph.scripts': '脚本', + 'files.glyph.config': '配置', + 'files.glyph.assets': '静态资源', + 'files.glyph.hidden': '元数据目录', + 'files.glyph.lock': '锁文件', + 'files.glyph.docker': 'Docker', + 'files.glyph.ignore': '忽略规则', + 'files.glyph.license': '许可证', + 'files.glyph.python': 'Python', + 'files.glyph.notebook': 'Notebook', + 'files.glyph.shell': 'Shell', + 'files.glyph.style': '样式', + 'files.glyph.database': '数据库', + 'files.glyph.data': '数据清单', + 'files.glyph.markdown': '标记文档', + 'files.glyph.log': '日志', + 'files.glyph.sheet': '表格', + 'files.glyph.document': '文档', + 'files.glyph.archive': '压缩包', + 'files.glyph.font': '字体', 'files.glyph.media': '媒体', - 'files.glyph.lang.ts': 'TypeScript', 'files.glyph.lang.js': 'JavaScript', 'files.glyph.lang.go': 'Go', - 'files.glyph.lang.rust': 'Rust', 'files.glyph.lang.java': 'Java', 'files.glyph.lang.kotlin': 'Kotlin', - 'files.glyph.lang.ruby': 'Ruby', 'files.glyph.lang.php': 'PHP', 'files.glyph.lang.c': 'C', - 'files.glyph.lang.cpp': 'C++', 'files.glyph.lang.csharp': 'C#', 'files.glyph.lang.scala': 'Scala', - 'files.glyph.lang.lua': 'Lua', 'files.glyph.lang.dart': 'Dart', 'files.glyph.lang.swift': 'Swift', - 'files.glyph.lang.vue': 'Vue', 'files.glyph.lang.svelte': 'Svelte', 'files.glyph.lang.html': 'HTML', + 'files.glyph.lang.ts': 'TypeScript', + 'files.glyph.lang.js': 'JavaScript', + 'files.glyph.lang.go': 'Go', + 'files.glyph.lang.rust': 'Rust', + 'files.glyph.lang.java': 'Java', + 'files.glyph.lang.kotlin': 'Kotlin', + 'files.glyph.lang.ruby': 'Ruby', + 'files.glyph.lang.php': 'PHP', + 'files.glyph.lang.c': 'C', + 'files.glyph.lang.cpp': 'C++', + 'files.glyph.lang.csharp': 'C#', + 'files.glyph.lang.scala': 'Scala', + 'files.glyph.lang.lua': 'Lua', + 'files.glyph.lang.dart': 'Dart', + 'files.glyph.lang.swift': 'Swift', + 'files.glyph.lang.vue': 'Vue', + 'files.glyph.lang.svelte': 'Svelte', + 'files.glyph.lang.html': 'HTML', 'agents.title': 'Agent 网络', 'agents.sub': '当前 Agent 与子 Agent 的实时上下文 · 点击节点跳转会话', 'agents.chip.count': '{n} 个 Agent', @@ -171,9 +228,15 @@ export const DICT_ZH: Record = { 'ev.prune': '剪枝工具输出', 'ev.skill': 'Skill 注入({name})', 'ev.model': '模型切换:{a} → {b}', - 'ev.mode.plan.on': '进入计划模式', 'ev.mode.plan.off': '退出计划模式', - 'form.instructions': '指令注入', 'form.catalog': '目录更新', 'form.snapshot': '状态快照', - 'form.notice': '通知', 'form.relay': '代理转发', 'form.recall': '历史召回', 'form.context': '上下文注入', + 'ev.mode.plan.on': '进入计划模式', + 'ev.mode.plan.off': '退出计划模式', + 'form.instructions': '指令注入', + 'form.catalog': '目录更新', + 'form.snapshot': '状态快照', + 'form.notice': '通知', + 'form.relay': '代理转发', + 'form.recall': '历史召回', + 'form.context': '上下文注入', 'node.toolResult': '工具结果', 'node.calls': '调用 ', 'node.empty': '(空回复)', @@ -203,7 +266,9 @@ export const DICT_ZH: Record = { 'browser.search.skill': '按技能过滤…', 'browser.search.assistant': '按回复或调用过滤…', 'browser.search.tool': '按工具或参数过滤…', - 'browser.kind.think': '思考', 'browser.kind.tool': '工具', 'browser.kind.answer': '回答', + 'browser.kind.think': '思考', + 'browser.kind.tool': '工具', + 'browser.kind.answer': '回答', 'browser.kindTip': '按内容种类过滤回复:思考、工具调用、回答文本', 'browser.rowNoMatch': '没有匹配当前过滤条件的行', 'browser.loading': '正在从更早的会话历史加载完整内容…', @@ -218,7 +283,9 @@ export const DICT_ZH: Record = { 'tool.jsonToggle': '查看原始 JSON', 'tool.jsonHide': '收起', 'tool.search': '按名称、描述或参数过滤…', - 'tool.sort.size': '按大小', 'tool.sort.count': '按次数', 'tool.sort.name': '按名称', + 'tool.sort.size': '按大小', + 'tool.sort.count': '按次数', + 'tool.sort.name': '按名称', 'tool.sortTip': '工具排序:按当前步骤中的调用次数(默认)、token 大小或名称', 'tool.hitsTip': '当前展示步骤的上下文中,该工具被调用并返回结果的次数', 'tool.noMatch': '没有匹配当前过滤条件的工具', @@ -226,16 +293,23 @@ export const DICT_ZH: Record = { 'rich.md': 'Markdown', 'rich.toMd': '按 Markdown 渲染查看', 'rich.toRaw': '查看原始文本', - 'rich.copy': '复制原文', 'rich.copied': '已复制', - 'rich.md.copy': '复制', 'rich.md.copied': '已复制', + 'rich.copy': '复制原文', + 'rich.copied': '已复制', + 'rich.md.copy': '复制', + 'rich.md.copied': '已复制', 'rich.md.footnotes': '脚注', 'block.thinking': '思考', 'block.answer': '回答', - 'block.content': '内容', 'block.result': '结果', 'block.summary': '摘要', + 'block.content': '内容', + 'block.result': '结果', + 'block.summary': '摘要', // Line-count chip on a tool-result's payload card (the singular/plural // split is a no-op in zh, where the measure word never inflects). - 'block.line': '{n} 行', 'block.lines': '{n} 行', - 'call.ok': '正常', 'call.fail': '失败', 'call.exit': 'exit {n}', + 'block.line': '{n} 行', + 'block.lines': '{n} 行', + 'call.ok': '正常', + 'call.fail': '失败', + 'call.exit': 'exit {n}', 'node.failed': '工具执行失败', 'attach.images': '图片附件', 'attach.other': '其他内容', @@ -253,7 +327,10 @@ export const DICT_ZH: Record = { 'ov.title': '上下文洞察', 'ov.unavailable': '会话列表暂不可用', 'ov.range.label': '统计范围', - 'ov.range.24h': '近 24 小时', 'ov.range.7d': '近 7 天', 'ov.range.30d': '近 30 天', 'ov.range.all': '全部', + 'ov.range.24h': '近 24 小时', + 'ov.range.7d': '近 7 天', + 'ov.range.30d': '近 30 天', + 'ov.range.all': '全部', 'ov.kpi.sessions': '活跃会话', 'ov.kpi.ofTotal': '共 {n} 个', 'ov.kpi.tokens': '累计消耗', @@ -274,18 +351,33 @@ export const DICT_ZH: Record = { 'ov.heat.sub': '按日期筛选', 'ov.heat.empty': '暂无活跃数据 — 会话产生模型请求后,这里会显示每日用量', 'ov.heat.sessions': '{n} 个活跃会话', - 'ov.heat.less': '少', 'ov.heat.more': '多', - 'ov.heat.metric': '指标', 'ov.heat.metric.sessions': '会话', 'ov.heat.metric.steps': '步数', + 'ov.heat.less': '少', + 'ov.heat.more': '多', + 'ov.heat.metric': '指标', + 'ov.heat.metric.sessions': '会话', + 'ov.heat.metric.steps': '步数', 'ov.heat.steps': '{n} 步', - 'ov.heat.wd.1': '星期一', 'ov.heat.wd.3': '星期三', 'ov.heat.wd.5': '星期五', - 'ov.heat.mon.01': '1月', 'ov.heat.mon.02': '2月', 'ov.heat.mon.03': '3月', - 'ov.heat.mon.04': '4月', 'ov.heat.mon.05': '5月', 'ov.heat.mon.06': '6月', - 'ov.heat.mon.07': '7月', 'ov.heat.mon.08': '8月', 'ov.heat.mon.09': '9月', - 'ov.heat.mon.10': '10月', 'ov.heat.mon.11': '11月', 'ov.heat.mon.12': '12月', + 'ov.heat.wd.1': '星期一', + 'ov.heat.wd.3': '星期三', + 'ov.heat.wd.5': '星期五', + 'ov.heat.mon.01': '1月', + 'ov.heat.mon.02': '2月', + 'ov.heat.mon.03': '3月', + 'ov.heat.mon.04': '4月', + 'ov.heat.mon.05': '5月', + 'ov.heat.mon.06': '6月', + 'ov.heat.mon.07': '7月', + 'ov.heat.mon.08': '8月', + 'ov.heat.mon.09': '9月', + 'ov.heat.mon.10': '10月', + 'ov.heat.mon.11': '11月', + 'ov.heat.mon.12': '12月', 'ov.list.title': '会话', 'ov.list.search': '搜索标题 / 目录 / 最后消息…', 'ov.list.sortLabel': '排序', - 'ov.list.sort.recent': '最近更新', 'ov.list.sort.tokens': 'Token 总用量', 'ov.list.sort.context': '当前 Context', + 'ov.list.sort.recent': '最近更新', + 'ov.list.sort.tokens': 'Token 总用量', + 'ov.list.sort.context': '当前 Context', 'ov.list.empty': '还没有会话 — 发起一轮对话后,这里会列出每个会话的上下文画像', 'ov.list.noMatch': '没有匹配当前筛选条件的会话', 'ov.list.noData': '暂无上下文数据 — 该会话产生模型请求后才会统计', @@ -310,8 +402,13 @@ export const DICT_ZH: Record = { export const DICT_EN: Record = { 'tab': 'Context', 'sidebar.guideDescription': 'Inspect context composition, stats, and evolution.', - 'cat.system': 'System Prompt', 'cat.tools': 'Tool Schemas', 'cat.user': 'User Messages', - 'cat.inject': 'Injected Context', 'cat.skill': 'Skill Injections', 'cat.assistant': 'Assistant Messages', 'cat.tool': 'Tool Results', + 'cat.system': 'System Prompt', + 'cat.tools': 'Tool Schemas', + 'cat.user': 'User Messages', + 'cat.inject': 'Injected Context', + 'cat.skill': 'Skill Injections', + 'cat.assistant': 'Assistant Messages', + 'cat.tool': 'Tool Results', 'overview.title': 'Current Context', 'overview.estimate': 'tokens (estimated)', 'overview.free': 'Free Window', @@ -319,7 +416,8 @@ export const DICT_EN: Record = { 'overview.ofUsed': 'of used context', 'overview.compactReserve': 'Auto-compaction reserve: auto-compaction triggers at {pct}% of the window — this area is normally kept as headroom, not actually used', 'stats.title': 'Context Stats', - 'stats.turns': 'Turns', 'stats.steps': 'Steps', + 'stats.turns': 'Turns', + 'stats.steps': 'Steps', 'stats.humanInputs': 'Human Inputs', 'stats.humanInputsTip': 'User messages and question answerings in current session.', 'stats.toolCalls': 'Tool Calls', @@ -333,7 +431,10 @@ export const DICT_EN: Record = { 'stats.subCostTip': 'Summed estimates of every subagent session’s own cumulative cost (same basis); the current agent is not included.', 'stats.costPriceHead': 'Per-1M-token rates:', 'stats.costPriceBy': 'Priced as listed on models.dev for {p} · {m}.', - 'stats.costHit': 'Cache input', 'stats.costMiss': 'Uncached input', 'stats.costWrite': 'Cache write', 'stats.costOut': 'Output', + 'stats.costHit': 'Cache input', + 'stats.costMiss': 'Uncached input', + 'stats.costWrite': 'Cache write', + 'stats.costOut': 'Output', 'stats.costUnavailable': 'Model prices are unavailable (retrying automatically) — the cost cannot be estimated yet.', 'timing.title': 'Timing Stats', 'timing.total': 'Active Time', @@ -344,7 +445,9 @@ export const DICT_EN: Record = { 'timing.toolArgs': 'Tool args', 'timing.tools': 'Tool runs', 'timing.other': 'Overhead', - 'timing.callTimes': '{n} calls', 'timing.toolTimes': '{n} runs', 'timing.blockTimes': '{n} blocks', + 'timing.callTimes': '{n} calls', + 'timing.toolTimes': '{n} runs', + 'timing.blockTimes': '{n} blocks', 'timing.empty': 'No timing data yet — durations appear as the conversation runs', 'timing.tps': '{tps} tok/s', 'timing.tpsTip': 'Output speed (TPS): summed decoded output tokens over summed decode time — the same pairing as the harness session stats.', @@ -359,19 +462,26 @@ export const DICT_EN: Record = { 'plugin.settings': 'Settings', 'plugin.settingsOpen': 'Open plugin settings', 'trend.title': 'Context Trend', - 'gran.step': 'Step', 'gran.turn': 'Turn', - 'settings.title': 'Context', 'settings.desc': 'Preferences for Context panel in dsh-context plugin', + 'gran.step': 'Step', + 'gran.turn': 'Turn', + 'settings.title': 'Context', + 'settings.desc': 'Preferences for Context panel in dsh-context plugin', 'settings.placement': 'Entry Options', - 'placement.tab': 'Tab', 'placement.sidebar': 'Sidebar', 'placement.all': 'All', + 'placement.tab': 'Tab', + 'placement.sidebar': 'Sidebar', + 'placement.all': 'All', 'settings.insightsEntry': 'Context Insights entry', - 'insightsEntry.show': 'Show', 'insightsEntry.hide': 'Hide', + 'insightsEntry.show': 'Show', + 'insightsEntry.hide': 'Hide', 'settings.gran': 'Trend Chart · Default granularity', 'settings.mode': 'Trend Chart · Default display', 'settings.toolSort': 'Tool Schemas · Default sort', 'settings.fileSort': 'File Activity · Default sort', - 'settings.expand': 'Expand', 'settings.collapse': 'Collapse', + 'settings.expand': 'Expand', + 'settings.collapse': 'Collapse', 'settings.readOnly': 'Settings are read-only in this environment', - 'gran.total': 'Total', 'gran.delta': 'Delta', + 'gran.total': 'Total', + 'gran.delta': 'Delta', 'gran.modeHint': 'Total: cumulative makeup; Delta: change vs the previous request', 'trend.focus': 'Showing {cat} only', 'trend.adaptive': 'Adaptive', @@ -399,14 +509,23 @@ export const DICT_EN: Record = { 'events.at': 'Turn {t} · Step {s}', 'events.range': 'Turn {t} · Step {a}→{b}', 'events.rangeTo': 'Turn {a} · Step {as} → Turn {b} · Step {bs}', - 'kind.inject': 'Inject', 'kind.compaction': 'Compact', 'kind.prune': 'Prune', 'kind.model': 'Switch', 'kind.mode': 'Mode', + 'kind.inject': 'Inject', + 'kind.compaction': 'Compact', + 'kind.prune': 'Prune', + 'kind.model': 'Switch', + 'kind.mode': 'Mode', 'files.title': 'File Activity', 'files.scopeLatest': 'Up to latest · follows the trend chart pick', - 'files.kind.all': 'All', 'files.kind.read': 'Read', 'files.kind.write': 'Written', - 'files.kind.search': 'Searched', 'files.kind.image': 'Images', + 'files.kind.all': 'All', + 'files.kind.read': 'Read', + 'files.kind.write': 'Written', + 'files.kind.search': 'Searched', + 'files.kind.image': 'Images', 'files.chipTip': '{files} files · {ops} ops', 'files.search': 'Filter by path…', - 'files.sort.count': 'Most active', 'files.sort.latest': 'Latest', 'files.sort.path': 'By path', + 'files.sort.count': 'Most active', + 'files.sort.latest': 'Latest', + 'files.sort.path': 'By path', 'files.sortTip': 'Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path', 'files.files': '{n} files', 'files.deltaTip': 'Cumulative line change estimated from edit/write call arguments: + added / − removed', @@ -419,23 +538,53 @@ export const DICT_EN: Record = { 'files.preview': 'Preview in the sidebar', 'files.readEst': 'Lines read, estimated from the limit argument', 'files.errs': '{n} failed', - 'files.form.image': 'Image', 'files.form.dir': 'Directory', 'files.form.text': 'File', - 'files.glyph.root': 'Workspace root', 'files.glyph.tests': 'Tests', 'files.glyph.docs': 'Docs', - 'files.glyph.deps': 'Dependencies', 'files.glyph.build': 'Build output', 'files.glyph.scripts': 'Scripts', - 'files.glyph.config': 'Config', 'files.glyph.assets': 'Assets', 'files.glyph.hidden': 'Metadata directory', - 'files.glyph.lock': 'Lockfile', 'files.glyph.docker': 'Docker', 'files.glyph.ignore': 'Ignore rules', - 'files.glyph.license': 'License', 'files.glyph.python': 'Python', 'files.glyph.notebook': 'Notebook', - 'files.glyph.shell': 'Shell', 'files.glyph.style': 'Styles', - 'files.glyph.database': 'Database', 'files.glyph.data': 'Data / manifest', - 'files.glyph.markdown': 'Markup document', 'files.glyph.log': 'Log', 'files.glyph.sheet': 'Spreadsheet', - 'files.glyph.document': 'Document', 'files.glyph.archive': 'Archive', 'files.glyph.font': 'Font', + 'files.form.image': 'Image', + 'files.form.dir': 'Directory', + 'files.form.text': 'File', + 'files.glyph.root': 'Workspace root', + 'files.glyph.tests': 'Tests', + 'files.glyph.docs': 'Docs', + 'files.glyph.deps': 'Dependencies', + 'files.glyph.build': 'Build output', + 'files.glyph.scripts': 'Scripts', + 'files.glyph.config': 'Config', + 'files.glyph.assets': 'Assets', + 'files.glyph.hidden': 'Metadata directory', + 'files.glyph.lock': 'Lockfile', + 'files.glyph.docker': 'Docker', + 'files.glyph.ignore': 'Ignore rules', + 'files.glyph.license': 'License', + 'files.glyph.python': 'Python', + 'files.glyph.notebook': 'Notebook', + 'files.glyph.shell': 'Shell', + 'files.glyph.style': 'Styles', + 'files.glyph.database': 'Database', + 'files.glyph.data': 'Data / manifest', + 'files.glyph.markdown': 'Markup document', + 'files.glyph.log': 'Log', + 'files.glyph.sheet': 'Spreadsheet', + 'files.glyph.document': 'Document', + 'files.glyph.archive': 'Archive', + 'files.glyph.font': 'Font', 'files.glyph.media': 'Media', - 'files.glyph.lang.ts': 'TypeScript', 'files.glyph.lang.js': 'JavaScript', 'files.glyph.lang.go': 'Go', - 'files.glyph.lang.rust': 'Rust', 'files.glyph.lang.java': 'Java', 'files.glyph.lang.kotlin': 'Kotlin', - 'files.glyph.lang.ruby': 'Ruby', 'files.glyph.lang.php': 'PHP', 'files.glyph.lang.c': 'C', - 'files.glyph.lang.cpp': 'C++', 'files.glyph.lang.csharp': 'C#', 'files.glyph.lang.scala': 'Scala', - 'files.glyph.lang.lua': 'Lua', 'files.glyph.lang.dart': 'Dart', 'files.glyph.lang.swift': 'Swift', - 'files.glyph.lang.vue': 'Vue', 'files.glyph.lang.svelte': 'Svelte', 'files.glyph.lang.html': 'HTML', + 'files.glyph.lang.ts': 'TypeScript', + 'files.glyph.lang.js': 'JavaScript', + 'files.glyph.lang.go': 'Go', + 'files.glyph.lang.rust': 'Rust', + 'files.glyph.lang.java': 'Java', + 'files.glyph.lang.kotlin': 'Kotlin', + 'files.glyph.lang.ruby': 'Ruby', + 'files.glyph.lang.php': 'PHP', + 'files.glyph.lang.c': 'C', + 'files.glyph.lang.cpp': 'C++', + 'files.glyph.lang.csharp': 'C#', + 'files.glyph.lang.scala': 'Scala', + 'files.glyph.lang.lua': 'Lua', + 'files.glyph.lang.dart': 'Dart', + 'files.glyph.lang.swift': 'Swift', + 'files.glyph.lang.vue': 'Vue', + 'files.glyph.lang.svelte': 'Svelte', + 'files.glyph.lang.html': 'HTML', 'agents.title': 'Agent Network', 'agents.sub': 'Live context of this agent and its subagents · click a node to open its session', 'agents.chip.count': '{n} agents', @@ -473,9 +622,15 @@ export const DICT_EN: Record = { 'ev.prune': 'Tool output pruned', 'ev.skill': 'Skill injected ({name})', 'ev.model': 'Model switched: {a} → {b}', - 'ev.mode.plan.on': 'Plan mode on', 'ev.mode.plan.off': 'Plan mode off', - 'form.instructions': 'Instructions', 'form.catalog': 'Catalog Update', 'form.snapshot': 'State Snapshot', - 'form.notice': 'Notice', 'form.relay': 'Agent Relay', 'form.recall': 'Recall', 'form.context': 'Context Injection', + 'ev.mode.plan.on': 'Plan mode on', + 'ev.mode.plan.off': 'Plan mode off', + 'form.instructions': 'Instructions', + 'form.catalog': 'Catalog Update', + 'form.snapshot': 'State Snapshot', + 'form.notice': 'Notice', + 'form.relay': 'Agent Relay', + 'form.recall': 'Recall', + 'form.context': 'Context Injection', 'node.toolResult': 'Tool Result', 'node.calls': 'Calls ', 'node.empty': '(empty reply)', @@ -505,7 +660,9 @@ export const DICT_EN: Record = { 'browser.search.skill': 'Filter skills…', 'browser.search.assistant': 'Filter by reply or calls…', 'browser.search.tool': 'Filter by tool or arguments…', - 'browser.kind.think': 'Thinking', 'browser.kind.tool': 'Tools', 'browser.kind.answer': 'Answer', + 'browser.kind.think': 'Thinking', + 'browser.kind.tool': 'Tools', + 'browser.kind.answer': 'Answer', 'browser.kindTip': 'Filter replies by content kind: thinking, tool calls, or answer text', 'browser.rowNoMatch': 'No rows match the current filter', 'browser.loading': 'Loading full content from older session history…', @@ -520,7 +677,9 @@ export const DICT_EN: Record = { 'tool.jsonToggle': 'View Raw JSON', 'tool.jsonHide': 'Collapse', 'tool.search': 'Filter by name, description, or parameters…', - 'tool.sort.size': 'By size', 'tool.sort.count': 'By calls', 'tool.sort.name': 'By name', + 'tool.sort.size': 'By size', + 'tool.sort.count': 'By calls', + 'tool.sort.name': 'By name', 'tool.sortTip': 'Sort tools by call count in the shown step (default), token size, or name', 'tool.hitsTip': 'Times this tool was called and answered within the shown step’s context', 'tool.noMatch': 'No tools match the current filter', @@ -528,14 +687,21 @@ export const DICT_EN: Record = { 'rich.md': 'Markdown', 'rich.toMd': 'View as Markdown', 'rich.toRaw': 'View Raw Text', - 'rich.copy': 'Copy Raw Text', 'rich.copied': 'Copied', - 'rich.md.copy': 'Copy', 'rich.md.copied': 'Copied', + 'rich.copy': 'Copy Raw Text', + 'rich.copied': 'Copied', + 'rich.md.copy': 'Copy', + 'rich.md.copied': 'Copied', 'rich.md.footnotes': 'Footnotes', 'block.thinking': 'Reasoning', 'block.answer': 'Response', - 'block.content': 'Content', 'block.result': 'Result', 'block.summary': 'Summary', - 'block.line': '1 line', 'block.lines': '{n} lines', - 'call.ok': 'OK', 'call.fail': 'Failed', 'call.exit': 'exit {n}', + 'block.content': 'Content', + 'block.result': 'Result', + 'block.summary': 'Summary', + 'block.line': '1 line', + 'block.lines': '{n} lines', + 'call.ok': 'OK', + 'call.fail': 'Failed', + 'call.exit': 'exit {n}', 'node.failed': 'Tool execution failed', 'attach.images': 'Images', 'attach.other': 'Other content', @@ -553,7 +719,10 @@ export const DICT_EN: Record = { 'ov.title': 'Context Insights', 'ov.unavailable': 'The session list is unavailable', 'ov.range.label': 'Range', - 'ov.range.24h': 'Last 24 hours', 'ov.range.7d': 'Last 7 days', 'ov.range.30d': 'Last 30 days', 'ov.range.all': 'All', + 'ov.range.24h': 'Last 24 hours', + 'ov.range.7d': 'Last 7 days', + 'ov.range.30d': 'Last 30 days', + 'ov.range.all': 'All', 'ov.kpi.sessions': 'Active Sessions', 'ov.kpi.ofTotal': 'of {n} total', 'ov.kpi.tokens': 'Tokens Used', @@ -574,18 +743,33 @@ export const DICT_EN: Record = { 'ov.heat.sub': 'Filter by date', 'ov.heat.empty': 'No activity yet — daily usage appears once sessions make model requests', 'ov.heat.sessions': '{n} active sessions', - 'ov.heat.less': 'Less', 'ov.heat.more': 'More', - 'ov.heat.metric': 'Metric', 'ov.heat.metric.sessions': 'Sessions', 'ov.heat.metric.steps': 'Steps', + 'ov.heat.less': 'Less', + 'ov.heat.more': 'More', + 'ov.heat.metric': 'Metric', + 'ov.heat.metric.sessions': 'Sessions', + 'ov.heat.metric.steps': 'Steps', 'ov.heat.steps': '{n} steps', - 'ov.heat.wd.1': 'Mon', 'ov.heat.wd.3': 'Wed', 'ov.heat.wd.5': 'Fri', - 'ov.heat.mon.01': 'Jan', 'ov.heat.mon.02': 'Feb', 'ov.heat.mon.03': 'Mar', - 'ov.heat.mon.04': 'Apr', 'ov.heat.mon.05': 'May', 'ov.heat.mon.06': 'Jun', - 'ov.heat.mon.07': 'Jul', 'ov.heat.mon.08': 'Aug', 'ov.heat.mon.09': 'Sep', - 'ov.heat.mon.10': 'Oct', 'ov.heat.mon.11': 'Nov', 'ov.heat.mon.12': 'Dec', + 'ov.heat.wd.1': 'Mon', + 'ov.heat.wd.3': 'Wed', + 'ov.heat.wd.5': 'Fri', + 'ov.heat.mon.01': 'Jan', + 'ov.heat.mon.02': 'Feb', + 'ov.heat.mon.03': 'Mar', + 'ov.heat.mon.04': 'Apr', + 'ov.heat.mon.05': 'May', + 'ov.heat.mon.06': 'Jun', + 'ov.heat.mon.07': 'Jul', + 'ov.heat.mon.08': 'Aug', + 'ov.heat.mon.09': 'Sep', + 'ov.heat.mon.10': 'Oct', + 'ov.heat.mon.11': 'Nov', + 'ov.heat.mon.12': 'Dec', 'ov.list.title': 'Sessions', 'ov.list.search': 'Search title / path / last message…', 'ov.list.sortLabel': 'Sort', - 'ov.list.sort.recent': 'Last updated', 'ov.list.sort.tokens': 'Total Tokens', 'ov.list.sort.context': 'Current Context', + 'ov.list.sort.recent': 'Last updated', + 'ov.list.sort.tokens': 'Total Tokens', + 'ov.list.sort.context': 'Current Context', 'ov.list.empty': 'No sessions yet — each session’s context profile appears here once you start one', 'ov.list.noMatch': 'No sessions match the current filters', 'ov.list.noData': 'No context data yet — stats appear once the session makes model requests', diff --git a/src/client/index.ts b/src/client/index.ts index 537e533..0f92e0c 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -122,7 +122,10 @@ function apply(ctx: ClientCtx): void { const ContextModal = makeContextModal(ctx, kit, settings) ctx.slots.inject('conversation.input.overlay', () => { return ctx.slots.register( - { name: 'conversation.input.overlay', id: 'context-modal', order: 10, locale: NS, + { name: 'conversation.input.overlay', + id: 'context-modal', + order: 10, + locale: NS, inject: (sessionId = '') => ({ hooks: { contextModal: modalStoreOf(sessionId) } }) }, props => h(ContextModal, props), ) diff --git a/src/host/fold.ts b/src/host/fold.ts index 1142528..a18556d 100644 --- a/src/host/fold.ts +++ b/src/host/fold.ts @@ -1028,7 +1028,9 @@ export function applyTimeline(state: TimelineState, event: TimelineEvent, bounds const source = msg?.source if (isInjection(source)) { const rec: ContextEventRecord = { - seq: event.seq, time: event.time, kind: 'inject', + seq: event.seq, + time: event.time, + kind: 'inject', // Re-proved: the harness validates a source's `kind` but not its // `form`, so a hostile form must degrade to the default instead of // failing the record's strict wire/state schemas on every delivery @@ -1153,7 +1155,8 @@ export function applyTimeline(state: TimelineState, event: TimelineEvent, bounds bumpDetailRev(s) const total = s.systemTokens + s.toolsTokens + s.sums.user + s.sums.inject + s.sums.skill + s.sums.assistant + s.sums.tool const record: RequestRecord = { - time: event.time, seq: event.seq, + time: event.time, + seq: event.seq, system: s.systemTokens, tools: s.toolsTokens, user: s.sums.user, @@ -1294,7 +1297,9 @@ export function applyTimeline(state: TimelineState, event: TimelineEvent, bounds s.pendingShadowEventSeq = event.seq } s.events.push({ - seq: event.seq, time: event.time, kind: event.type === 'compaction/summary' ? 'compaction' : 'prune', + seq: event.seq, + time: event.time, + kind: event.type === 'compaction/summary' ? 'compaction' : 'prune', tokens: data && typeof data.shadowedTokenCount === 'number' ? data.shadowedTokenCount : 0, ...(event.type === 'compaction/summary' && data && Array.isArray(data.shadowedSeqs) ? { count: data.shadowedSeqs.length } diff --git a/src/shared/imageTokens.ts b/src/shared/imageTokens.ts index 4667afd..00b4e91 100644 --- a/src/shared/imageTokens.ts +++ b/src/shared/imageTokens.ts @@ -94,7 +94,10 @@ function safeResize(height: number, width: number, paddedHeight: number, paddedW const pad = COMPRESS_PAD_TO - 1 const budget = MAX_TOKENS - pad let result: ResizeSolution = { - nLlmH, nLlmW, bestHeight: paddedHeight, bestWidth: paddedWidth, + nLlmH, + nLlmW, + bestHeight: paddedHeight, + bestWidth: paddedWidth, numTokens: gridTokens(nLlmH, nLlmW), } if (result.numTokens > budget) {