Skip to content

feat(daemon): sendMessage actions with a tool guide, background tasks, and replies by message id - #3047

Merged
spacedragon merged 15 commits into
mainfrom
dev/yulong/sendmessage-tool-description-limit
Oct 15, 2026
Merged

spacedragon merged 15 commits into
mainfrom
dev/yulong/sendmessage-tool-description-limit

Conversation

@spacedragon

Copy link
Copy Markdown
Contributor

Summary

sendMessage had a 4363-character description and a 9484-character schema built on a top-level oneOf. OpenAI-hosted function tools are reported to cap a description at 1024 characters. This PR implements docs/designs/send-message-guide.md:

  • Required action (§3–§7). sendMessage takes a required action (callAgent, startTask, messageTask, replyToSession, postMessage) and a flat schema. Detailed usage moves into a new getToolGuide tool. A call without action is rejected before any effect, and the error points to the guide. The description is now 971 characters and the schema 1956.
  • Tasks replace background sub-sessions (§2.1, §8).
    • startTask {message} starts a task of the current session, and messageTask {taskId, message} sends more work to it across work rounds.
    • Each round reports automatically.
    • stopTask {taskId} replaces stopSubsession: the owning session may stop its own task on any of its turns, and every other caller gets the unknown-id answer.
    • A self callAgent without channel, or a messageTask without taskId, is rejected. The error contains the exact startTask call and the startTask guide.
  • Existing peer sessions and replies by message id (§9).
    • callAgent with sessionId calls an existing peer session, any number of times.
    • Every message one session sends another carries an id and trusted metadata: source session, source agent, return route, kind, and whether a reply is requested.
    • replyToSession {messageId, message} is authorized and routed by that message's record alone. A message may be answered more than once, and reply obligations are tracked per message.
    • A session's origin now only records who created it.
    • Store schema v52 replaces the earlier reply-source tables and migrates each existing origin into one message record.
    • The relay forwards the new frame fields only to daemons advertising session-message-v1 and strips them for an older daemon, so calls between versions keep working.
  • Glossary. GLOSSARY.md gains the terms Conversation, Session, Task, Owning session, Session message, and Reply, and reworded entries for Origin session and Reply obligation.

Breaking change for reviewers and operators

Calls without action are rejected; there is no transition period (§4). Agent descriptions, knowledge articles, and skills that show the old sendMessage forms must be updated by hand when this is deployed. The same applies to replyToSession {sessionId}, messageSubsession, and stopSubsession.

Verification

  • typecheck passes for daemon, protocol, relay and control-plane.

  • eslint reports 0 errors on the changed files, and prettier passes.

  • Daemon: the 40 test files this branch touches, plus the §7.6 baseline suites, all pass: 1358 passed, 13 skipped. That includes a Postgres run of the store migration test.

  • Relay agent-msg-router tests pass (41), and so do protocol tests (747).

  • Live runtime matrix (packages/daemon/test/acp-matrix/send-message-probe.ts, 7 comparable scenarios × 2 reps), compared with the pre-change baseline:

    Runtime Baseline (pass/scored) After
    claude-acp 14/14 14/14
    cursor 14/14 14/14
    devin 13/14 14/14
    github-copilot-cli 13/14 13/14 ¹
    opencode 13/14 13/14 ¹
    codex-acp 6/6 (8 unavailable) 14/14
    omp 14/14 not measured ²
    qwen-code 5/14 not measured ²

    ¹ Both misses passed 2/2 on recheck. The copilot miss was a host "Too many open files" error; the opencode miss was a plain channel post instead of callAgent.
    ² Every turn from these two runtimes failed at the model provider with "402 Insufficient Balance", in about one second and with no tool call. They need a rerun once the provider account is topped up.

    No scenario that passed 2/2 in the baseline now fails 2/2.

Not done: product conventions, assistant-mode §5.6–§5.8, and the console's Sub-sessions tab still say "sub-session". Renaming them is a follow-up (§11 questions 10–11).

🤖 Generated with Claude Code · Opus 5.5

spacedragon and others added 13 commits October 15, 2026 04:16
Includes the send-message-guide design doc (docs/designs/send-message-guide.md) and its glossary and index entries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…emand tool guide

sendMessage now takes a required `action` (callAgent, messageSubsession,
replyToSession, postMessage) over one flat input schema with no top-level
union, and its description fits within 1024 characters. Detailed rules and
runnable examples move to a new getToolGuide tool, offered with sendMessage
to every agent, including one without IM integrations.

The action and its argument combination are validated at the executeTool
boundary before channel-name resolution or any other lookup, and again in
the handler. Calls without an action, with an unknown action, or with
conflicting fields fail through the argument-error path and point to the
guide. messageSubsession creates a background sub-session from trusted
caller identity; a supplied sessionId is rejected until continuation exists.
A postless callAgent to yourself is rejected with the messageSubsession
guide pointer. isPlatformPost now holds only for postMessage. Delivery
paths and receipts are unchanged.

Standing context, the channel intro, listAgents, the root-post notice, and
the inferred-reply text use the action forms. The runtime-matrix probe reads
the daemon's own action errors instead of fabricated ones.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
messageSubsession with a sessionId now sends into an existing direct child,
checked against the persistent sub-session index and the trusted caller
session, with no fallback creation. Each parent input joins the child's
open work round or opens the next one under the per-agent cap; retained
idle children hold no slot. A child's replyToSession is delivered without
ending its round, and the daemon reports every round's end once, with the
round's final output. Rounds are numbered on the index row (store schema
v51), stamped on each input's call metadata, and a stale outcome of an
older round changes nothing. Submissions are claimed once per caller
session, so a replayed call gets its first receipt.

The descriptor, getToolGuide, standing guidance and the runtime-matrix
probe now advertise and exercise continuation; send-message-guide.md
section 8.8 records the decisions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…origins

callAgent now takes an optional sessionId naming an existing session of
the agent in toAgent: the input is delivered into exactly that session,
postless and on its own reply route, or nowhere. The owning daemon
resolves the id by session key or outward id, refuses sub-sessions, and
admits the call only when the people on the calling turn may read the
target's conversation under the existing cross-conversation read rule,
for local and remote calls alike. Remote calls travel as a new
session-call delivery kind with the target session and the calling
turn's people, forwarded only to daemons that advertise
agent-session-call-v1.

Every admitted call or reply records its source on the session it
reached, so the session can answer each origin separately, in later
turns and after a restart, without broadcast. Reply requests are tracked
per admitted call: a reply answers one request, a newer caller no longer
cancels an earlier one's inferred answer or failure report, and both go
to the call's own source. Turn context names the source of each input
and the sessions it may reply to. The descriptor, getToolGuide, standing
guidance and the runtime-matrix probe advertise and exercise the form;
send-message-guide.md section 9 records the admission rule and the
decisions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Move the scripted evaluation callers and the add-on prompts to the
  explicit action forms, so the evaluation gates pass again.
- Keep a delegated sub-session's round open while it waits for a peer
  reply it asked for, and infer from the session's own coordinate.
- Judge session calls between peers on a shared a2a coordinate by who
  is asking, so a turn no person is behind is refused.
- Report an admitted session call's failed turn to its own source.
- Suppress inference after a failed explicit reply to the same source.
- Record no reply source for the agent's own sub-session.
- Keep a reopened round that another input joined when its opening
  input is refused.
- Drop the continuation hint from a report that lands in a later
  session, and from the stop note of a task or patrol.
- Scope the "reply any time" guidance to delegated sub-sessions.
- Point every recognized action's argument error at its guide, without
  a doubled tool prefix or period.
- Bring the design record and product conventions up to date.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…description

The live runtime matrix showed devin and opencode starting background work
in the current turn instead of delegating it. The description now tells the
model to call messageSubsession first, say it started, and end its turn, and
the postMessage guide points channel work at callAgent with the caller's id.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In the live runtime matrix opencode recognized a long request as
messageSubsession work, inspected the workspace first, and then declined
instead of delegating. Standing context and the messageSubsession guide now
say to start the sub-session before exploring, since it inherits this
conversation's context.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ts no work

Devin still answered a request to work visibly in another channel with two
bare postMessage root posts. The description now states that postMessage
starts no work, beside the existing callAgent self-call with channel.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…plies

Replace background sub-sessions with tasks started by startTask, messaged by
messageTask and stopped by their owning session with stopTask. A self
callAgent needs a channel, and without one is refused with the exact
startTask call. Every session message carries an id and trusted metadata;
replyToSession answers a message by its id, and a session's origin records
only who created it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replace the messageSubsession action with startTask {message} and
messageTask {taskId, message}. Task receipts name their handle taskId,
viewSessionStatus takes a taskId, and task refusals use task_not_found,
task_reporting, task_ended, task_nesting and task_limit. A self callAgent
without channel, and a messageTask without a valid taskId, return the
exact startTask call with the startTask guide inline, never the body.

stopSubsession becomes stopTask: the owning session may stop its own
task on any of its turns, and every other caller gets the unknown-id
answer. Descriptor, guide, standing context, task inputs, round reports
([task finished] / [task ended]), the stop note, the running-tasks
reminder and the live probe use task vocabulary. Replies keep their
sessionId addressing until message ids land.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every session message (a call, including into an existing session or a
channel, a task input, a reply, and a task's round report) now carries an
opaque id the sending daemon derives from its submission, so a replay
keeps it. The receiving daemon records the message at admission with its
source, return route, kind, hop count and reply obligation; the sending
daemon records what it sent. The new relay frame fields travel only to a
daemon that advertises session-message-v1, so an older peer fails closed.

replyToSession takes a required messageId and is decided by that record
alone: the recipient must be the calling session, the reply goes only to
the message's source by its recorded route, and a remote source accepts
it only into the session that sent the message. A reply into an owned
task is admitted like messageTask. Obligations, inference and failure
reports key on the message, and folded turns infer nothing.

The origin now records creation only. Each input shows its message id,
its source and the exact reply call, and standing context lists the
messages the session may still answer instead of a parent session. Store
schema v52 replaces the reply-source tables and migrates each existing
origin into one synthesized call. The live probe renders its reply
scenario through the daemon's own message header.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Route only the owning session's reply into its task through messageTask
  admission; deliver any other session's reply like a peer's, and refuse a
  reply that would wake a patrol or approved proposal again.
- Return the first receipt for a replayed startTask or new-session callAgent
  submission instead of a message_id_in_use refusal or a second turn.
- Probe for the earlier reply-source table before the v52 migration reads it,
  so a Postgres pool store no longer aborts its migration transaction.
- Relay: strip message ids for a target without session-message-v1 and frame
  a call the pre-id way, instead of refusing every call to an older daemon.
- Resolve task handles by outward id only, never an ACP runtime id.
- Rewrite a kept report's old messageSubsession hint to messageTask.
- Probe header carries the sender label the daemon renders; the direct-call
  reminder covers the sessionless From label.
- Docs: record the taskId/receipt, replay and reply-routing decisions; update
  daemon-detailed-design and session-concept for stopTask and Created by session.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@agentconnect-md-test agentconnect-md-test Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Architecture review of fa6eb6f (merge of base b94e1e1 verified locally).

Verdict: request changes, one blocking item

The runtime shape is aligned with the architecture and with send-message-guide.md as written. What blocks is documentation drift that this PR itself introduces into the file CLAUDE.md names as the authority for product behaviour.

Blocking: docs/product-conventions.md describes a sendMessage contract the daemon at this head rejects

This PR edits the file to name forms that never shipped or that the same PR removes:

  • Lines 699–700 and 1262: "a postless messageSubsession call" / "sendMessage with action messageSubsession". messageSubsession is not a sendMessage action at this head (SEND_MESSAGE_ACTIONS is callAgent, startTask, messageTask, replyToSession, postMessage) and never existed on main (git grep messageSubsession on the base is empty). The shipped form is startTask {message}.
  • Line 704: sendMessage({"action":"replyToSession","sessionId":"<parent>", …}). At this head replyToSession with sessionId is an argument error by design (§3.5, §9.8); the contract is messageId.
  • Lines 1274–1277: "A later messageSubsession with the childSessionId it returned". The shipped form is messageTask {taskId}.
  • Line 130 and lines 1300–1310 (unchanged text, changed behaviour): stopping a sub-session "follows the !stop sender rule", "only on a turn a person there started, and only for someone !stop accepts there … A sub-session, patrol, approved task, hook or scheduled run cannot stop one." This PR removes the person-started-turn and !stop sender gates and makes the owning session the sole authority on any of its turns, hook and scheduled runs included (§8.7, §12 item 4, assistant-subsessions.ts). The convention now states a gate that no longer exists.

The design's §11 question 10 defers vocabulary (sub-session versus task) to a follow-up, and that is a reasonable scope cut. These are not vocabulary: they are call shapes and an authority rule, and the PR's own body says agent descriptions, knowledge articles and skills must be hand-migrated from the docs. The authoritative doc has to carry the shipped contract in the same change that makes the break. Please update those passages (the sub-session wording can stay for now if you prefer; the call forms and the stop rule must be the shipped ones).

Same category, not blocking on its own but should be fixed or bannered here the way session-concept.md was:

  • docs/designs/assistant-mode.md §5.6 line 676 "a postless self-call opens one" (now refused) and §5.8 (stop gates removed).
  • docs/designs/send-message-routing-rework.md line 519 "sendMessage({"sessionId":"<parent>"…}) remains authorized". The new design names this doc as its baseline, so a reader following the pointer lands on a contradicted rule.

Architecture confirmations (no action)

  • Data-plane invariant holds. session_message / session_message_sent live in the daemon store (SQLite or the shared PostgreSQL dialect); bodies stay in transcripts; there is no control-plane change. The relay forwards messageId/messageKind/inReplyTo/targetSessionId/callerAskers as metadata and persists nothing. callerAskers is asserted by the authenticated source daemon exactly like lineage, so no new trust root.
  • Versioned seam. New frame fields are optional and gated on agent-session-call-v1 / session-message-v1; session-call fails closed on an older relay or daemon, and the pre-id compatibility path is explicit and bounded.
  • Reply authority is the record alone. replyToSession resolves the received-message row, checks recipient key and agent, and refuses with one wording; an owner's reply into its own task is routed through messageTask admission so the ownership and round accounting cannot be bypassed. Remote replies land only in the session the sent-message record names. The origin is written once and read for nothing but status.
  • Peer-session admission reuses the existing read rule (judgeSessionParticipation over askerMayRead/checkPlaceRead) rather than inventing a second authorization vocabulary, and a turn no person is behind is refused. Good.
  • stopTask authority is the persistent index row (parent key and outward id), not turn state. The trade recorded in §6/§11 Q9 (a peer's reply text can now lead the owner to stop a task) is bounded because a stop undoes nothing and messageTask reopens; acceptable, but it is a product-level change and belongs in product-conventions (covered above).

Non-blocking notes

  1. Compatibility code for the branch's own intermediate contract. coordinator.ts rewrites persisted reports naming messageSubsession, and subsession-approval.ts strips LEGACY_TASK_START_WRAPPER. Neither text was ever produced by a main build. Unless an intermediate build was deployed somewhere you need to carry, remove these so "legacy" in this codebase keeps meaning "a released daemon". If it was deployed, say so in a comment with the removal condition.
  2. Relay now shapes message text. agent-msg-router.ts prefixes From <label>: for a daemon without session-message-v1. It is a defensible compatibility shim, but it moves sender framing from the daemons into the relay. Please record its removal condition (daemon floor at or above session-message-v1) so the relay returns to pure forwarding.
  3. session-concept.md banner approach. The file now carries two "the passages below describe the earlier design" layers. Fine for this PR; schedule a rewrite of §2.3/§5 so the session definition does not depend on the reader applying overlays.
  4. Design doc and implementation in one PR. send-message-guide.md is new here with status "implemented". Acceptable given the parity audit in §7 and the implementation records in §7.8/§8.9/§9.8, which are thorough. Future revisions of this line should land the design first so review can hold the code to a reviewed design rather than the reverse.

Re-request review after the product-conventions fix; the rest can follow in the tracked §11 Q10/Q11 follow-up.

sent by architect (Claude Agent · claude-fable-5-1) · open in session

Comment thread docs/product-conventions.md Outdated
delivery, so the new child runs exactly once; the platform's echo is only the second
observation of that delivery. A postless self-call opens a background sub-session instead, one
level deep and under a cap (see "Sub-sessions").
observation of that delivery. A postless `messageSubsession` call opens a background sub-session

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

messageSubsession is not an action at this head (and never was on main). The shipped form for postless self-work is startTask {message}; a self callAgent without channel is refused with that call. This file is the product authority CLAUDE.md points to, so it has to name the shipped contract.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 92fa64d: the postless self-call sentence now says a self-call without a channel is refused with the startTask call, and startTask opens the background sub-session.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed in d19ccaeb: the text now describes the refused self-call and the startTask alternative correctly. The approval stands.

sent by review-bot (Codex · gpt-6-astra) · open in session

Comment thread docs/product-conventions.md Outdated
## A parent-session reply is injected, and the parent answers normally

`sendMessage({"sessionId":"<parent>", …})` injects an answer into the session that woke
`sendMessage({"action":"replyToSession","sessionId":"<parent>", …})` injects an answer into the session that woke

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this head replyToSession with sessionId is an argument error by design (§3.5, §9.8). The contract is {"action":"replyToSession","messageId":"<id of the message being answered>", …}.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 92fa64d: the section now shows replyToSession with messageId and states that the reply is authorized and routed by the answered message alone.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed in d19ccaeb: the section uses replyToSession with messageId and correctly ties authorization and routing to the answered message. This finding is resolved.

sent by review-bot (Codex · gpt-6-astra) · open in session

Comment thread docs/product-conventions.md Outdated

Every agent, in assistant mode or not, hands long work, such as "fix this bug and open a PR", to a
background sub-session of its own: `sendMessage` with its own agent id and no `channel`.
background sub-session of its own: `sendMessage` with action `messageSubsession` and no `sessionId`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: the shipped action is startTask, taking only action and message.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 92fa64d: startTask with only a message.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed in d19ccaeb: the section now specifies action: "startTask" with only message. This finding is resolved.

sent by review-bot (Codex · gpt-6-astra) · open in session

Comment thread docs/product-conventions.md Outdated
an agent in assistant mode; one more is refused, and the agent is told so. Every agent is told
about this form, and a self-call in any other form than this or the channel-root one is refused.
The sub-session can message the conversation that opened it at any time, and a message does not end its
work; the daemon reports the end of each round of work. A later `messageSubsession` with the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shipped continuation is messageTask {taskId}; the receipt names the handle taskId, not childSessionId (§8.6, §12 item 2). Also worth stating here, since it is a behaviour change this PR makes: the owning session may stop its task with stopTask on any of its turns, and the !stop sender / person-started-turn gates described at lines 130 and 1300–1310 no longer apply to that tool (§8.7).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 92fa64d: continuation is messageTask with the taskId from startTask, and the stop paragraph (and the line-130 exception list) now state the shipped stopTask rule: the owning conversation on any of its turns, no !stop sender gate, every other caller refused in the unknown-id words.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed in d19ccaeb: continuation uses messageTask {taskId}, and stopTask is authorized by owning-session identity on any turn, without the !stop sender gate. Both documentation points are resolved.

sent by review-bot (Codex · gpt-6-astra) · open in session

@agentconnect-md-test agentconnect-md-test Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed fa6eb6f. Two introduced correctness issues need fixes:

  1. [P2] Settle the peer wait when a remote reply is admitted — packages/daemon/src/collab/coordinator.ts:664–668

    When a task calls a peer on another daemon with needsReply: true, its originating daemon stores a remote link with replyState: 'awaiting'. The returning handleRelayAgentMsg path admits the reply but never updates that link; markChildParentReply runs on the replier's daemon. Consequently awaitsPeerReply remains true after the task processes the reply and finishes. The new round settlement guard suppresses its completion report, retains its concurrency slot, and eventually the idle sweep reports a failure. Reconcile the originating task's outstanding call when its reply is admitted, using the message correlation.

  2. [P2] Forward the submission ID for existing-session calls — packages/daemon/src/mcp/ops/send-message-actions.ts:235–237

    callPeerSession omits submissionId: deps.callId, although the other send paths forward it. Replaying the same MCP request therefore reaches callExistingSession without a submission ID, so messageIdFor mints a fresh ID each time. The receiver admits another message and can repeat the requested work instead of returning the original receipt. Forward the stable call ID here as well. The coordinator-level replay test supplies submissionId directly and does not exercise this missing tool-layer wiring.

Validation: isolated execution of the changed methods with mocked dependencies reproduced both issues: an admitted remote reply left the wait true, and two existing-session tool calls with the same callId both omitted the submission ID. git diff --check passed. Full daemon tests were not run because dependency setup remained incomplete. No tracked files were changed.

sent by review-bot (Codex · gpt-6-astra) · open in session

spacedragon and others added 2 commits October 15, 2026 04:34
…product conventions

Product conventions now name startTask, messageTask {taskId} and
replyToSession {messageId}, and the owning session's stopTask authority on
any of its turns. Assistant mode §5.6/§5.8 and the routing rework's
parent-reply section point to send-message-guide.md for the new rules.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-session call replays idempotent

- A reply admitted from another daemon now ends the originating session's
  wait on the message it answers (by inReplyTo and the sent record, or the
  remote call for a pre-id peer), so a task's round reports and frees its
  slot. A local reply likewise ends only the wait on the message it answers.
- callAgent into an existing session forwards the MCP call id as its
  submission, so a replayed request is one message with its first receipt,
  and a replay does not reopen a wait already answered.
- Drop compatibility for this branch's unreleased intermediate contract: the
  messageSubsession report rewrite and the intermediate task-start wrapper.
  The wrapper a released daemon wrote is still stripped from task names.
- Note when the relay's pre-id fallback can be removed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@spacedragon

Copy link
Copy Markdown
Contributor Author

Addressed in 92fa64d and d19ccae.

Architecture review

  • Blocking, product conventions: now names the shipped forms: startTask {message}, messageTask {taskId}, replyToSession {messageId}, and the refused self-call without a channel. The stop rule is now the owning session's stopTask, allowed on any of its turns without the !stop sender gate; the exception list near the top is updated to match. The sub-session vocabulary stays for the §11 Q10 follow-up.
  • Other affected docs: assistant-mode.md §5.6 and §5.8, and §7 of send-message-routing-rework.md, now open with an update note that points to send-message-guide.md for the new contract.
  • Note 1, branch-only compatibility code: removed the rewrite of messageSubsession hints in persisted reports, and the branch-only task-start wrapper. The wrapper the review named, LEGACY_TASK_START_WRAPPER, is the one released main produced (in v2.4.0), so its stripping stays.
  • Note 2, relay framing: the shim now carries its removal condition. Once the supported daemon floor advertises session-message-v1, the fallback and the From <label>: framing are removed and the relay goes back to forwarding only.
  • Notes 3–4: recorded for the follow-up.

review-bot

  1. A remote reply now ends the wait. When a peer's reply is admitted through the relay, the waiting link that matches the answered messageId (from the sent-message record) is marked answered. A peer without message ids is matched by lineageReplyTo and its agent instead. Locally, a reply ends only the wait for the message it answers. Regression tests: a task calls a remote peer and its round reports finished once the reply arrives, with and without message ids.
  2. callPeerSession now forwards submissionId. A tool-layer test sends the same callId twice and gets one recorded message, the first receipt, and one prompt to the target.

@agentconnect-md-test agentconnect-md-test Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed d19ccae. Both findings from my previous review are resolved:

  • Admitted remote replies now settle the matching peer wait, allowing the task round to complete. Message correlation prevents an older reply from settling a newer wait.
  • Existing-session calls forward the stable submission ID, and replaying an answered call preserves its settled state.

The product-convention corrections now describe the shipped actions and stop authority. No remaining blocking findings.

Validation: focused execution of the changed methods with mocked dependencies passed for submission-ID forwarding, remote-reply settlement, message correlation, settled-call replay, and legacy replies. Reviewed the added regression tests; git diff --check passed. Full daemon tests were not run because this checkout still lacks a complete dependency installation.

sent by review-bot (Codex · gpt-6-astra) · open in session

@agentconnect-md-test agentconnect-md-test Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Architecture review of d19ccae (exact head checked out locally; two commits since fa6eb6f: 92fa64d docs, d19ccae daemon fixes).

Verdict: approve

The blocking finding from the previous round is resolved, and the follow-up notes are either addressed or recorded.

Blocking item: resolved

docs/product-conventions.md now states the shipped contract: a self-call without a channel is refused with the startTask call; background self-work is startTask with only a message; continuation is messageTask with the taskId startTask returned; the parent-reply section shows replyToSession {messageId} and says the reply is authorized and routed by the answered message alone. The stop rule is the owning conversation's stopTask on any of its turns, with the !stop sender gate explicitly not applying, and the exception list near the top of the file was updated to match. The sub-session vocabulary stays for the §11 Q10 follow-up, which is the scope cut I accepted.

Follow-up notes: verified

  • assistant-mode.md §5.6 and §5.8, and send-message-routing-rework.md §7, now open with an update note pointing at send-message-guide.md for the new contract, the same pattern session-concept.md uses. Acceptable for this PR; the rewrite of those sections stays a follow-up.
  • Branch-only compatibility code: the messageSubsession report rewrite and the branch-only TASK_START_WRAPPER are gone. LEGACY_TASK_START_WRAPPER is kept, and I confirmed it is the wrapper the base (main) produces, so stripping it from approval-card labels is genuine released-daemon compatibility.
  • The relay framing shim now carries its removal condition in the comment (daemon floor advertising session-message-v1).

review-bot's findings

Both fixes are consistent with the architecture rather than patched around it: the in-memory peer-wait link now carries the messageId it waits on, a remote reply admitted through the relay ends exactly that wait (correlated through the sender's own sent-message record, with a lineage fallback for pre-id peers), and callPeerSession forwards the MCP callId so a replayed existing-session call keeps one message id and one receipt. That keeps "replies are correlated by message, never by session state" true across daemons. I read the fixes and the added tests; I did not run the daemon suites in this sandbox.

Still open for later PRs (non-blocking, recorded)

  • §11 Q10/Q11: product and assistant-mode vocabulary (sub-session vs task; approved proposals called tasks).
  • §11 Q9: a peer's reply text can lead an owning session to stop a task; revisit if this is abused.
  • Rewrite session-concept.md §2.3/§5 and the bannered sections instead of stacking update notes.
  • Other oversized tool descriptions (§11 Q6) may want to join getToolGuide.

sent by architect (Claude Agent · claude-fable-5-1) · open in session

@agentconnect-md-test

Copy link
Copy Markdown
Contributor

Verified both fixes at d19ccaeb and already submitted APPROVE. Focused checks passed for remote-reply settlement and submission-ID forwarding; no remaining blockers from my review.

sent by review-bot (Codex · gpt-6-astra) · open in session

@agentconnect-md-test

Copy link
Copy Markdown
Contributor

Verified at d19ccae before approving: all four product-conventions passages now state the shipped forms, the stop paragraph and the line-130 exception list carry the owner-only stopTask rule, and the update notes on assistant-mode §5.6/§5.8 and routing-rework §7 point at the new design. On note 1, I confirmed on the base that LEGACY_TASK_START_WRAPPER is the text released main produces, so keeping that strip is correct; the branch-only rewrite and wrapper are gone. The relay shim's removal condition is recorded.

The two review-bot fixes read as consistent with the design's rule that replies are correlated by message rather than session state, including across daemons. I read the fixes and the added regression tests but did not run the daemon suites here.

My APPROVE at d19ccae stands. Remaining items are the recorded follow-ups: §11 Q10/Q11 vocabulary, Q9 owner stop on peer-reply turns, and rewriting the bannered sections of session-concept.md, assistant-mode and routing-rework rather than stacking more update notes.

sent by architect (Claude Agent · claude-fable-5-1) · open in session

@spacedragon
spacedragon merged commit 167bc85 into main Oct 15, 2026
14 checks passed
@spacedragon
spacedragon deleted the dev/yulong/sendmessage-tool-description-limit branch October 15, 2026 05:00
zfy0701 added a commit that referenced this pull request Oct 15, 2026
#3047 rewrote sendMessage as a flat schema with a required action, so this
branch's sendMessage rewrite and its test edits give way to main's. What
remains is the length guard, now pointed at stopTask, and the design-doc
note on both runtime limits. Every tool on main fits both limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant