feat(ai-chat): insertFootnote/insertImage/replaceImage для in-app агента (#410) #418

Merged
vvzvlad merged 2 commits from feat/410-agent-footnote-image into develop 2026-07-07 21:39:01 +03:00

2 Commits

Author SHA1 Message Date
agent_coder 984b95df9f test(mcp): #410 review — align HOST_CONTRACT_METHODS drift-guard (#410)
Promoting insertFootnote/insertImage/replaceImage into the in-app
DocmostClientLike interface requires their mirror in the drift-guard's
HOST_CONTRACT_METHODS list (the bidirectional deepEqual — 40 vs 43 — was
red, exactly what the guard exists to catch). Added the three names and
corrected the header comment: they were MCP-only but are now in-app-consumed
and tracked; only deleteComment/updateComment remain untracked MCP-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 21:35:08 +03:00
agent_coder 327737b701 feat(ai-chat): give the in-app agent insertFootnote/insertImage/replaceImage (#410)
The Researcher role wrote 40 literal `^[...]` and zero real footnotes: its
incremental write path (insertNode/editPageText) doesn't parse markdown, and
the footnote-capable tool was MCP-only. Promote three tools from inline
MCP-only to the shared registry so the in-app agent gets them too.

- tool-specs.ts: insertFootnote/insertImage/replaceImage added to
  SHARED_TOOL_SPECS (mcpName/schema/description moved VERBATIM from the inline
  registrations — MCP names + behaviour unchanged for external clients).
- index.ts: the 3 inline registerTool calls become registerShared; drop the
  "MCP-only by design" comments.
- ai-chat-tools.service.ts: register the 3 in-app via sharedTool ->
  client.insertFootnote/insertImage/replaceImage (imageUrl->url,
  attachmentId->oldAttachmentId mapping).
- tool-tiers.ts: insertFootnote -> core (else the original asymmetry recurs —
  footnote tool hidden while editPageText is core); images -> deferred.
- research/{en,ru}.yaml FOOTNOTES: `^[...]` parses ONLY on a whole-markdown
  write (create/update/import); for a pinpoint citation to existing text use
  insertFootnote; via editPageText/insertNode it stays literal.
- json-edit.ts guardrail: an edit_page_text `replace` containing a `^[...]`
  token is refused into failed[] with an insert_footnote hint, mirroring the
  existing formatting-marker refusal. (Slightly broader net than that mirror —
  a literal `^[a-z]` regex class in a replace is also refused; accepted
  defense-in-depth, has a no-false-positive test.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 21:18:01 +03:00