fix(ai-chat): resolve current page for agent context (#43, hardness #1) #47

Merged
Ghost merged 27 commits from fix/ai-chat-current-page into develop 2026-06-21 01:33:29 +03:00

Исправление баг #43 (Хрупкость №1) — агент AI-чата не получал «текущую страницу».

Проблема

AiChatWindow смонтирован в pathless-родительском layout-роуте, где сегмент :pageSlug не матчится. Поэтому useParams() возвращал {}pageSlug === undefinedusePageQuery отключён → openPage всегда null. Агент никогда не получал контекст текущей страницы и не понимал «эта/текущая страница».

Фикс (вариант A из issue)

Извлекаю pageSlug через useMatch("/s/:spaceSlug/p/:pageSlug") по полному pathname — матчится независимо от позиции компонента в дереве роутов. Поведение при отсутствии совпадения идентично прежнему (undefined → запрос отключён → openPage null).

Ревью

APPROVE, проблем нет. Подтверждено: паттерн точно совпадает с роутом в App.tsx; useParams больше нигде в файле не используется (импорт заменён чисто); query-key ключуется по строке pageId, а не по объекту match (стабильность сохранена); на redirect/share-роутах компонент вообще не рендерится.

Проверка

tsc --noEmit — exit 0.

Осталось в #43 (вне scope, НЕ закрывает issue)

  • Хрупкость №2 (устойчивость к прокси): pageId уходит только строкой в system-промпт; CLIProxyAPI может его склеить/обрезать. Нужен инструмент get_current_page (берёт pageId из серверной сессии) ИЛИ скрытый префикс в user-сообщение — вариант C. Это серверная работа + новый tool, отдельным PR.
  • Противоречие из issue (по коду openPage должен быть null в обоих режимах, а юзер говорит, что напрямую работает) — теперь, когда openPage реально резолвится, симптом «не вижу страницу» должен уйти в обоих режимах; останется проверить прокси-кейс.

🤖 Generated with Claude Code

Исправление баг #43 (Хрупкость №1) — агент AI-чата не получал «текущую страницу». ## Проблема `AiChatWindow` смонтирован в pathless-родительском layout-роуте, где сегмент `:pageSlug` не матчится. Поэтому `useParams()` возвращал `{}` → `pageSlug === undefined` → `usePageQuery` отключён → `openPage` **всегда null**. Агент никогда не получал контекст текущей страницы и не понимал «эта/текущая страница». ## Фикс (вариант A из issue) Извлекаю `pageSlug` через `useMatch("/s/:spaceSlug/p/:pageSlug")` по полному pathname — матчится независимо от позиции компонента в дереве роутов. Поведение при отсутствии совпадения идентично прежнему (undefined → запрос отключён → openPage null). ## Ревью APPROVE, проблем нет. Подтверждено: паттерн точно совпадает с роутом в App.tsx; `useParams` больше нигде в файле не используется (импорт заменён чисто); query-key ключуется по строке pageId, а не по объекту match (стабильность сохранена); на redirect/share-роутах компонент вообще не рендерится. ## Проверка `tsc --noEmit` — exit 0. ## Осталось в #43 (вне scope, НЕ закрывает issue) - **Хрупкость №2 (устойчивость к прокси):** pageId уходит только строкой в system-промпт; CLIProxyAPI может его склеить/обрезать. Нужен инструмент `get_current_page` (берёт pageId из серверной сессии) ИЛИ скрытый префикс в user-сообщение — вариант C. Это серверная работа + новый tool, отдельным PR. - **Противоречие** из issue (по коду openPage должен быть null в обоих режимах, а юзер говорит, что напрямую работает) — теперь, когда openPage реально резолвится, симптом «не вижу страницу» должен уйти в обоих режимах; останется проверить прокси-кейс. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Ghost added 1 commit 2026-06-20 21:57:32 +03:00
AiChatWindow derived the open page via useParams(), but it's mounted in a
pathless parent layout route where :pageSlug isn't matched, so useParams()
returned {} and openPage was ALWAYS null — the agent never received current-page
context (couldn't resolve 'this page'/'the current page'). Derive pageSlug from
useMatch('/s/:spaceSlug/p/:pageSlug') against the full pathname instead, so it
resolves regardless of where the component sits in the route tree. No-match
behavior is unchanged (undefined -> query disabled -> openPage null).

Addresses Hardness #1 of #43. Hardness #2 (proxy resilience: a get_current_page
tool / hidden user-message context so identity doesn't depend on the system
prompt surviving CLIProxyAPI) remains open.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ghost added 1 commit 2026-06-20 22:19:52 +03:00
The current page id was only injected as text in the system prompt, which a
proxy (CLIProxyAPI) can rewrite/truncate, so the agent could lose track of 'this
page'. Add a getCurrentPage tool the model can call to read the open page (id +
title) from the server-side request context (forUser now takes openedPage,
threaded from body.openPage — the same value used for the system prompt). The
inline system-prompt line is kept as belt-and-suspenders. Reads/writes still go
through the CASL-enforced page tools by id, so this is strictly not worse than
the existing prompt hint — just delivered over a channel the proxy can't mangle.

User-approved on the issue. Completes #43 together with the hardness-1 fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ghost merged commit 4f5a08cba0 into develop 2026-06-21 01:33:29 +03:00
Sign in to join this conversation.