Merge upstream/main (a914a30, 19 commits) — en attente du fix upstream, NE PAS MERGER - #57
Merged
Merged
Conversation
* feat(tui): add selectable themes and custom theme files MCode TUI colors are now driven by named themes, each shipping a dark and a light palette. `/theme` opens a panel with live preview so users can switch themes and lock light/dark on top of terminal detection. Custom theme files under `tui/themes/*.json` support partial overrides, color aliases and syntax palettes, and hot reload so editing a theme in use takes effect without restarting. The selected theme persists in `tui/tui-settings.json` alongside `tuiMode`; unrecognized values fall back to the default theme instead of blocking startup. Assisted-by: mavis reason:port-theme-system * test: register theme system tests and source inventory The public source gate requires every first-party test to be listed in test/vitest-suites.json and every shipped source path to be recorded in release/public-source.json. Register the new theme system tests and the 11 files this change adds. Assisted-by: mavis reason:register-theme-tests
* perf(edit): bound the post-edit diff and unified patch `edit` computed its display diff and its unified patch with unbounded Myers, which costs O((N+M)*D) in the edit-script length D. A whole-file rewrite therefore scaled quadratically in the number of changed lines: rewriting every line of a 20 000-line file blocked the tool for over two minutes and produced a multi-megabyte patch that no renderer displays. The sibling `write` path already caps the same work in `packages/agent-tools/src/shared/write-capture.ts`; `edit` had no cap. Pass jsdiff's `maxEditLength` (1000 edits) and `timeout` (5 s) to `diffLines` and `createTwoFilesPatch`. `maxEditLength` is the primary bound because it is deterministic and therefore unit-testable; `timeout` only backstops slow machines. When a bound trips, `details.diff` carries a one-line notice so every renderer still has something to show, `details.patch` is omitted, and the new `details.diffOmitted` names the reason. The unified patch is skipped once the display diff was abandoned rather than repeating a second Myers run that aborts on the same bound. The file is written before any diff runs, so a dropped diff never changes what lands on disk. Measured end to end through `createEditTool` on a 20 000-line whole-file rewrite, three runs each on the same machine: 120 s / 138 s / 139 s before, 60 ms / 60 ms / 58 ms after. Assisted-by: minimax-code reason:bound-edit-diff-cost * fix(tui): render an omitted edit diff as a summary block When the edit tool hits its diff bounds it replaces the diff body with a one-line notice, so the body carries no `+`/`-` lines. Both preview consumers misread that notice as a real diff: - The TUI counted changed lines from the body and rendered `subject.ts · +0 -0 · Applied` for a 20k-line rewrite. - The ACP projector ran the notice through `parseUnifiedDiff`, emitting a diff block with `oldText === newText === "(diff omitted: ...)"`, which a client renders as "no changes". Read `details.diffOmitted` alongside `details.diff` and emit the existing summary block instead, which renders as a header plus the notice and needs no line counts. `timeout` maps to `unavailable`, bounds hits to `too-large`. E2E: PASS (tmux TUI against dist/cli.js, 20000-line replace_all edit: header is `subject.ts · Applied` + `(diff omitted: more than 1000 lines changed)`; a 1-line edit still renders `+1 -1 · Applied`) * fix(agent-tools): keep Compatible hooks working when the edit patch is bounded out Bounding the post-edit diff drops `details.patch` on exactly the edits the bound targets, and `withCompatibleEditToolResponse` returned the result untouched in that case. The Compatible PostToolUse payload then carried no `tool_response`, so `needsPostToolAdapter` found no usable CLAUDE handler and the runner rejected the hook with HOOK_INVALID_INPUT — every Compatible PostToolUse hook was silently skipped on large edits that used to run fine. There is no fallback for `edit` in `buildCompatiblePostToolResponse`, unlike bash, read and mcp. Synthesize the structured patch instead of reparsing one that does not exist: a bounded-out edit is by definition a wholesale rewrite, so emit a single hunk that removes every old line and adds every new one. This costs no Myers run, keeps the payload the same order as the `originalFile` already in it, and stays a correct — if imprecise — description of what landed on disk. The `\ No newline at end of file` marker is reproduced on whichever side lacks the trailing newline, in jsdiff's position. `EditCapture` now also carries the written content, and BOM stripping is shared between the two sides so the synthesized hunk matches what jsdiff would have produced. Assisted-by: minimax-code reason:bound-edit-diff-cost * fix(edit): report why the unified patch was omitted The unified patch is a second, independently timed Myers run over the same input as the display diff. When only that run ran out of time, `details` held a diff, no patch, and no `diffOmitted`, so a consumer could not tell an omitted patch apart from a tool that never produces one. Add `details.patchOmitted`: it mirrors `diffOmitted` when the display diff was abandoned, and reports `timeout` when only the patch was, so `details.patch` is absent exactly when `patchOmitted` is set. jsdiff routes `createTwoFilesPatch` through the same bounded `diffLines`, so `maxEditLength` is deterministic across both runs and can never trip for the patch alone; only its separately measured wall clock can. `diffOmitted` keeps its meaning and is unchanged. The patch-only timeout is not unit-testable — it needs the two runs to land on opposite sides of a 5 s wall clock, which no deterministic input can force — so the tests pin the invariant on both reachable paths instead. Assisted-by: minimax-code reason:bound-edit-diff-cost * fix(edit): raise the diff bound so ordinary rewrites keep their diff `maxEditLength` bounds D, the length of the Myers edit script, not the number of changed lines: replacing a line costs one deletion plus one insertion. A bound of 1000 therefore gave up on any full rewrite past 500 lines, and a reported 501-line whole-file replacement lost its diff even though the whole tool call, diff included, finishes in under 55 ms. That is well inside what the tool should still render. Raise the bound to 2000, which admits a full rewrite of any file up to 1000 lines. Measured end to end through `createEditTool`, three runs each on the same machine: 53 / 49 / 47 ms for a 501-line rewrite, 177 / 173 / 177 ms for 1000 lines, and 200 / 192 / 193 ms for a 20 000-line rewrite that still gives up — against 167 620 / 173 523 / 167 547 ms unbounded, with peak heap dropping from 47-60 MB to 14-15 MB. Doubling the bound roughly doubles the worst case the bound itself admits, which stays near 200 ms. Also correct the notice the bound emits. It read "more than N lines changed", which misreports a rewrite by a factor of two; D is exactly the number of added plus removed lines, so say that instead. Assisted-by: minimax-code reason:bound-edit-diff-cost * fix(agent-tools): send an empty structured patch when the edit patch is bounded out Synthesizing a whole-file hunk kept Compatible PostToolUse handlers selectable when the bound dropped `details.patch`, but pushed the hook payload past the runner's 1 MiB input limit on exactly the large files the bound targets. A 20 000-line file with 1001 changed lines serialized to 1 679 234 B and failed `serializeHookInput`, so the handler still never ran — it now failed one step later than before. Trimming the common prefix and suffix does not fix that either: the same shape still reaches 1 154 543 B on a 960 KB file. `structuredPatch` is a required array in the Compatible file-edit output schema, and the upstream tool already sends `[]` when its own diff times out, so the empty array is that schema's own way to say "no diff information". It keeps the handler selectable, keeps `edit` renamed to `Edit` for matchers, and passes the write-back `hasCompatibleJsonShape` check, which compares array-ness only and so still accepts a hook that returns real hunks. The same payload now serializes to 563 600 B. `EditCapture.updatedFile` existed only to feed the synthesized hunk and goes away with it, restoring the capture to its previous shape. A malformed `details.patch` now also degrades to `[]` instead of dropping the whole compatible response, which is strictly better: the handler runs rather than being skipped with HOOK_INVALID_INPUT. This does not make every hook payload fit. The response separately carries `originalFile`, `oldString` and `newString` in full, so a whole-file rewrite of a large file still exceeds the limit on those fields alone, both before and after this change. That bound predates this branch and is left alone. Also correct the stale timings in the test header, which predated the remeasurement at the current bound. Assisted-by: minimax-code reason:bound-edit-diff-cost
An Anthropic-compatible relay that routes prompt-cache hits per session had no documented way to learn the session identity. The request builder forwards metadata.user_id only when a caller sets metadata explicitly, because it is an abuse-detection and attribution field rather than a cache key, so nothing in the public documentation explained how to preserve session identity instead. Document the supported sendSessionAffinityHeaders compat override for custom providers, and pin the resulting wire-level request: no session identity without the override, x-session-affinity once a provider entry opts in, and no session header when the turn runs without prompt caching. Closes MiniMax-AI#318
…-AI#324) * docs(tui): design follow-tail preservation * docs(tui): add follow-tail implementation plan * fix(tui): preserve detached transcript position * fix(tui): re-arm follow-tail on explicit navigation * test(tui): cover follow-tail intent boundaries * fix(tui): re-arm follow-tail for queued messages * fix(tui): ignore stale queue admissions
* fix(tui): restore early-aborted prompt to composer * fix(tui): preserve restored submission edits and metadata
* fix(tui): clear previous run duration on new turn * fix(tui): dismiss stale duration when a message appears
Render the projected submission synchronously so the prior interrupted footer is erased before input returns. Queue the normal input render before dispatch so a synchronous render coalesces instead of doing a second pass.
) * feat(tui): select plugins with durable identity mentions * test(tui): update plugin mention discovery hints * fix(tui): preserve plugin bindings across draft and message edits * fix(tui): retain unique plugin labels across multiple external edits * fix: reconcile plugin mention display and simplify completion rows * fix(tui): reconcile theme edits missed during watcher startup
# Conflicts: # docs/tui-capabilities.md # package.json # packages/tui/package.json # packages/tui/src/observability/incident-reporter.ts # packages/tui/src/tui/app.ts # packages/tui/src/tui/controller/run/active-run-flow.ts # packages/tui/src/tui/launcher.ts # packages/tui/src/tui/theme/controller.ts # packages/tui/src/tui/theme/palettes.ts # packages/tui/src/tui/theme/runtime.ts # packages/tui/test/unit/incident-reporter-privacy.test.ts # packages/tui/test/unit/tui-chat-controller.test.ts # packages/tui/test/unit/tui-engine-local-deltas.test.ts # release/public-source.json # test/source-sync.test.mjs # test/vitest-suites.json
…ollow-up) - auth/application.ts: drop the no-useless-catch try/catch wrapper in login() - transcript/usage-visualization.ts, update/application.ts: prefer-template - update-release.test.ts: prefer-template in fixture URLs - package-identity.test.ts: import the scripts/lib/package-identity.mjs via import.meta.resolve so the cross-package reference satisfies import/no-relative-packages while keeping the vitest resolution path
…identity - tui-app.test.ts: /usage placeholder assertion 'Ask Mcode…' -> 'Ask Kcode…' - theme tests (palettes/custom-themes/runtime): MINIMAX_CODE_DARK/LIGHT_THEME and MINIMAX_CODE_THEME_CONTRAST_POLICY renamed to the KCODE_* exports the fork's theme/palettes.ts ships; theme-contrast helper gains the KCODE_THEME_CONTRAST_POLICY alias with the same palette roles
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.
Merge upstream/main a914a30 → fork (19 commits)
Commits upstream intégrés (19, ae65651..a914a30)
Doublons
Aucun : 0 commit du lot déjà couvert par une PR fork (vérifié par ancestor-check 19/19 absents de main).
Criblage telemetry
createConfiguredTuiBusinessTelemetry(usine telemetry amont, HEAD du conflit launcher.ts) rejetée ; businessEventTracker/trackChatSend non intégrés (app.ts) ;docs/telemetry.mdreste absent ;incident-reporter.ts+ son test : UD → delete (fork).Conflits (16 fichiers) et résolutions
package.json/packages/tui/package.json: identité fork (kinetick-code/@mavis/code) conservée.docs/tui-capabilities.md: écosystème tabs fork conservé (docs feat(tui): select plugins with durable identity mentions MiniMax-AI/minimax-code#334 plugin non repris côté doc).launcher.ts: imports union (tui-settings theme feat(tui): add selectable themes and custom theme files MiniMax-AI/minimax-code#311) + Kcode renames, usine telemetry supprimée.app.ts: switchComposerDraft fork (clearRetainedSubmissions fix(tui): reset rewound todos and keep commands local during edits MiniMax-AI/minimax-code#336) conservé,detachForegroundObserverupstream retiré (absent du type fork) ; onMessageAdmitted sans tracker telemetry.active-run-flow.ts/plugin-autocomplete.ts: renames Mcode→Kcode.incident-reporter.ts+incident-reporter-privacy.test.ts: UD → delete (fork).release/public-source.json+test/vitest-suites.json: régénérés (node scripts/source-inventory.mjs --write, 4261 files) + union entrées fork.test/source-sync.test.mjs: côté upstream (tests Windows profile chore(tui): restore ESLint rules and verification gate MiniMax-AI/minimax-code#341, cohérent avec verify.mjs mergé).Gates
Frontières fork préservées