247 """Resolve the presenter to use — the plan 147 D3 pinned selection matrix.
251 - Explicit ``--ui`` flag or ``OC_CHAT_UI`` env var: honoured whenever
252 ``rich``/``enhanced`` import — even non-tty and even in batch mode
253 (uniform precedence, a defined outcome for captured/test runs) — via
254 the existing ImportError-to-plain fallback + stderr warning below.
255 - Implicit (neither set) + batch mode (``--input``/``--output`` present):
256 ``plain`` regardless of tty — batch is non-interactive by definition;
257 a globally exported ``OC_CHAT_UI`` would otherwise silently pollute
258 CI/headless captured output with implicit chrome (the explicit row
259 above still opts in deliberately).
260 - Implicit + interactive: ``rich`` when the package imports AND
261 ``sys.stdout.isatty()`` (stdout, not stdin, governs — Rich renders to
262 stdout); else ``plain``.
263 - ``NO_COLOR``: MONOCHROME-RICH (ratified 2026-07-12) — deliberately NOT
264 checked here. Whatever presenter the rows above resolve stays
265 resolved; ``rich.console.Console`` honours ``NO_COLOR`` natively
266 (verified: no_color=True / color_system=None with no explicit pass-
267 through needed), so this is never a presenter downgrade.
268 - The legacy ``--enhanced-ui`` flag keeps its pre-existing absolute-
269 override precedence (checked independently of ``want`` below,
270 unchanged from before D3).
272 Lazy-imports :class:`RichPresenter` on demand so the optional ``rich``
273 dependency is not required at module-import time.
275 If ``--log-events PATH`` was passed, the resolved presenter is
276 wrapped in a :class:`LoggingPresenter` that tees every event to PATH
277 in JSON-lines format.
280 func =
"select_presenter"
283 explicit = args.ui
or os.getenv(
"OC_CHAT_UI")
286 _dbg(
"OC_CHAT.MAIN", func, f
"explicit ui={want!r}", 3)
288 is_batch = getattr(args,
"input",
None)
is not None or getattr(args,
"output",
None)
is not None
293 _dbg(
"OC_CHAT.MAIN", func, f
"implicit ui={want!r} batch={is_batch}", 3)
298 inner = RichPresenter(verbose=verbose)
299 _dbg(
"OC_CHAT.MAIN", func,
"selected RichPresenter", 2)
300 except ImportError
as exc:
302 _dbg(
"OC_CHAT.MAIN", func, f
"VALIDATION_ERROR: rich not installed: {exc}", 3)
304 f
"[--ui rich requested but `rich` is not installed ({exc}). "
305 f
"Install with: pip install -e .[rich]. Falling back to plain.]",
309 elif want ==
"enhanced" or getattr(args,
"enhanced_ui",
False):
312 inner = EnhancedPresenter(
313 model=
"deepseek-v4-pro",
314 max_context_size=getattr(args,
"max_context_size", DEFAULT_CONTEXT_WINDOW_SIZE),
317 _dbg(
"OC_CHAT.MAIN", func,
"selected EnhancedPresenter", 2)
318 except ImportError
as exc:
320 _dbg(
"OC_CHAT.MAIN", func, f
"VALIDATION_ERROR: enhanced UI not available: {exc}", 3)
322 f
"[--ui enhanced requested but components missing ({exc}). "
323 f
"Falling back to plain.]",
329 _dbg(
"OC_CHAT.MAIN", func,
"selected ConsolePresenter", 2)
331 log_path = getattr(args,
"log_events",
None)
334 return LoggingPresenter(inner, log_path)