Skip to content

fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes - #2

Merged
7723qqq merged 1 commit into
7723qqq:mainfrom
h2o502:fix/i18n-and-ui-bugs
Jul 21, 2026
Merged

fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes#2
7723qqq merged 1 commit into
7723qqq:mainfrom
h2o502:fix/i18n-and-ui-bugs

Conversation

@h2o502

@h2o502 h2o502 commented Jul 21, 2026

Copy link
Copy Markdown

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.jsonen.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 printableChar filter (paste
still works).

10. Misleading error when resuming a session from another directory

run-v2-print.ts printed a "created under a different directory" hint on
stderr and then threw Session "..." not found — contradictory. It now
throws the sessionDifferentDir message (which carries the remediation
hint) directly.

Verification

  • Scripted check: every t('<key>') in the touched files resolves to a
    string in both runtime locales — 0 missing.
  • en.json/zh.json vs en.ts/zh.ts drift: 0 entries.
  • EN/ZH key parity: identical key sets; placeholder sets equal (except the
    intentional {{plural}} omissions in zh, Chinese having no plural form).
  • No malformed {{...}} placeholders remain in either locale.
  • All touched .ts files parse cleanly (node --experimental-strip-types).
  • Patch verified with git apply --check against current main.

# 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
7723qqq merged commit 3ad8463 into 7723qqq:main Jul 21, 2026
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 决策)。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants