feat(ai-chat): auto-collapse chat window on page focus (#42) #50

Merged
Ghost merged 30 commits from feat/ai-chat-collapse-on-focus into develop 2026-06-21 01:36:54 +03:00
2 changed files with 25 additions and 0 deletions
Showing only changes of commit a6ba19f0dc - Show all commits

View File

@@ -257,6 +257,9 @@ export class AiChatService {
sessionId,
workspace.id,
chatId,
// Same open-page value used by the system prompt above; exposed to the
// model via getCurrentPage so page identity survives prompt mangling.
body.openPage,
);
// Merge in admin-configured external MCP tools (web search, etc.; §6.8).

View File

@@ -50,6 +50,11 @@ export class AiChatToolsService {
// agent write (REST + collab) records { actor:'agent', aiChatId } off a
// SIGNED claim — non-spoofable, never a client body field (§6.5/§6.6).
aiChatId: string,
// The page the user currently has open (from the request context), exposed
// to the model via getCurrentPage. Optional and last so existing callers
// keep compiling. Kept proxy-robust: the model can CALL for the current
// page instead of relying on it surviving in the system prompt text.
openedPage?: { id?: string; title?: string } | null,
): Promise<Record<string, Tool>> {
const apiUrl =
process.env.MCP_DOCMOST_API_URL ||
@@ -210,6 +215,23 @@ export class AiChatToolsService {
},
}),
getCurrentPage: tool({
description:
'Return the page the user is currently viewing — i.e. what "this page", ' +
'"the current page", or "here" refers to. Returns the page id and title, ' +
'or null if the user is not currently on a page. Call this first whenever ' +
'the user refers to the current page without giving an explicit id.',
inputSchema: z.object({}),
execute: async () => {
if (!openedPage?.id) {
return { page: null };
}
return {
page: { id: openedPage.id, title: openedPage.title ?? '' },
};
},
}),
getPage: tool({
description:
'Fetch a single page as Markdown by its page id. Returns the page ' +