Skip to content

feat(cli): close cross-agent loop — auto-handle agent.ask + ${COMMONLY_AGENT_TOKEN} substitution#237

Closed
samxu01 wants to merge 1 commit intomainfrom
feat/cli-cross-agent-loop-closure
Closed

feat(cli): close cross-agent loop — auto-handle agent.ask + ${COMMONLY_AGENT_TOKEN} substitution#237
samxu01 wants to merge 1 commit intomainfrom
feat/cli-cross-agent-loop-closure

Conversation

@samxu01
Copy link
Copy Markdown
Contributor

@samxu01 samxu01 commented Apr 25, 2026

Summary

Two follow-ups from the 2026-04-17 ADR-003 Phase 4 live validation report, both blocking a fully-automated cross-agent demo without manual curl or post-attach token-patching.

Track A — agent.ask event handling in performRun

  • New ASK_EVENT_TYPES + PASSIVE_ACK_EVENT_TYPES sets alongside the existing CHAT_EVENT_TYPES.
  • buildPromptForEvent renders agent.ask events into a structured prompt (fromAgent, requestId, question + instruction to call commonly_respond_to_ask). Chat events keep verbatim payload forwarding.
  • Asks suppress the pod-message post — responses go via MCP, not chat. Outcome on the ack record is responded for asks, posted for chat.
  • agent.ask.response events are passive-acked (v1): asker drops them. Resuming the original session with the answer is post-v1 work — needs requestId-keyed session state.
  • Spawn ctx now carries runtimeToken + instanceUrl so adapters can use them.

Track B — ${COMMONLY_*} substitution in claude adapter

  • writeMcpConfig walks every MCP server's env values, command args, and url, substituting:
    • ${COMMONLY_AGENT_TOKEN}ctx.runtimeToken
    • ${COMMONLY_API_URL}ctx.instanceUrl
    • ${COMMONLY_INSTANCE_URL}ctx.instanceUrl (alias)
  • One-pass literal substitution; works inside larger strings.
  • Unknown ${COMMONLY_*} placeholders are left intact so typos surface as runtime MCP errors, not silent empties.
  • Falsy ctx values are a no-op (placeholder preserved) so users can diagnose missing context without confusing empty-string errors.

Live demonstration

Two claude agents (asker, target) attached with a shared env file using ${COMMONLY_AGENT_TOKEN} placeholders. User triggered the asker via chat mention. Result, with no manual intervention:

[ASKER]  [chat.mention]      spawning claude
[TARGET] [agent.ask]         spawning claude
[TARGET] [agent.ask]         ask handled — 66 bytes of stdout (not posted)
[ASKER]  [chat.mention]      posted 168 bytes  (acknowledgment in pod)
[ASKER]  [agent.ask.response] passive ack — answer to <requestId> dropped (v1)

Target's actual response from the AgentAsk record:

"Commonly is the shared substrate that lets independent AI agents discover, message, and collaborate with each other across teams and tools — turning isolated assistants into a coordinated workforce."

Test plan

  • cd cli && npm test135/137 passing (4 new run-loop, 5 new claude env, 2 skipped Linux-only). Zero regressions.
  • Self-review against docs/REVIEW.md
  • Live validation on commonly-dev end-to-end with placeholder env (no hardcoded tokens) — full bidirectional ask/respond flow with zero manual curl

Known follow-ups (deliberately out of scope)

  • Resume the asker's original session with the answer when agent.ask.response arrives. Needs requestId-keyed session state. v1 ack-and-drops; asker's next turn picks up the answer from memory or a context call.

🤖 Generated with Claude Code

…Y_AGENT_TOKEN} substitution

Two follow-ups from the 2026-04-17 ADR-003 Phase 4 live validation,
both blocking a fully-automated cross-agent demo without manual curl
or post-attach token-patching.

Track A — agent.ask event handling in performRun
- New ASK_EVENT_TYPES + PASSIVE_ACK_EVENT_TYPES sets alongside the
  existing CHAT_EVENT_TYPES.
- buildPromptForEvent renders agent.ask events into a structured
  prompt (fromAgent, requestId, question + instruction to call
  commonly_respond_to_ask). Chat events keep the verbatim payload
  forwarding they had before.
- Asks suppress the pod-message post — responses go via MCP, not chat.
  Outcome on the ack record is 'responded' for asks, 'posted' for chat.
- agent.ask.response events are passive-acked (v1): the asker drops
  them. Resuming the original session with the answer is post-v1
  work — needs requestId-keyed session state.
- Spawn ctx now carries runtimeToken + instanceUrl so adapters can
  use them (Track B uses them to substitute placeholders).

Track B — ${COMMONLY_*} substitution in claude adapter
- writeMcpConfig walks every MCP server's env values, command args,
  and url, substituting:
    ${COMMONLY_AGENT_TOKEN}   → ctx.runtimeToken
    ${COMMONLY_API_URL}       → ctx.instanceUrl
    ${COMMONLY_INSTANCE_URL}  → ctx.instanceUrl (alias)
- One-pass literal substitution; works inside larger strings.
- Unknown ${COMMONLY_*} placeholders are left intact so typos surface
  as runtime MCP errors, not silent empties.
- Falsy ctx values are a no-op (placeholder preserved) so users can
  diagnose missing context without a confusing empty-string error.

Tests: 135/137 cli tests pass (4 new run-loop, 5 new claude env).
Closes the manual-curl gap from the cross-agent demo report.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@samxu01
Copy link
Copy Markdown
Contributor Author

samxu01 commented Apr 26, 2026

Closing as I'm splitting this — Track A doesn't pass the design bar after a re-read, Track B is good and will land separately.

Why Track A is being deferred (not merged): Auto-handling agent.ask events in the run loop makes the cross-agent flow fully invisible to humans — the target responds via MCP, no chat trace, no admin-readable surface. That undercuts Commonly's "shared social substrate" pitch by turning agent↔agent into a hidden RPC backchannel. The codex-in-gateway work (#236) and the agent-room 1:1 enforcement (#232/#235) point in the right direction: agents should DM each other in actual pods that admins can read.

What's next here:

  • Track B (${COMMONLY_AGENT_TOKEN} substitution) is a pure productization win regardless of how cross-agent talk gets re-architected — splitting it into a new PR.
  • Track A's logic + tests stay on feat/cli-cross-agent-loop-closure for reference; the branch isn't deleted. If we eventually decide commonly_ask_agent is worth keeping as an opt-in fast path (with proper inspectability), much of this code can be picked back up.
  • The default path for agent↔agent should be DM-pod messaging (existing chat infra). That's a separate design discussion + ADR addendum.

Branch + commit preserved on origin: feat/cli-cross-agent-loop-closure @ 0085333cb3.

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