Skip to content

feat(desktop): side conversation dock and /side#1

Closed
taekchef wants to merge 2575 commits into
mainfrom
feat/side-conversation
Closed

feat(desktop): side conversation dock and /side#1
taekchef wants to merge 2575 commits into
mainfrom
feat/side-conversation

Conversation

@taekchef

@taekchef taekchef commented Jun 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Multi-turn side conversations with resizable right dock
  • createSide → POST /api/conversations/:id/side with Phase 1 fallback
  • /btw unchanged; 16 unit tests

Test plan

  • bun run test
  • Side dock on acp/codex session

Pair with iOfficeAI/AionCore#383 for Phase 2 backend.

Made with Cursor

IceyLiu and others added 30 commits May 13, 2026 02:13
Extend the Chinese rename to the remaining 7 locales. Applies only to
user-facing strings — i18n keys, storage fields, and system workDir
stay untouched.

Per-language term:
- en-US: Workspace → Project (home-page button reads 'Chat in Project')
- zh-TW: 工作空間/工作區 → 專案
- ja-JP: ワークスペース → プロジェクト
- ko-KR: 워크스페이스/작업 공간 → 프로젝트
- ru-RU: Рабочая область → Проект
- tr-TR: Çalışma Alanı → Proje
- uk-UA: Робоча область → Проєкт

Also drop stale references to the removed 'workspace migration' feature
in export-dialog hints.
iOfficeAI#2891)

Problem: opening /guid fired 13 GET /api/agents + 3 POST /api/agents/refresh
because 17+ call sites bypassed SWR by calling
`ipcBridge.acpConversation.getAvailableAgents.invoke()` directly, and
useCustomAgentsLoader ran a refresh-on-mount effect that cascaded into
mutate-driven refetches.

Changes:
- Add renderer/hooks/agent/useAgents.ts exposing useAgents()/getAgents()/
  refreshAgents(), all reading/writing the DETECTED_AGENTS_SWR_KEY cache.
- Migrate 11 components and 4 hooks (MCP, channels, settings, team) from
  direct .invoke() to the new entry points. Non-React callers use getAgents().
- Replace useCustomAgentsLoader's own fetch+mount-refresh with a subscription
  to the shared cache; refreshCustomAgents is now explicit-only.
- Drop useMultiAgentDetection and the multiAgentModeEnabled i18n key — agent
  detection is handled by aionui-backend, the toast was redundant.
- Add scripts/check-agents-invoke.js and a pre-commit hook that forbids
  direct getAvailableAgents.invoke outside the canonical entry points.
- Remove an unused destructured prop in AcpChat.tsx that was breaking tsc.
- Update windowBounds.test.ts to match the new 0.95 default height ratio.

Expected: /guid issues 1 GET /api/agents and 0 POST /api/agents/refresh.

Co-authored-by: zk <zk@users.noreply.local>
… appear in leader dropdown (iOfficeAI#2893)

teamCapableKeys only collected id and backend from team-capable CLI agents,
but Aion CLI has no backend field — only agent_type: "aionrs". This caused
all preset assistants (preset_agent_type: "aionrs") to fail the capability
check and be filtered out of the team leader selector.

Co-authored-by: zhuqingyu <zhuqingyu@bituniverse.org>
New backend does not implement WeChat Work (WeCom) plugin yet.
Mark the channel as coming_soon to disable the toggle and show a
placeholder message, consistent with Slack and Discord.
…fficeAI#2894)

ACP cron jobs store the literal string "acp" in `agent_type`; the actual
vendor id (claude/gemini/codex/…) and display name live in
`agent_config.backend` / `agent_config.name`. The existing helper passed
`agent_type` straight to the logo map and agent lookup, so ACP tasks
rendered as "acp" with no logo on the scheduled-tasks list and detail
pages.

Extract the resolver into `jobAgentMeta.ts` with a dedicated ACP branch,
and share it between `ScheduledTasksPage` and `TaskDetailPage`. Non-ACP
agents (aionrs, remote, nanobot, …) keep using `agent_type` directly,
since aionrs reuses `agent_config.backend` for provider_id.

Co-authored-by: zk <zk@users.noreply.local>
…go immediately (iOfficeAI#2895)

- AssistantEditDrawer's Main Agent Select was writing the AgentMetadata
  row id (e.g. "2d23ff1c") into `preset_agent_type` because
  useDetectedAgents exposed `a.id` as the option value. Align the option
  id with the storage contract by using `a.backend || a.agent_type`, so
  selecting Claude Code persists "claude" — matching the slug every
  downstream consumer (config keys, agent resolution, logo lookup) expects.

- GuidPage.handlePresetAgentTypeSwitch only revalidated the detected
  agents SWR cache, not `assistants.list`, so selectedAssistantRecord
  (and the derived effectiveAgentLogo) lagged a network roundtrip behind
  the click. Optimistically patch `assistants.list` before the PUT and
  revalidate after, so the hero logo updates on the same frame as the
  user action.

Co-authored-by: zk <zk@users.noreply.local>
- replace hand-drawn vertical dots with MoreOne icon across team/conversation/project-folder rows
- give more buttons a persistent bg-fill-3 with hover bg-fill-2 (vs row hover bg-fill-3)
- lift menu buttons to text-t-secondary -> text-t-primary on hover; unify rd-4px size-20px shell
- always show team create button; add tooltips for team create (+) and project-folder create (+)
- widen project-folder trailing gap from 2px to 6px
- rewrite project-folder row: fixed trailing slot (w-22px) + flex-1 header so the name truncates before the button area
- increase conversation row right reserve to pr-16px so the name truncates before the hover menu
- trim team row right reserve from pr-24px to pr-12px for a bit more room
- en-US: 'New conversation in this project' -> 'New chat in this project'
… color

- light mode: --text-primary and Arco --color-text-1 to #000000
- dark mode: --text-primary and Arco --color-text-1 to #ffffff
- keep the override in sync across both themes/default-color-scheme.css and
  DisplaySettings/presets/default.css (the latter is injected at runtime)
- add sider-section-title hook; dark mode sider/settings section titles pick
  a mid-gray (#9098a1) so they remain legible on #0e0e0e without being as
  bright as --text-secondary
…xt bleed

- introduce .sider-action-btn hook on more/plus buttons across sider rows
- light mode: --bg-2 default, --bg-1 on hover
- dark mode: --bg-5 default, --bg-6 on hover (lean toward white)

Arco's --color-fill-* tokens are semi-transparent in dark mode, which let
the row text show through when the button sat on top of a hovered row.
…, default gemini→aionrs (iOfficeAI#2897)

* fix(migration): tolerate retired built-in ids and add word-form-creator

- `applyBuiltinOverrides` treated any non-2xx as failure, including the
  404 the backend returns when a legacy `enabled=false` override names a
  built-in id that the current manifest no longer ships (e.g. the retired
  `pdf-to-ppt` / `pptx-generator`). One dead preference kept the whole
  migration flag false, so every launch retried forever and the
  `assistants` key was never removed from the Electron config. Treat 404
  as a skip (the assistant is gone, the preference is moot) while still
  failing on real errors.

- `PRESET_ID_WHITELIST` drifted from the backend manifest: the newer
  `word-form-creator` built-in was only protected by the `builtin-`
  prefix check. An unprefixed user row named `word-form-creator` would
  have been imported and then shadowed by the built-in of the same id.
  Added the missing entry. Backend `preset-id-whitelist.json` is updated
  to match in a separate PR.

Tests: added two regression cases for the 404 skip path — one asserting
the migration finalizes (and removes the key) despite a retired id, one
asserting a 500 still keeps the flag false.

* feat(migration): preserve explicit preset_agent_type and default gemini→aionrs

Two related changes layered onto the assistant migration:

1. `legacyAssistantToCreateRequest` treats the legacy default `'gemini'`
   (and absent / non-string values) as "no explicit choice" and rewrites
   to `'aionrs'`. The internal Gemini engine was removed from the
   current build — what remains with that name is a distinct ACP backend
   the user must install. Old legacy rows were stamped with `'gemini'`
   by default, so importing them verbatim would leave every migrated
   user assistant pointing at a backend that isn't there on boot. Users
   who explicitly picked `'codex' / 'claude' / 'qwen' / …` keep their
   choice verbatim.

2. New Phase 3 in `migrateAssistantsToBackend`: replay the user's
   explicit `presetAgentType` pick for legacy built-ins via
   PUT /api/assistants/{id}. Live manifest (via `GET /api/assistants`)
   is the source of truth for the current default, so an identical
   choice is filtered out (no no-op override rows) and retired built-in
   ids are also filtered ahead of the network call. 404 at call time
   still skips (same rationale as Phase 2).

Net behaviour for the three interesting legacy states:
  - `presetAgentType: 'gemini'` or missing  → current default (`aionrs`)
  - `presetAgentType: 'aionrs'`              → no-op (matches default)
  - `presetAgentType: 'codex' / 'claude' / …` → PUT override, pick survives

Tests: added 3 unit cases around the `'gemini' → 'aionrs'` rewrite, and
4 integration cases covering Phase 3 — explicit pick preserved, legacy
default collapsed, already-matching default skipped, retired id filtered.

---------

Co-authored-by: zk <zk@users.noreply.local>
…iceAI#2898)

Backend refactor (aionui-backend#254) moved session-scoped ACP
operations from /api/acp/* and ai-agent-owned handlers into the
conversation domain. Sync the frontend to the new contract:

- POST /api/conversations/{id}/stop → /cancel (conversation.stop
  keeps its name; only the underlying URL changed)
- /api/acp/health-check → /api/agents/health-check
- Drop /api/acp/detect-cli and /api/acp/env (unused by the UI)
- Drop /api/conversations/{id}/config and
  /api/conversations/{id}/reload-context — frontend never called
  the config endpoints and reloadContext had no call sites
- Rename acpConversation.getModelInfo → getModel to match the
  backend handler name; update AcpModelSelector call sites
- Remove cached_config_options from AcpChat destructure (already
  broken tsc, the prop was never declared)
…#2899)

* refactor(ipcBridge): align ACP endpoints with backend migration

Backend refactor (aionui-backend#254) moved session-scoped ACP
operations from /api/acp/* and ai-agent-owned handlers into the
conversation domain. Sync the frontend to the new contract:

- POST /api/conversations/{id}/stop → /cancel (conversation.stop
  keeps its name; only the underlying URL changed)
- /api/acp/health-check → /api/agents/health-check
- Drop /api/acp/detect-cli and /api/acp/env (unused by the UI)
- Drop /api/conversations/{id}/config and
  /api/conversations/{id}/reload-context — frontend never called
  the config endpoints and reloadContext had no call sites
- Rename acpConversation.getModelInfo → getModel to match the
  backend handler name; update AcpModelSelector call sites
- Remove cached_config_options from AcpChat destructure (already
  broken tsc, the prop was never declared)

* chore(docs): remove completed migration and archived specs

- Removed team MCP and backend migration documentation (Phase 1 complete)
- Removed ACP rewrite specs (superseded by current implementation)
- Removed archived feature specs (extension-market, remote-agent, wake-prompt)
- Removed E2E audit reports and handoff documents
- Cleaned up 156 obsolete documentation files (65,398 lines)
Gemini CLI has been removed; only Aion CLI supports custom models now.
Backend now receives work_dir as explicit CLI argument instead of
relying solely on AIONUI_WORK_DIR env var. Env var kept as fallback.
cacheDir is no longer meaningful in the new backend-separated
architecture. The setting is removed from the UI while preserving
the underlying IPC contract — existing custom paths are passed
through unchanged to avoid breaking users who previously modified it.
…gent ready

- Fix ipcBridge type to match actual backend response (array, not object)
- Map backend { command, description } to frontend SlashCommandItem format
- Defer slash-commands fetch until agentStatus is non-null to avoid 404
  on newly created conversations before the agent is warmed up
Slash commands fetch was racing against agent startup — the API returned
404 because the agent task did not exist yet. Call warmup on conversation
entry and only trigger the slash-commands fetch after it succeeds.
…iOfficeAI#2914)

* fix(acp): use WebSocket-pushed available_commands for slash menu

The backend already pushes available_commands via WebSocket during
session bootstrap. Instead of polling the HTTP API (which was broken
because the session aggregate never stored commands), consume the
WebSocket event directly in useAcpMessage and pass the commands to
AcpSendBox.

* fix(acp): fetch slash commands via HTTP on hydration

WebSocket push of available_commands arrives during warmup when no
StreamRelay is listening, so the event gets dropped. Add an HTTP fetch
via /slash-commands after conversation hydration completes to populate
the initial command list. Runtime updates still flow through the
existing WebSocket available_commands handler.

* fix(acp): warmup before fetching slash commands

Change the HTTP fetch timing from 'after hydration' to 'after warmup
completes'. This mirrors the aionrs pattern: call warmup API first
(which ensures the session aggregate has available_commands data from
the CLI notification), then fetch /slash-commands.

Previous approach fetched on hasHydratedRunningState which could race
with warmup - the agent task might not be ready yet.

---------

Co-authored-by: zynx <>
piorpua and others added 28 commits May 27, 2026 21:15
* Use backend MCP servers as settings source

* Fix builtin MCP bootstrap config sync

* Fix image MCP provider resolution

* Hide internal AionUi MCP status source

* Lock MCP toggle while syncing agents

---------

Co-authored-by: zynx <>
* fix(settings): use provider health check probe

* fix(settings): add aws credential health error type

---------

Co-authored-by: zynx <>
…ces (iOfficeAI#3084)

When re-entering a conversation, the ACP model selector silently rolled
back to the model that was pinned on conversation creation
(`extra.current_model_id`), even after the user had switched models —
the actual session was on the new model, but the UI lied about it
(ELECTRON-1RV).

Root cause: `useAcpModelInfo.reloadModelInfo` and the `acp_model_info`
stream handler used the conversation's stale `initialModelId` to
override the backend's authoritative `current_model_id` whenever
`hasUserChangedModel` had been reset (i.e. on every conversation
switch).

Fix:
- Treat backend `getModel().current_model_id` as the source of truth.
  Only fall back to `initialModelId` when the backend has no current
  model yet (genuine pre-handshake case).
- On every conversation-side model switch, persist
  `acp.config[backend].preferredModelId` and write the chosen id back
  into `conversation.extra.current_model_id`, so the home page picks up
  the new default and the same conversation's `initialModelId` stops
  shadowing the backend.
- Mirror the same persistence for mode switches (AgentModeSelector +
  ACP/Aionrs mobile sheets) and aionrs model switches in the
  conversation header and team views, so any change to model/mode —
  whether from the home page or a conversation — becomes the next
  session's default.
…ficeAI#3091)

* fix(settings): avoid blue switch during image generation loading

* fix(ci): remove unsupported setup-bun cache input

* Revert "fix(ci): remove unsupported setup-bun cache input"

This reverts commit 8338fb3.

---------

Co-authored-by: zk <zk@users.noreply.local>
Co-authored-by: zk <zk@users.noreply.local>
* test(web-host): cover backend crash restart port and logs

* fix(web-host): reuse backend port after crash restart

* style(web-host): format backend restart changes

---------

Co-authored-by: zynx <>
* feat(mcp): move MCP management to conversation scope

* test(mcp): align tests after session MCP refactor

* fix(mcp): clean up agent process trees on shutdown

* fix(mcp): clarify runtime hints and test status copy

* fix(mcp): tighten edit validation and status messaging

* fix(mcp): refine conversation MCP helper layout

* fix(mcp): clarify test status hint copy

---------

Co-authored-by: zk <zk@users.noreply.local>
* fix(conversation): show structured agent errors

* fix(conversation): always allow feedback on error tips

* feat(agent): preserve error resolution metadata

* feat(agent): show error resolution guidance

* style(agent): format error normalization test

* fix(conversation): localize agent error codes

- Add friendly title and body copy for newly classified agent error codes in every locale
- Update MessageTips tests to use the real billing-required code and guard against fallback copy
- Regenerate typed i18n keys for the new conversation agent error entries

* fix(conversation): expand agent error details by default

* fix(conversation): label agent error suggestions

* fix(conversation): localize agent error copy
…AI#3121)

* fix(desktop): classify missing backend binary installs

* fix(desktop): add backend install diagnostics

---------

Co-authored-by: zynx <>
Co-authored-by: zk <zk@users.noreply.local>
Introduce multi-turn side threads with a resizable right dock, Cmd+Shift+S
and /side entry points, Phase 1 create params with POST /side fallback,
history filtering for ephemeral side chats, and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@taekchef

taekchef commented Jun 4, 2026

Copy link
Copy Markdown
Owner Author

Superseded by upstream PR: iOfficeAI#3196

@taekchef taekchef closed this Jun 4, 2026
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.

7 participants