Skip to content

# refactor(chat-ao): split controller into Base + Coworker subclass - #810

Open
anfibiacreativa wants to merge 2 commits into
mainfrom
feat/chat-ao-controller-decouple
Open

anfibiacreativa wants to merge 2 commits into
mainfrom
feat/chat-ao-controller-decouple

Conversation

@anfibiacreativa

Copy link
Copy Markdown
Member

Summary

  • Split the 683-line ao-controller.js into BaseChatController (shared by both harnesses) and CoworkerChatController extends Base (orchestrator REST episode history).
  • ao-controller.js is now a thin re-export of the Coworker controller, so chat-ao.js and all existing tests are unchanged.
  • Added a design-matrix doc (docs/chat-ao-controller-decoupling.md) that the split — and the follow-up CMA subclass — follows.

Why

The chat controller serves two harnesses from one class: AO-direct / CX Coworker (orchestrator REST history) and the CMA bridge (WS-only, replay on reconnect). Because they share one file, every change forced the question "does this regress the other path?" — the divergence (episode loading/resume, session-ready/error handling) is small but keeps growing, and a single shared controller makes isolation impossible. This is the first step of a staged decoupling so a change to one path cannot regress the other.

What Changed

  • base-chat-controller.js — BaseChatController holds everything common to both harnesses: WebSocket lifecycle (_connectionInfo/_ensureSocket/_connect/reconnect), streaming (_onTextDelta/_onTextDone), tool-call handling, question/plan/permission flows, sendMessage/stop, skills, and the EVENT_HANDLERS dispatch. The harness-specific seams are exposed as overridable hooks: loadEpisodes, switchEpisode, _onSessionReady, _onSessionError, startNewEpisode.
  • coworker-chat-controller.js — CoworkerChatController extends Base with the orchestrator REST episode cluster: _fetchEpisodes/_fetchEpisodeMessages/_fetchEpisodeContext/_fetchWarmSession, warmSession/_attach/reattachIfIdle, and loadEpisodes/_loadEpisode/_refreshEpisodeList/switchEpisode (incl. the 24h stale-episode rule).
  • ao-controller.js — reduced to a thin re-export of the Coworker controller, preserving the historical default export so chat-ao.js and the test suite need no import changes.
  • docs/chat-ao-controller-decoupling.md — the Base / Coworker / CMA responsibility matrix and staged rollout, referenced from the source comments.

This is intentionally behavior-preserving. No CMA subclass yet and no routing change — today's only instantiated controller is Coworker, which is byte-equivalent in behavior to the previous single class.

Test Plan

  • test/nx2/blocks/chat-ao/** — 316 tests across 19 files pass (zero behavior change).
  • ESLint clean on the three touched/added files.
  • Secrets scan clean over the full branch diff.
  • Smoke CX Coworker on the preview: new chat, episode switch, stop mid-turn, question/plan/permission flows, reconnect after idle.

Risks / Follow-ups

  • Risk: low — pure extraction with a re-export shim; the Coworker path is unchanged and fully covered by the existing suite. Base still calls this._attach()/this._refreshEpisodeList() (now Coworker methods) via dynamic dispatch, which is why Base keeps no-op hook defaults.
  • Follow-up PR 2: add CmaChatController extends Base — ports the ewoncma bridge features (buildAoConnectionInfo + activationKey combined with x-site, friendlyBackendError, skills→bridge, ?bridge= override, +menu customizations), folds in resume-on-reload, and selects the subclass by ew.altHarness in chat-ao.js.
  • Follow-up: the stop-indicator (feat/chat-ao-stop-indicator) lands in Base (shared behavior).
  • This retires ewoncma as a long-lived branch once its CMA work is absorbed current with main.

Test URLs:

First step of decoupling the AO-direct/Coworker and CMA-bridge chat paths so
a change to one cannot regress the other (see docs/chat-ao-controller-decoupling.md).

- base-chat-controller.js: BaseChatController with everything shared by both
  harnesses (WS lifecycle, streaming, tool calls, question/plan/permission,
  send/stop, skills) plus overridable hooks (loadEpisodes, switchEpisode,
  _onSessionReady, _onSessionError, startNewEpisode).
- coworker-chat-controller.js: CoworkerChatController extends Base with the
  orchestrator REST episode cluster (list/messages/context, warm/attach,
  loadEpisodes/_loadEpisode/_refreshEpisodeList/switchEpisode).
- ao-controller.js: thin re-export of the Coworker controller so chat-ao.js
  and existing tests are unchanged.

Zero behavior change: 316 chat-ao tests pass. The CMA subclass lands next.
Base + Coworker/CMA subclass responsibility matrix and staged rollout that the
split follows. Referenced by the controller source comments.
@aem-code-sync

aem-code-sync Bot commented Oct 2, 2026

Copy link
Copy Markdown

Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch.
In case there are problems, just click the checkbox below to rerun the respective action.

  • Re-sync branch
Commits

@anfibiacreativa
anfibiacreativa added this pull request to stack #812 October 3, 2026 07:43

This branch was successfully deployed

1 active deployment
feat/chat-ao-controller-decouple — 8189bca3 Deployed Oct 2, 2026 by aem-code-sync[bot]
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.

1 participant