Conversation
The template could write task comments (`b24_task_comment_add`) but never read them, so an agent could not answer "who said what on this task". `b24_task_comment_list` wraps `task.commentitem.getlist` (v2) and returns every comment in full — bodies are never truncated — with `authorId` + `authorName` per comment (Bitrix24 ships both, so attribution costs no extra `user.get`), an `authors` roll-up over the whole thread, and a `total` / `matched` / `returned` triple so the agent can tell "no comments" from "the filter matched nothing" and detect further pages. Wire constraints, verified against a live portal (2026-09-02): - The endpoint accepts `TASKID` only. Passing `ORDER` (or `FILTER`) fails the whole call with `ERROR_CORE` / `TASKS_ERROR_EXCEPTION_#8 … ACTION_FAILED_TO_BE_PROCESSED`, so ordering, the `authorId` filter and `limit` / `offset` paging are applied locally over the full thread. Threads are tens of comments, so the single round-trip stays cheap. - `tasks.task.chat.message.list` was evaluated as the modern replacement and rejected: it returns an empty body on webhook auth. - `POST_MESSAGE_HTML` is null for UI-written comments; `POST_MESSAGE` (BBCode) is the canonical body. Service messages are returned as-is. `docs/MANUAL-TEST-PHRASES.md` section 6 specified an `includeSystem: false` default keyed on `messageType: "SERVICE"` / `AUTHOR_ID: 0`, but neither field exists — Bitrix24's lifecycle notes are ordinary comments authored by whoever triggered the change, indistinguishable at the REST layer from something that person typed. The tool surfaces them verbatim and the description tells the agent to judge by the text, rather than mislabelling a real comment with a heuristic. Section 6 of that doc is rewritten to record what the API actually does. Adds `server/utils/task-comments.ts` (`toTaskCommentShort`, sibling to the elapsed-time / checklist / result parsers), `BitrixTaskCommentRaw`, the `mcp-stdio/tools.ts` registry entry, 14 unit tests, and 3 eval cases (read-vs-add and read-vs-`b24_task_result_list` disambiguation).
|
I only found #119 after opening this, sorry — it covers the same ground, so: Refs #119. This PR is the read half of it. On the two things that issue leaves open:
The other half is more useful, I think: One quirk worth knowing before you review: the endpoint takes I deliberately left the Last thing, and this is the one place I went against what the docs asked for. |
…forum
My first cut of `b24_task_comment_list` was wrong in the way that matters
most: it answered "no comments" with total confidence for any task
created recently.
Bitrix24 keeps task comments in two places and the task's age decides
which one:
- legacy tasks carry a `forumTopicId`; comments are forum posts, read
with `task.commentitem.getlist`;
- tasks created after the portal moved to the chat-based task card
carry a `chatId` and no forum topic; comments are chat messages, read
with `im.dialog.messages.get` (DIALOG_ID: "chat<id>").
For a chat-era task the forum method returns `[]` — no error, no hint.
Verified live (2026-09-03): a task created that day reported
`forumTopicId: null` / `chatId: 6479`, two comments posted through
`b24_task_comment_add` came back only from the chat, and the tool as
first written reported an empty thread. This is exactly the failure the
issue description warned about with "не работает в новой карточке задач";
my earlier claim that the forum method works on modern task cards was
drawn from 2025 tasks and does not generalise.
A task straddling the migration holds comments in BOTH stores — the 2025
task's own chat contained two system notices, one of them telling the
reader that earlier comments stay in the forum — so the tool now reads
both, merges them chronologically, and tags each comment with
`source: "forum" | "chat"`.
Two things fall out of the chat store:
- `author_id: 0` marks a Bitrix24-generated entry, so the
`includeSystem: false` default that `MANUAL-TEST-PHRASES.md` §6
always specified is finally implementable (hidden entries counted in
`systemHidden`). It reaches chat entries only; the forum API has no
marker, so a forum row reports `isSystem: false` meaning "unknown",
and the description says so.
- the response ships a `users` array, so chat-side attribution needs no
extra `user.get`, matching what AUTHOR_NAME gives us on the forum
side.
Chat paging walks backwards through `LAST_ID` at 200 per call, up to five
pages, and reports `chatTruncated: true` if the thread is longer.
Response also carries per-store counts. Bodies are still returned in
full; `limit` / `offset` drop whole comments.
Live re-check: the QA task returns its 2 human comments with 17 system
entries hidden and counted; the 2025 task returns 4 forum comments plus
its 2 chat system notices; §6 of the manual-test doc is rewritten around
the two-store reality.
|
I have to correct myself on the main claim I made above, and it's the one that matters. I said What actually happens: Bitrix24 keeps task comments in two places, and the task's age decides which. Legacy tasks carry a So I've reworked the PR. It now reads both stores and merges them, because a task from around the migration holds comments in both — the 2025 task I tested has four forum comments and its chat contains exactly two system notices, one of which literally tells the reader that earlier comments stay in the forum. Each comment carries The read path for the chat is Two nice consequences of the chat store:
The chat response also ships a Chat paging walks backwards through Sorry for the noise of a wrong claim followed by a correction — better here than in merged code. |
Summary
Adds
b24_task_comment_list— the read side of task comments. The template could write them (b24_task_comment_add) but never read them, so an agent could not answer "who said what on this task". Wrapstask.commentitem.getlist(v2), returns every comment in full withauthorId+authorName, anauthorsroll-up, andtotal/matched/returnedcounters.Type of change
Linked issue
Checklist
pnpm lintpassespnpm typecheckpassespnpm testpassesdocs/andskills/(README tool table +docs/MANUAL-TEST-PHRASES.md§6; the tool-authoring conventions are unchanged, sodocs/ADDING-TOOLS.md/ the agent skill need no edit — only the delete-tool registry lives there and this tool is read-only)Screenshots / logs
Verified against a live portal (webhook auth) before opening this PR — a task with a mixed thread:
{"taskId":29,"total":4,"matched":4,"returned":4,"offset":0, "authors":[{"id":9,"name":"…","comments":3},{"id":11,"name":"…","comments":1}], "comments":[{"id":71,"taskId":29,"authorId":9,"authorName":"…","authorEmail":null, "postDate":"2025-07-31T13:39:26+03:00","text":"[USER=11]…[/USER], вы назначены соисполнителем.","textHtml":null}, …]}Author names / bodies elided here; the fixtures in the test suite are synthetic.
Notes for reviewers
Three findings from the live portal that shaped the design — each is the reason for a piece of the implementation, so they're worth a look before reviewing the code:
The endpoint accepts
TASKIDonly. AddingORDER(and likewiseFILTER) fails the whole call withERROR_CORE/TASKS_ERROR_EXCEPTION_#8 … ACTION_FAILED_TO_BE_PROCESSED— not a silently-ignored param. Ordering, theauthorIdfilter andlimit/offsetpaging are therefore applied locally over the full thread. One round-trip; threads are tens of comments. The tool header comments say so explicitly to stop a future "optimisation" from pushing params onto the wire.tasks.task.chat.message.listis not a usable replacement today — it returns an empty body on webhook auth, so the classic (deprecated) method stays. Worth knowing for feat(tools): add task CRUD — create / list / update / add-comment #4 on the roadmap indocs/MANUAL-TEST-PHRASES.md, which plans the same migration forb24_task_comment_add.Service messages cannot be filtered the way §6 of
MANUAL-TEST-PHRASES.mdassumed. That doc specified anincludeSystem: falsedefault keyed onmessageType: "SERVICE"/AUTHOR_ID: 0. Neither field exists on this endpoint: Bitrix24's lifecycle notes ("Задача завершена.", "Крайний срок изменен на: …", "[USER=N]…[/USER], вы назначены соисполнителем.") arrive as ordinary comments authored by the user who triggered the change, indistinguishable at the REST layer from something that person typed. I chose to return them verbatim and tell the agent in the description to judge by the text, rather than ship a text heuristic that would mislabel a genuine comment that happens to read like a template. §6 is rewritten to record this. If you'd rather have the heuristic behind an opt-in flag, say so and I'll add it — but I'd argue the false positives are worse than the noise, since the failure mode is quoting a template back to the operator as a human statement.Two smaller deliberate choices:
limit/offsetdrop whole comments; no comment is ever shortened. A cut-off comment silently changes what the operator is told.totalvsmatchedvsreturned— thread size / after the author filter / after paging. Lets the agent distinguish "no comments" from "filter matched nothing" and detect further pages, in the same spirit as thereturned-vs-limitidiom the v3 list tools use.Transport is v2 per the convention block in
sdk-helpers.ts(classictask.*method). Tests: 14 unit cases across the parser and the tool (ordering, author filter, paging, empty thread, legacy{ result: [...] }envelope, error wrap), plus 3 eval cases including read-vs-add and read-vs-b24_task_result_listdisambiguation.