Commit graph

347 commits

Author SHA1 Message Date
qer
ab29cf9e31 feat(web): remove SVG icon from plan pill to match permission style
- Both permission and plan pills are now clean text-only pills
- Ensures consistent visual appearance across toolbar controls
2026-06-11 10:14:47 +08:00
qer
183cc2c302 feat(web): remove SVG icons from permission pill and dropdown
- Keep permission pill text-only for a cleaner look
- Remove unused .pd-icon CSS rule
2026-06-11 10:14:47 +08:00
qer
4f490a1575 feat(web): unify toolbar pills, add SVG icons, circular ctx ring, improve model display
- Replace emoji with inline SVG icons for attach/permission/plan buttons
- Unify attach-btn, perm-pill, toggle-pill styles (same padding/radius/font)
- Add SVG icons in permission dropdown rows
- Convert context bar from horizontal bar to circular SVG ring
- Strip provider prefix from model display name; fall back to models list lookup
- Load models eagerly on app init for friendly name resolution
- Increase toolbar and dropdown font sizes
- Increase textarea default height from 40px to 56px
- Remove plan/permission/ctx from model dropdown (keep only models + thinking)
- Fix ctx number spacing (remove spaces around slash)
2026-06-11 10:14:47 +08:00
haozhe.yang
d23224ae59 chore(deps): bump vitest to 4.1.4 in kimi-web
- update vitest from ^2.1.8 to 4.1.4 in apps/kimi-web/package.json\n- regenerate pnpm-lock.yaml for vitest 4.1.4\n- update flake.nix vendor hash
2026-06-11 10:14:47 +08:00
qer
32d4476e99 feat(web): increase toolbar font sizes for better readability 2026-06-11 10:14:47 +08:00
qer
4cb72f827e fix(web): remove default dot glyph for unknown/skills tools 2026-06-11 10:14:47 +08:00
qer
e52269e1d7 fix(web): move props.text watcher below defineProps in Markdown.vue
2f229b3 added the image-resolving watcher above the defineProps call.
watch() invokes its getter synchronously to collect dependencies, so
the closure read props while the const was still in its temporal dead
zone — every Markdown mount threw 'Cannot access props before
initialization', crashing the whole chat transcript (session content
rendered blank, Vue warn loop in console). vue-tsc can't catch this
(the reference sits inside a closure). Verified by a mount test that
reproduced the ReferenceError before the move and passes after, plus
an in-browser check that session content renders again.
2026-06-11 10:14:47 +08:00
qer
d8b1444e4c feat(web): resolve local image paths in markdown via daemon readFile 2026-06-11 10:14:47 +08:00
qer
31949f4c21 fix(web): remove leading dot from tool call summary 2026-06-11 10:14:47 +08:00
qer
3a9f111376 feat(web): copy entire turn content (thinking + text + tools) instead of text only 2026-06-11 10:14:47 +08:00
qer
333c45017d feat(web): replace copy icon with SVG document icon 2026-06-11 10:14:47 +08:00
qer
8d774aba12 feat(web): move copy button below assistant message for visibility 2026-06-11 10:14:47 +08:00
qer
878cad7945 feat(web): add copy button to assistant message bubbles 2026-06-11 10:14:47 +08:00
qer
539b4ae275 fix(web): display image attachments in chat user bubbles 2026-06-11 10:14:47 +08:00
qer
16cc8a0646 fix(web): widen model name display to 280px/240px 2026-06-11 10:14:47 +08:00
qer
75511deece fix(web): relax model name max-width to 240px/200px with ellipsis overflow 2026-06-11 10:14:47 +08:00
qer
4e8559798d fix(web): truncate long model names in composer toolbar with ellipsis 2026-06-11 10:14:47 +08:00
qer
64246cf613 fix(web): bump mobile font sizes by 2px and pin input at 16px to prevent iOS zoom 2026-06-11 10:14:47 +08:00
qer
84ad02a486 fix(web): hide SVG arrow in mobile send button to avoid double arrows 2026-06-11 10:14:47 +08:00
haozhe.yang
f9ca3cf1b7 feat(kimi-code): add kimi web command and daemon background mode
- add kimi web subcommand to open the daemon-hosted browser UI\n- refactor kimi daemon to start in background by default; add --foreground flag\n- serve web assets from daemon via new registerWebAssetRoutes\n- copy kimi-web build output into CLI bundle at build time\n- update docs for daemon and web commands\n- add unit tests for daemon and web command handlers
2026-06-11 10:14:47 +08:00
qer
b2a49ce231 fix(web): polish mobile tab bar and collapse composer toolbar into model dropdown 2026-06-11 10:14:46 +08:00
qer
d65b499084 fix(web): show an image placeholder for image-only queued prompts
A prompt queued while the agent is busy can carry image attachments
with no text; the queue strip rendered its bare text — an empty string,
so the row was just a blank button next to a remove cross. Queue items
now expose {text, attachmentCount}: image-only prompts render an
'image ×N' placeholder with a small badge, and any item carrying
images disables the load-back-into-input edit action (the uploaded
files can't be restored to the composer; editing would silently drop
them — remove stays available). Verified with component render tests
for the three shapes (image-only / text-only / text+images).
2026-06-11 10:14:46 +08:00
haozhe.yang
d992719560 refactor(protocol): invert protocol-agent-core dependency direction
- move event payloads and tool display schemas from agent-core into protocol\n- make agent-core depend on protocol instead of the reverse\n- remove alias workarounds in protocol build config\n- add changeset for agent-core, protocol, and kimi-code
2026-06-11 10:14:46 +08:00
haozhe.yang
86bd89ff7f refactor(services,protocol): remove node-sdk dependency
- Replace `@moonshot-ai/kimi-code-sdk` with `@moonshot-ai/agent-core` in protocol\n- Remove `@moonshot-ai/kimi-code-sdk` from services dependencies\n- Introduce internal `managedAuth` facade in services to replace `KimiAuthFacade`\n- Add compile-time assertions that neither package references the node SDK
2026-06-11 10:14:46 +08:00
qer
b8a9e1cd05 feat(web): widen default sidebar to 220px 2026-06-11 10:14:46 +08:00
qer
c3955cb04b fix(web): rework chat auto-scroll around a user-intent 'following' flag
The follow-to-bottom gate was an atBottom position snapshot updated by
scroll events, which broke in three ways users hit daily:

- the scroll event fired by our own pin could observe a view that had
  already grown past the 80px threshold mid-stream (thinking / tool
  phases) and flip the gate off — the view stopped above the newest
  content and a 'new messages' pill appeared without any user scroll;
- QuestionCard replaces the Composer in the bottom dock OUTSIDE the
  scroller, so its appearance (and the composer growing via queue strip
  / attachments / multiline input) shrank the scroll viewport without
  producing a single scroll or mutation event — nothing re-pinned and
  the newest message stayed hidden behind the dock;
- sending a prompt while scrolled up only raised the pill.

following is now an intent flag: it turns off ONLY when the user
scrolls up out of the bottom zone (our own scrolls always move down,
so an upward scrollTop is always user intent; sub-80px drifts never
break it), and back on when they return, click the pill, send a
prompt, answer a question, or switch session/tab. ResizeObservers on
the dock, the scroller and the content column re-pin on pure layout
changes (the QuestionCard case and image loads), without raising the
pill; the 1200ms stick-window machinery is replaced by the flag.
Also re-pins on visibilitychange (background tabs freeze rAF).

Verified e2e against the stub daemon: full-stream follow stayed within
1px across thinking/tool/approval phases (155 samples); mid-stream
scroll-up stopped following and raised the pill; sending and answering
while scrolled up force-pinned; QuestionCard appearance kept the view
pinned (max 1px); content-collapse clamp events did not break follow.
2026-06-11 10:14:46 +08:00
qer
d657a59ff6 fix(web): normalize context meter hover and prevent logo double-click selection 2026-06-11 10:14:46 +08:00
qer
39f8b6b4e0 refactor(web): delete dead WorkspaceRail and StatusLine components
Neither component is imported or rendered anywhere since the sidebar
redesign (verified by repo-wide grep): 649 + 485 lines of unreachable
UI. Worse than dead weight, both contained stale forks of live code —
WorkspaceRail duplicated the settings popover that now lives in
Sidebar.vue (already missing its codeFont/accent additions), and
StatusLine duplicated the Composer-toolbar controls with a diverged
permission color mapping — so edits could land in the dead copy and
silently no-op. Also removes the five workspace.* i18n keys only the
rail used and updates the comments that still pointed at StatusLine.
2026-06-11 10:14:46 +08:00
qer
c4babdaff6 fix(web): stop dropping in-flight turn content on re-select and resync
Three projection bugs that corrupted live streaming:

- Every sidebar click re-subscribed the session, and the subscribe
  wrapper unconditionally projector.reset() — wiping the turn/prompt
  bindings, after which every remaining delta/tool event of the
  in-flight turn was silently discarded (turn.step.started hard-bailed
  on the missing promptId, so it never self-healed). Re-subscribing no
  longer resets; only the resync path (which reloads messages) does.

- turn.step.started and tool.result now synthesize a promptId when the
  binding is missing (mid-turn join after reconnect/resync), mirroring
  turn.started, so the rest of the turn renders instead of vanishing.

- The projector emitted messages/content by reference and then mutated
  them in place (slot.text += delta) while the reducer also appended
  the delta to the same object — doubling the first streamed chunk of
  every text/thinking block. Emits now clone the content objects.
2026-06-11 10:14:46 +08:00
qer
784574364e fix(web): reconnect the daemon WebSocket after unexpected drops
DaemonEventSocket had no reconnect logic at all (the close() docstring
mentioned 'reconnect attempts' that never existed): one daemon restart,
laptop sleep or network blip permanently killed all live updates —
replies, approvals, questions and status changes never arrived again
and the only recovery was a full page reload. connectEventsIfNeeded's
eventConn guard made the loss unrecoverable from above.

onclose now schedules connect() with exponential backoff + jitter
(1s..30s, reset on a successful hello); the kept subscriptions map is
replayed via client_hello on reconnect and a too-large seq gap is
handled by the existing resync_required path. close() still stops
everything. Verified against a live WS server: kill → backoff →
reconnect → subscriptions re-sent with their lastSeq.
2026-06-11 10:14:46 +08:00
qer
9ad0efa010 fix(web): clean up turn-end state for the session that actually finished
The only turn-end cleanup was a watch(activity) callback, and activity
is computed from the ACTIVE session — so a session that finished while
the user was looking at another one never had its in-flight flag
cleared or its queue flushed. Switching back didn't help (idle → idle
is not a watch transition): the session was bricked — permanent
'sending…' placeholder and every new prompt silently enqueued forever,
recoverable only by a page reload that also discarded the queue.

Cleanup + queue flush now run from the WS sessionStatusChanged → idle
event for the session the event names (background sessions included);
git/runtime status refreshes still only run for the on-screen session.
2026-06-11 10:14:46 +08:00
qer
ce405b7945 fix(web): add a 30s timeout to all daemon REST calls
fetch() was issued with no signal anywhere, so a hung connection (the
half-open TCP you get after a network change — the same scenario that
kills the WS) left the promise pending for minutes. submitPrompt sets
the per-session in-flight flag before awaiting, and that flag is only
cleared on settle, so one hung submit silently routed every subsequent
prompt into the queue until the browser's own socket timeout fired.
AbortSignal.timeout(30s) turns the hang into the existing
DaemonNetworkError path (with a jsdom-safe fallback), which already
cleans up the in-flight state.
2026-06-11 10:14:46 +08:00
qer
d6f80932c5 fix(web): stop losing image attachments and failed prompts silently
Three related silent-loss paths:
- Submitting while an image upload was in flight sent the prompt
  WITHOUT the image and cleared the chips; the composer now refuses to
  submit until uploads settle (text + chips stay put).
- Sending while the agent was busy enqueued only the prompt text —
  attachments were dropped with no warning. Queue entries are now
  structured {text, attachments} and the flush passes both through.
- A failed submit left the optimistic user message in the transcript
  looking delivered (until a reload silently removed it), and a failed
  queue flush dropped the prompt entirely. The optimistic message is
  now rolled back in the catch, and a failed flush re-queues the prompt
  at the head.
2026-06-11 10:14:46 +08:00
qer
5acbdd22d3 fix(web): show running state for tools that are still executing
toolUse blocks were stamped status 'ok' the moment they appeared
(unless awaiting approval), so every executing tool rendered a green
check that could later flip to a cross — the ToolCall spinner state was
unreachable in practice. Tools now start as 'running' and resolve to
ok/error when their toolResult is absorbed; turns that were ended by a
later message settle dangling tools back to 'ok' so aborted turns in
old transcripts don't spin forever. Behaviour verified for live,
historical-dangling, completed and error cases.
2026-06-11 10:14:46 +08:00
qer
b75fef40ed fix(web): read approval display from the daemon's tool_input_display field
The daemon broadcasts approval payloads with tool_input_display
(packages/protocol/src/approval.ts) but the client only read a
non-existent 'display' field, so against the real daemon every approval
card fell through to the generic one-liner: file-edit approvals showed
no diff, shell approvals no command/cwd/danger info — users were
approving actions blind. Read tool_input_display first and keep
'display' as a fallback for the stub daemon's older shape.
2026-06-11 10:14:46 +08:00
qer
32faee20d4 fix(web): stop parseDiff treating deleted '-- ' lines as file headers
Inside a hunk, a deleted SQL/Lua/Haskell comment line renders as
'--- comment' in unified diff output and matched the '--- ' file-header
pattern, flipping inHunk off and silently dropping the rest of the hunk
from the ~/diff view. Only 'diff --git' can end a hunk now; the other
header patterns are only honoured between hunks. Verified with a real
SQL-comment deletion diff.
2026-06-11 10:14:46 +08:00
qer
375ff01e47 feat(web): align mobile shell with the redesigned desktop sidebar
MobileSwitcherSheet: replace the old workspace-chips row + flat
active-workspace session list with the desktop sidebar's design —
collapsible workspace groups (folder icon + name + branch/path
sub-line + per-group new-session button) over all workspaces, plus a
'+ new workspace' top row. Session titles share an alignment contract
(--m-pad/--m-gutter/--m-gap) with the group headers, mirroring the
desktop --sb-* contract; the modern inset-pill override compensates
its margin so titles stay on the alignment line.

MobileSettingsSheet: add the desktop settings-popover capabilities
that had no mobile counterpart — theme + accent segmented toggles,
language switcher, and sign in/out.

Tested e2e in a real browser via a same-origin 375px iframe driving
the actual app + stub daemon: mobile shell activates, switcher shows
5 groups with correct session counts, group collapse toggles, session
tap switches the active session and closes the sheet, scrim closes,
theme/accent toggles flip html[data-theme] live, and 375/414/640
viewports show no horizontal overflow. (Initial 'stuck sheet' was the
background-tab rAF freeze, not an app bug — verified by patching rAF.)
vue-tsc passes.
2026-06-11 10:14:46 +08:00
qer
2feead181c feat(web): de-terminalize remaining components under the modern theme
A 9-agent sweep of all 36 components found ~110 terminal-styled remnants
still rendering under modern: sharp 0-4px corners on dialogs/menus/cards
(approval+question cards, slash/mention menus, statusline popover, all
six dialog shells, file/diff/task rows), --mono hardcoded on UI copy,
2px blue 'terminal stripe' dialog tops, and hardcoded colors bypassing
the token system. Adds a grouped de-terminalization layer to style.css
using the --r-* / --sans / --sh tokens; code, paths, commands and
timestamps deliberately keep --mono.

Also fixes real bugs the sweep surfaced:
- var(--blue-soft) was referenced in Composer + style.css but defined
  nowhere, so the Plan pill active state and permission-row highlight
  rendered with no background; replaced with the existing --soft token
- the modern .se row override also matched MobileTopBar's unrelated .se
  span, mis-spacing the mobile title path; now scoped to .sessions .se
- #1565C0 hardcoded in DiffView/FileTree/ChangedTree ignored the
  html[data-accent=mono] grayscale remap; routed through var(--blue)
- .gh-name hardcoded #000 instead of var(--ink)

Verified in-browser (modern + terminal): dialogs, slash menu, settings
popover, sidebar; terminal theme is untouched.
2026-06-11 10:14:46 +08:00
qer
f1ecfc59cf fix(web): align session/workspace text via shared sidebar indent contract
Workspace names, path lines and session titles each derived their left
edge from unrelated magic numbers (terminal: session 5px left of the
workspace name; modern: 1px right because the .se inset margin was not
compensated; path line 2px off). Define one --sb-pad-x/--sb-gutter/
--sb-gap contract on .side and derive all three from it; drop the dead
:root block in SessionRow's scoped style. Verified in-browser: all
three text edges at x=34 in both themes.
2026-06-11 10:14:46 +08:00
haozhe.yang
bf0b827119 refactor(daemon): extract API v1 route registration into registerApiV1Routes module
- Extract inline /api/v1 route setup from start.ts into registerApiV1Routes\n- Centralize health check, meta, auth, sessions, messages, and all other route registrations\n- Reduce start.ts size and improve separation of concerns
2026-06-11 10:14:46 +08:00
haozhe.yang
51b3e65a7a refactor(services): move daemon fs, fileStore, logger and workspace services to shared package
- Move filesystem, fileStore, logger and workspace service implementations from daemon to shared services package\n- Update daemon routes and services to import from shared package\n- Move chokidar and ignore dependencies to services package\n- Rename daemon loggerService.ts to pinoLoggerService.ts
2026-06-11 10:14:46 +08:00
haozhe.yang
3cf8fd37f7 test(daemon-e2e): add prompt queue steer e2e scenario
- add scenario 10 verifying REST + WS contract for steering queued prompts\n- cover debug active-prompt injection, queue assertions, steer response,\n  prompt.steered frame, content verification, and queue drain\n- update README scenario index
2026-06-11 10:14:46 +08:00
haozhe.yang
888c935d81 refactor(agent-core): consolidate lifecycle and event primitives, remove redundant JSDoc
- remove extensive JSDoc comments from event, lifecycle, and instantiationService\n- add new disposable utilities: RefCountedDisposable, ReferenceCollection, AsyncReferenceCollection, ImmortalReference, MandatoryMutableDisposable\n- change dispose() to throw collected errors instead of swallowing via onUnexpectedError\n- use combinedDisposable in Event.any and simplify Emitter internals\n- refactor InstantiationService.dispose to use centralized dispose() helper\n- update tests to match new error-throwing behavior
2026-06-11 10:14:46 +08:00
haozhe.yang
196e8a5040 feat(di): add sinon-based test helpers and IConstructorSignature
- add sinon mocking, spying, and stubbing to TestInstantiationService (mock, spy, stubPromise, stubInstance)\n- add createServices factory for disposable test service collections\n- export IConstructorSignature for constructor signatures with DI service args\n- remove verbose JSDoc comments from DI core files\n- migrate test-instantiation tests from vitest vi.fn to sinon
2026-06-11 10:14:46 +08:00
qer
52750eec62 fix(web): remove sidebar header white background in modern theme 2026-06-11 10:14:46 +08:00
qer
58139d06a5 feat(web): font/theme/scroll/thinking fixes + hide injected messages; drop web tests
- Font: switcher now applies site-wide (--mono + --sans); default Inter + font-smoothing
- Theme accent color toggle (Kimi blue / mono Vercel style)
- Hide system-injected user messages via origin in message metadata (TUI parity)
- Fix historical-session re-stream (markstream smooth-streaming gated on live streaming)
- Fix auto-scroll-to-bottom on opening a session (stick-to-bottom window over async load + late markdown render)
- Default-collapse thinking blocks in historical sessions
- Markdown badge images render at natural size
- Plus in-progress workspace/sidebar/rail/files/tasks UI work

Removes the apps/kimi-web test suite (WIP; restorable from history at e609a07).
2026-06-11 10:14:46 +08:00
qer
3a5d683f8f feat(web): Modern theme polish, onboarding, design system & backend wiring
UI/UX:
- Onboarding: welcome + language/theme prefs (Modern default, applied on start)
- Modern is the default theme; chat surface white; floating dock (input on top,
  status controls as functional pill-buttons below, ctx far right)
- Single-line composer with smaller send; empty-chat hint vertically centered
- Global connecting splash on first load; overlay drift fixes (viewport-unit sizing)
- Wide-screen reading-column cap; font-size pass (chrome up, chat = session list)
- Merged ~/files + ~/diff into one tab (Changed|All, list/tree)
- ThinkingBlock capped to ~3.5 lines with auto-scroll-to-latest
- Workspace rail title + branch on second line

Design system:
- Radius scale tokens (--r-xs/sm/md/lg = 6/8/12/16); unified component radii
- Moved Modern per-component overrides to global style.css (scoped :global() did
  not win the cascade — input/tabs/cards were silently un-styled)
- docs/design-system.html: tokens + live component reference

Backend wiring:
- POST /sessions/{id}/profile for model + runtime (thinking/permission/plan)
- GET /sessions/{id}/status; :compact / :fork; agent.status.updated
- Historical replay no longer re-streams (messagesToTurns dedup)
2026-06-11 10:14:45 +08:00
haozhe.yang
9688b93a88 feat(daemon-e2e): add docker e2e workflow and undoSession client support
- add Dockerfile and run-docker-e2e.sh for isolated docker-based e2e testing\n- add docker:e2e npm script with workspace-scoped runner names\n- add undoSession method to DaemonClient and HttpClient\n- add live and mocked tests for undoSession\n- update AGENTS.md and README.md with docker:e2e usage docs
2026-06-11 10:14:45 +08:00
haozhe.yang
18224d47ff refactor(services,daemon,agent-core): align service registry bootstrap and expand DI lifecycle primitives
- remove defaultServicesModule() and services/src/module.ts; consume getSingletonServiceDescriptors() directly\n- update daemon service registrations and bootstrap to use registry descriptors\n- add DisposableMap, DisposableSet, disposable tracking, and disposeOnReturn to agent-core DI\n- update AGENTS.md with new registration patterns
2026-06-11 10:14:45 +08:00
haozhe.yang
d4eacc7e7d feat(prompt): add prompt queue and steer support
- add per-session prompt queue: concurrent submits return queued status\n- add list/steer REST endpoints and protocol schemas\n- synthesize prompt.steered lifecycle event\n- add debug endpoint to inject active prompts for e2e testing\n- add daemon-e2e queue + steer invariant test\n- fix WS ping frame handling in daemon-e2e client
2026-06-11 10:14:45 +08:00