fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes - #2
Merged
Merged
Conversation
# fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes
## Summary
Static analysis of the fork found a cluster of user-facing bugs: several TUI
screens render raw i18n key paths instead of text, the Astron settings panel
is completely untranslated (and gets wiped by unrelated config saves), the
`/workflow` help prints `undefined`, and an 80 ms footer timer runs forever
once thinking effort is enabled.
All fixes are minimal, behavior-preserving except where the current behavior
is objectively wrong. Every `t()` key used in the touched files was
machine-verified to resolve in **both** `en` and `zh` runtime locales
(`locales/*.ts`), with placeholder parity.
## Bugs fixed
### 1. i18n key paths pointing at non-existent locations (raw keys shown in UI)
| File | Wrong prefix | Correct prefix | Count |
|---|---|---|---|
| `tui/commands/config.ts` | `tui.dialogs.config.configXxx` | `tui.messages.configXxx` | 41 |
| `migration/migration-screen.ts`, `migration/badge.ts` | `migration.X` | `tui.migration.X` | 34 |
| `tui/components/dialogs/coding-plan-config.ts` | `codingPlan.X` | `tui.codingPlan.X` | 12 |
The Rust translation engine falls back to returning the key itself, so the
config/migration/coding-plan screens currently display strings like
`tui.dialogs.config.configThemeSet` to users.
### 2. Astron settings panel untranslated + locale drift
`astron-settings.ts` references 14 keys under `tui.dialogs.astronSettings.*`
that exist in `locales/*.json` but were never synced into the runtime
`locales/*.ts` sources — the whole panel renders raw key paths. Also missing
in `*.ts`: `settingsSelector.astron/astronDesc` and
`messages.configAstronSaveFailed` (17 keys total, EN+ZH). Reverse drift:
`tui.chrome.footer.swarmPlan` existed only in `*.ts`; added to `*.json`.
`en.json/zh.json` ↔ `en.ts/zh.ts` are now fully in sync.
### 3. Malformed placeholder in zh locale
`zh unsupportedEffort` contained `{{arg}` (missing closing `}}`) — the value
would render literally. Fixed in both `zh.ts` and `zh.json`.
### 4. `/workflow` usage prints `undefined`
`workflow.ts` did `t('tui.slashCommands.workflowHelp') as Record<string,
string>` — `t()` returns a string, so every `wf.*` access was `undefined`.
Replaced with individual leaf-key calls (`workflowHelp.usage`, `.list`, ...).
### 5. `/discuss` prompt-structure injection
User-supplied roles/stances were interpolated into the quoted
`roleDescription`/`assignedStance` strings unescaped, so a `"` in a role name
corrupts the generated prompt. Now escapes `\` and `"` (and removes the dead
`safeName` variable that was computed but never used).
### 6. Changing theme/locale/editor wipes Astron settings
`currentTuiConfig()` hardcoded `astron: DEFAULT_TUI_CONFIG.astron`, so every
`saveTuiConfig` from the theme/locale/editor/update-preference flows reset
the user's Astron parameters to defaults. It is now async and seeds `astron`
from the persisted `tui.toml` (matching how `AstronSettingsComponent` itself
does load-modify-save).
### 7. `createI18n` ignores `initialLocale` when `noDetect` is set
`options.noDetect ? 'en' : (options.initialLocale ?? detectLocaleNode())`
made an explicit `initialLocale` lose to `noDetect`. Priority is now
`initialLocale ?? (noDetect ? 'en' : detectLocaleNode())`. Also exports the
previously instance-only `getMessages`/`translateBatch` at module level, and
fixes a JSDoc example referencing a non-existent key.
### 8. Footer pulse timer runs 12.5×/s forever
`syncPulseTimer(state.thinkingEffort !== 'off')` kept an 80 ms interval alive
permanently once thinking effort was enabled — constant re-renders while
idle. Now tied to actual activity: `thinkingEffort !== 'off' &&
streamingPhase !== 'idle'`.
### 9. Coding-plan editor accepts control keys as text
The input handler appended any non-Enter/Escape data to the field value, so
arrow keys / Tab / Ctrl combos leaked escape bytes (e.g. `\x1b[C`) into
config values. Now uses the codebase-standard `printableCha
7723qqq
added a commit
that referenced
this pull request
Aug 10, 2026
- http.rs: widen the shared turn-context grace period from 100ms to 2s — a lagging WS projector could miss turn.started after the async-submit cleanup, silently dropping the whole turn projection and leaving its messages stuck in pending (v1 #2/#5) - kimi-web agent projector: only emit the client-accumulated usage snapshot at turn end when the turn actually reported step usage, so the authoritative event.session.usage_updated numbers are no longer clobbered with zeros (v1 #3) - regression tests for the usage gating
7723qqq
added a commit
that referenced
this pull request
Aug 10, 2026
…#5) - eventReducer messageUpdated now stores the protocol status on the message instead of dropping it, so streamed assistant messages leave their initial/pending state when the Rust server projects the completed close-out - AppMessage gains an optional status field; REST snapshot messages map as completed history (toAppMessage status param), WS-created messages stay unset until message.updated arrives - regression test covers the status hand-off; G-2 #2/#3 verified already fixed (take_turn 2s grace, usage_updated consumption)
7723qqq
added a commit
that referenced
this pull request
Aug 12, 2026
Closes the resume/replay data gap recorded in CODEX §1.4 #2: - session/list gains include_subagents (default false) and now filters subagent records (swarm-*/task-* written via Agent::durable_state) from the default user-facing list — a behavior fix - session/get_context gains include_subagents; when set, the response carries a subagents summary list (agent_id, title, message_count, updated_at) — agent_id is the session id since subagents persist under agent_id keys in the same sessions table - wire.gen.ts regenerated via gen-wire-contract (141 types, idempotent); SessionSummaryRpc/SessionListResult/SessionContextResult untouched, so kimi-server is unaffected (cargo check -p kimi-server clean) - integration test: default list hides subagents, opt-in lists them, get_context returns the summary only when requested - vscode host wiring (replay-adapter consumption shape) left for a separate confirmation; documented in CODEX
7723qqq
added a commit
that referenced
this pull request
Aug 15, 2026
#2: Batch appendLoopEvent in executeStepTools — collect all tool.call and tool.result events during for-await, then dispatch once at end. Eliminates N synchronous context array copies per step. #3: Wire tryNativeReadBatch into native-tools.ts — existing Rust nativeBatchRead now callable from the tool layer. Callers fall back to sequential nativeRead when native module unavailable. #1: Async-ify native_edit with spawn_blocking — no longer blocks event loop during parallel tool execution.
7723qqq
added a commit
that referenced
this pull request
Aug 15, 2026
fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes
7723qqq
added a commit
that referenced
this pull request
Aug 15, 2026
- http.rs: widen the shared turn-context grace period from 100ms to 2s — a lagging WS projector could miss turn.started after the async-submit cleanup, silently dropping the whole turn projection and leaving its messages stuck in pending (v1 #2/#5) - kimi-web agent projector: only emit the client-accumulated usage snapshot at turn end when the turn actually reported step usage, so the authoritative event.session.usage_updated numbers are no longer clobbered with zeros (v1 #3) - regression tests for the usage gating
7723qqq
added a commit
that referenced
this pull request
Aug 15, 2026
…#5) - eventReducer messageUpdated now stores the protocol status on the message instead of dropping it, so streamed assistant messages leave their initial/pending state when the Rust server projects the completed close-out - AppMessage gains an optional status field; REST snapshot messages map as completed history (toAppMessage status param), WS-created messages stay unset until message.updated arrives - regression test covers the status hand-off; G-2 #2/#3 verified already fixed (take_turn 2s grace, usage_updated consumption)
7723qqq
added a commit
that referenced
this pull request
Aug 15, 2026
Closes the resume/replay data gap recorded in CODEX §1.4 #2: - session/list gains include_subagents (default false) and now filters subagent records (swarm-*/task-* written via Agent::durable_state) from the default user-facing list — a behavior fix - session/get_context gains include_subagents; when set, the response carries a subagents summary list (agent_id, title, message_count, updated_at) — agent_id is the session id since subagents persist under agent_id keys in the same sessions table - wire.gen.ts regenerated via gen-wire-contract (141 types, idempotent); SessionSummaryRpc/SessionListResult/SessionContextResult untouched, so kimi-server is unaffected (cargo check -p kimi-server clean) - integration test: default list hides subagents, opt-in lists them, get_context returns the summary only when requested - vscode host wiring (replay-adapter consumption shape) left for a separate confirmation; documented in CODEX
7723qqq
added a commit
that referenced
this pull request
Aug 31, 2026
- session_try_acquire_quiescence / session_release_quiescence: RAII guard 存注册表,release 即 drop 重放 held turns 并唤醒 pump - EngineSessionHandle 补 tryAcquireQuiescence/releaseQuiescence - 集成测试:acquire → held turn 停靠(isSettled=false)→ release 重放 → ran;门控的活跃 turn 期间第二窗口被拒 - 3c 的 undo/compaction 消费方现可经句柄使用静默期
7723qqq
added a commit
that referenced
this pull request
Sep 1, 2026
v2 goalAgentRuntime 的 CreateGoal 启动审批与陈旧 goal 拒绝此前对原生 执行完全失效(goal 静默启动、旧轮可改已变更的 goal)。本批在引擎内补齐: - tools/goal_guard.rs(新增):GoalGuard——turn 起始 goal 绑定表 + requires_host(非 auto 路由)+ stale_denial(突变工具双拼写、 goalId 比较、goal 清空即 stale、读失败 fail-open、文案逐字节对齐 v2)。 - #7 审批 = 路由回宿主:非 auto 模式(含 mode 未知 fail-closed)下 CreateGoal 不经原生执行,走宿主 executeTool——goal-start 审批链 (含 mode 切换面板)原样生效,零重实现。mode 取 pipeline 快照 (PermissionEngine::mode())。 - #8 stale veto:run_turn 入口经新 HostCallbacks::set_turn_goal 绑定 turn→goal(默认 no-op,NativeToolCallbacks/SteerQueueCallbacks 转发, 零装配点改动);gate 在 permission 后插入 stale_denial,denial 发 tool.native is_error + 合成结果,不回退宿主。 - 预算宽限轮由 run_turn 硬停结构性覆盖,无需复刻(文档说明差异)。 - 顺带修复 napi callbacks.goal() 死缝:NapiHostCallbacks 增 goal_fn 并实现(session 接线,legacy 留 None fail-open)。 - 验证:cargo lib 866(goal_guard 6 + 门级 4 + 绑定 2)、stdio 16/16 (CreateGoal 无快照必回退 E2E)、napi-integration 49/49(manual 回退/ auto 原生/session stale E2E)、clippy 0、oxlint 0 errors。 - ROADMAP P38 #7/#8 销账 + P42 文档(含诚实边界:mode 会话级陈旧、 REPL 无审批、预算硬停差异);changeset 记录用户可见行为。 迁移队列:#4 P39 ✅、#3 P41 ✅、#7+#8 P42 ✅ → 剩余 #6/#2/MoonshotAI#13。
7723qqq
added a commit
that referenced
this pull request
Sep 1, 2026
v2 agentExternalHooksService 的用户 PreToolUse 钩子此前对引擎原生路径 零对应(原生工具执行不触发)。本批在引擎内全量执行: - tools/external_hooks.rs(新增):HookGuard——event 过滤(只 PreToolUse)/matcher 正则(非法跳过)/command 去重/并行(tokio join_all,按序取首个 block);平台 shell spawn;stdin 写 snake_case 载荷(hook_event_name/session_id/cwd/client_type/session_title/ tool_name/tool_input/tool_call_id);超时 select + kill;三分支判定 (exit 2 → stderr;stdout JSON deny → reason;其余 allow)与 fail-closed 文案(failed to spawn / timed out / errored)逐字节对齐 v2。 - 配置随 PolicySnapshot 推送(零新 wire 字段):PolicySnapshot 增 pre_tool_hooks;宿主 rust-engine.ts getPolicySnapshot 从 loadRuntimeConfigSafe 读 [hooks];REPL 经 KimiConfig.hooks 段 + build_policy_snapshot。 - gate 集成:permission allow 后、goal_guard 前(镜像 v2 链序); denial 发 tool.native is_error + 合成结果,不回退宿主。 - 验证:cargo lib 879(external_hooks 11 + 门级 2)、stdio 18/18 (exit2 拦/exit0 放行 E2E)、napi-integration 51/51(真实 .node)、 rust-engine 25/25(宿主推送零回归)、clippy 0、oxlint 0 errors。 - ROADMAP P38 #6 销账 + P43 文档(含诚实边界:载荷字段近似、kill 链 降级、快照会话级推送、其他 19 种事件仍归宿主、cmd 引号教训); changeset 记录用户可见行为。 迁移队列:#4 P39 ✅、#3 P41 ✅、#7+#8 P42 ✅、#6 P43 ✅ → 剩余 #2(toolDedupe)、MoonshotAI#13(tower worker,随 M3)。
7723qqq
added a commit
that referenced
this pull request
Sep 1, 2026
P33 风险条目「宿主独有能力无处安放…归入 M3」落地,补齐 M3 里程碑 缺失的「重新定义宿主分层」一半,并消解其与 M5「v2 从仓库消失」的矛盾。 决策(ROADMAP P44): - 决策 1 终态修正:v2 不消失,重新定义为「宿主层库」——删除引擎面 (≈11.9k 行,loop/engineOverride/rustSelfContained)+ 死域,保留库面 (≈28k 行:config/workspace/oauth/plugin/mcp/capability/auth/session index/telemetry/approval/question 等)。M5 删除范围与退出标准同步修订。 - 决策 2 逐域判定表:lsp/sessionQuery/codeRuntime/attachment/knowledge/ team/workflow/memory 随 v2 删除(死代码);tower/swarm/transcript 投影/ media/context memory/session index/permission/skill 宿主层库保留; subagent/task runner/#2 引擎吸收(队列)。 - 决策 3 宿主层库范围:4 个 M3 标记消费者的宿主边界。 M3a 实施: - 删 8 个 src 域 + agent/tools/team(teamTool 自注册无人导入)+ 15 个死 测试文件(约 -12.6k 行);errors.ts 剥离 LspErrors/SessionQueryErrors (import/re-export/ErrorCodes 聚合三处);全仓零残留引用(仅 Rust 移植 注释的历史引用)。 - tool-name-contract.json unloadedInV2 清空 + note 更新(重接线工具改由 TS unclassified 检查拦截)。 - 验证:agent-core-v2 typecheck 0 errors、toolNameContract 绿、 check:v2-library-surface OK;全量 13 个失败经 stash 基线证实为 pristine 同款(Windows 时序类 + manifest 漂移),与删除零相关; kimi-agent cargo 879 + vitest 116 全绿。
7723qqq
added a commit
that referenced
this pull request
Sep 2, 2026
引擎原生路径从重复调用零防护变为引擎内全量去重:tool_dedupe.rs DedupeGuard 镜像 v2 toolDedupeService(同键 = 工具名 + 排序键紧凑 JSON;同步步内重复共享原始最终结果、绝不二次执行;跨步 streak 3/5/8 追加递增 system-reminder 提醒、12 强停,文案/阈值逐字节对齐)。run_turn 经 OnceCell get_or_init 共享结果,去重范围限定原生可执行名(is_native_tool_name,宿主回退调用仍归宿主 dedupe 避免双重覆盖),重复调用补发 tool.native 保转录两卡片。验证:cargo 894 lib + 18 集成 + clippy 0;vitest 117(+1 stdio 产品路径 E2E);agent-core-v2 引擎契约 67/67。G-6 迁移队列剩 MoonshotAI#13(随 M3 tower 决策)。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes
Summary
Static analysis of the fork found a cluster of user-facing bugs: several TUI
screens render raw i18n key paths instead of text, the Astron settings panel
is completely untranslated (and gets wiped by unrelated config saves), the
/workflowhelp printsundefined, and an 80 ms footer timer runs foreveronce thinking effort is enabled.
All fixes are minimal, behavior-preserving except where the current behavior
is objectively wrong. Every
t()key used in the touched files wasmachine-verified to resolve in both
enandzhruntime locales(
locales/*.ts), with placeholder parity.Bugs fixed
1. i18n key paths pointing at non-existent locations (raw keys shown in UI)
tui/commands/config.tstui.dialogs.config.configXxxtui.messages.configXxxmigration/migration-screen.ts,migration/badge.tsmigration.Xtui.migration.Xtui/components/dialogs/coding-plan-config.tscodingPlan.Xtui.codingPlan.XThe Rust translation engine falls back to returning the key itself, so the
config/migration/coding-plan screens currently display strings like
tui.dialogs.config.configThemeSetto users.2. Astron settings panel untranslated + locale drift
astron-settings.tsreferences 14 keys undertui.dialogs.astronSettings.*that exist in
locales/*.jsonbut were never synced into the runtimelocales/*.tssources — the whole panel renders raw key paths. Also missingin
*.ts:settingsSelector.astron/astronDescandmessages.configAstronSaveFailed(17 keys total, EN+ZH). Reverse drift:tui.chrome.footer.swarmPlanexisted only in*.ts; added to*.json.en.json/zh.json↔en.ts/zh.tsare now fully in sync.3. Malformed placeholder in zh locale
zh unsupportedEffortcontained{{arg}(missing closing}}) — the valuewould render literally. Fixed in both
zh.tsandzh.json.4.
/workflowusage printsundefinedworkflow.tsdidt('tui.slashCommands.workflowHelp') as Record<string, string>—t()returns a string, so everywf.*access wasundefined.Replaced with individual leaf-key calls (
workflowHelp.usage,.list, ...).5.
/discussprompt-structure injectionUser-supplied roles/stances were interpolated into the quoted
roleDescription/assignedStancestrings unescaped, so a"in a role namecorrupts the generated prompt. Now escapes
\and"(and removes the deadsafeNamevariable that was computed but never used).6. Changing theme/locale/editor wipes Astron settings
currentTuiConfig()hardcodedastron: DEFAULT_TUI_CONFIG.astron, so everysaveTuiConfigfrom the theme/locale/editor/update-preference flows resetthe user's Astron parameters to defaults. It is now async and seeds
astronfrom the persisted
tui.toml(matching howAstronSettingsComponentitselfdoes load-modify-save).
7.
createI18nignoresinitialLocalewhennoDetectis setoptions.noDetect ? 'en' : (options.initialLocale ?? detectLocaleNode())made an explicit
initialLocalelose tonoDetect. Priority is nowinitialLocale ?? (noDetect ? 'en' : detectLocaleNode()). Also exports thepreviously instance-only
getMessages/translateBatchat module level, andfixes a JSDoc example referencing a non-existent key.
8. Footer pulse timer runs 12.5×/s forever
syncPulseTimer(state.thinkingEffort !== 'off')kept an 80 ms interval alivepermanently once thinking effort was enabled — constant re-renders while
idle. Now tied to actual activity:
thinkingEffort !== 'off' && streamingPhase !== 'idle'.9. Coding-plan editor accepts control keys as text
The input handler appended any non-Enter/Escape data to the field value, so
arrow keys / Tab / Ctrl combos leaked escape bytes (e.g.
\x1b[C) intoconfig values. Now uses the codebase-standard
printableCharfilter (pastestill works).
10. Misleading error when resuming a session from another directory
run-v2-print.tsprinted a "created under a different directory" hint onstderr and then threw
Session "..." not found— contradictory. It nowthrows the
sessionDifferentDirmessage (which carries the remediationhint) directly.
Verification
t('<key>')in the touched files resolves to astring in both runtime locales — 0 missing.
en.json/zh.jsonvsen.ts/zh.tsdrift: 0 entries.intentional
{{plural}}omissions in zh, Chinese having no plural form).{{...}}placeholders remain in either locale..tsfiles parse cleanly (node --experimental-strip-types).git apply --checkagainst currentmain.