Skip to content

139: "Who can I ask?" — discover teammate agents by skill and engage them; publish cards for hosted personas#178

Draft
agent-relay-code[bot] wants to merge 9 commits into
mainfrom
factory/139-agentworkforce-factory-49b6fd48
Draft

139: "Who can I ask?" — discover teammate agents by skill and engage them; publish cards for hosted personas#178
agent-relay-code[bot] wants to merge 9 commits into
mainfrom
factory/139-agentworkforce-factory-49b6fd48

Conversation

@agent-relay-code

@agent-relay-code agent-relay-code Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Part of the cross-repo epic #2785. This is the consumer issue — the one that makes the ecosystem come alive. Depends on the directory from relaycast#294 and the shared card contract from workforce#296.

Why

The Factory turns issues into reviewed PRs by spawning worker agents. Today, when a worker hits a question it can't answer, it has nowhere to turn — Factory only discovers fleet nodes/capabilities for placement (FleetClient.roster()), not agents by skill, and there's no path to actually ask another agent. Once every proactive agent publishes an A2A card and registers with the Relaycast directory, a Factory worker should be able to: "who can answer X?" → pick an agent by its card skills → send it the question and get a reply.

What

1. Directory-backed peer discovery

Add a card-aware lookup alongside roster() in FleetClient (src/ports/fleet.ts), implemented in RelayFleetClient (src/fleet/relay-fleet-client.ts) — query Relaycast's GET /v1/a2a/directory filtered by skill/tag, returning { name, skills[], url, kind } per candidate. Optionally cache via an in-package index like RelayflowPolicyRegistry (src/dispatch/relayflow-registry.ts).

2. "Ask a teammate" capability

Give a running worker a way to engage a discovered peer: resolve target via #1, then send over Relaycast — A2A POST /a2a/rpc message/send or the broker seam (FleetClient.sendMessage/waitForInjected/onAgentMessage). Reuse the existing DM/relay path; don't invent a new transport. Surface it as a discrete capability/tool the worker invokes mid-task, with a bounded wait for the reply.

3. Publish cards for factory-hosted personas

Generate an A2A card for factory-hosted personas (e.g. .agentworkforce/agents/factory-feature-guardian/persona.json) via @agentworkforce/persona-kit deriveAgentCard ([workforce#296]) and publish it when the persona-backed node comes online — createFactoryNodeDefinition (src/node/factory-node.ts) already assembles capabilities/tags/metadata and calls defineNode. Export the new surface from src/index.ts.

Acceptance

  • A FleetClient method returns teammate agents matching a skill/tag from the Relaycast directory (with how to reach each).
  • A worker can, mid-task, ask a discovered teammate a question and receive a reply (bounded wait).
  • Factory-hosted personas publish a deriveAgentCard-derived card when their node comes online; they appear in the directory.
  • New surface exported from src/index.ts; card schema consumed from @relaycast/a2a (no hand-rolled shape).

✅ End-to-end verification — Factory success criteria (REQUIRED)

This issue IS the "does the ecosystem come alive?" proof. "Compiles" is nowhere near done — the Factory must run a real discover → ask → reply round trip and observe the reply arrive, then check the harness in. Use agent-relay's in-process broker so this runs locally with no external services (src/fleet/internal-fleet-client.ts, src/fleet/ensure-relay-broker.ts).

Run it

npm run build
npm test                                   # vitest run (add the E2E below)

The E2E (test/e2e/ask-a-teammate.test.ts, runnable via npm test) must:

  1. Stand up a local directory (mock the Relaycast /v1/a2a/directory HTTP response, or seed the in-process broker) with two agents whose cards declare distinct skills (e.g. ["infra-watch"], ["code-review"]).
  2. Call the new Factory discovery method with skill: "infra-watch" → assert it returns only the infra agent, with an address to reach it.
  3. Stand up a stub teammate agent on the in-process broker (InternalFleetClient + ensureRelayBroker) that replies to an inbound question with a canned answer.
  4. From a simulated worker, invoke the new "ask a teammate" capability against the discovered agent → assert the question is delivered and the canned reply is received within the bounded wait.
  5. For persona cards: bring a factory-hosted persona node online via createFactoryNodeDefinition, then assert its deriveAgentCard-derived card was published/registered and shows up in the directory lookup from step 2.

Observe (must pass)

  • Discovery returns the right agent for a skill and [] for an unknown skill.
  • The worker actually receives the teammate's reply over the relay path (the round trip completes, not just "message sent").
  • A factory-hosted persona is discoverable by its skill after its node comes online.

Deliverable

  • test/e2e/ask-a-teammate.test.ts running the full discover→ask→reply round trip against the in-process broker + mock directory, one command (npm test).
  • The E2E fails if discovery returns everything unfiltered, if the reply never arrives, or if persona-card publishing is removed (no false green).

Anchor files

  • src/ports/fleet.ts (FleetClient, RosterEntry, sendMessage/waitForInjected/onAgentMessage)
  • src/fleet/relay-fleet-client.ts, src/fleet/internal-fleet-client.ts, src/fleet/ensure-relay-broker.ts
  • src/dispatch/relayflow-registry.ts (in-package registry precedent)
  • src/node/factory-node.ts (createFactoryNodeDefinition), src/index.ts (public barrel)
  • .agentworkforce/agents/factory-feature-guardian/persona.json (proto card / template)
  • planning/linear-issue-cloud-proactive-runtime-fleet-unification.md, planning/factory-unified-node-architecture-linear-issue.md

Fixes #139


Summary by cubic

Adds teammate discovery and an “ask a teammate” tool so workers can find agents by skill and get replies over the relay; also publishes A2A cards for factory-hosted personas so they appear in the Relaycast directory. Delivers the discover → ask → reply flow and shared card publishing required by issue 139.

  • New Features

    • Discovery: FleetClient.discoverTeammates(TeammateQuery) via RelaycastTeammateDirectory (GET /v1/a2a/directory with skill/tag/q). Enforces exact skill/tag matches client-side, dedupes by kind+address, preserves server alias matches for q, and times out cleanly. Works in RelayFleetClient (workspace key or agent token; directoryFetch/directoryTimeoutMs/teammateDirectory) and InternalFleetClient (inject a TeammateDirectory or use a workspace key). Unit tests cover filtering and free-text queries; E2E at test/e2e/ask-a-teammate.test.ts asserts client-side filtering even if the server returns all rows.
    • Ask a teammate: askTeammate(fleet, { from, question, ... }) resolves a target, arms the listener before send, accepts only that agent’s reply addressed to the requester, uses waitForInjected when available, and fails with a bounded timeout.
    • Persona cards: derive with @agentworkforce/persona-kit (compat mapper aligned with upstream deriveAgentCard: intent fallback when no skills, non-transport capabilities mapped to skills e.g. pullRequestreview, transport caps streaming/pushNotifications set), validate with @relaycast/a2a; publish via RelaycastAgentCardPublisher (POST /v1/a2a/register). On 409, verify an exact card match with GET /v1/a2a/agents and return alreadyPublished (plus certification when present).
    • Factory node: createFactoryNodeDefinition(...) attaches agentCard; startFactoryNode(...) publishes on node-online and exposes a cardPublished promise.
    • Public API: exports discovery (TeammateQuery, TeammateAgent, RelaycastTeammateDirectory, askTeammate), persona tooling (deriveFactoryPersonaCard, RelaycastAgentCardPublisher, startFactoryNode), and defaults (DEFAULT_RELAYCAST_BASE_URL, DEFAULT_TEAMMATE_DIRECTORY_TIMEOUT_MS, DEFAULT_ASK_TEAMMATE_TIMEOUT_MS). Adds A2aSkill to types. Tests include unit coverage plus an end-to-end round trip. Persona update: .agentworkforce/agents/factory-feature-guardian/persona.json adds skills and relay.agentName.
  • Migration

    • Use fleet.discoverTeammates({ skill | tag | q }) or askTeammate(...) to consult a peer mid-task.
    • For discovery: in RelayFleetClient, configure a workspace key or agent token; in InternalFleetClient, pass a teammateDirectory or a workspace key.
    • To publish cards: pass persona to createFactoryNodeDefinition(...) and provide a RelaycastAgentCardPublisher to startFactoryNode(...) when an agentCard is present.

Written for commit d93aaa5. Summary will update on new commits.

Review in cubic

@kjgbot
kjgbot marked this pull request as draft July 21, 2026 14:16
@kjgbot

kjgbot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

End-to-end merge gate: this PR is not production-ready yet.

Validated locally at 3e12fc676c6389b254310cd6e979a35d39cacedb:

  • focused discovery, ask/reply, and persona-card tests: 3 files / 5 tests pass
  • build, feature map, and 9 packed-install checks pass
  • all five hosted Factory E2Es pass

Real integration blockers:

Unblock/merge criteria:

  1. publish/deploy the Relaycast directory and canonical persona-kit deriveAgentCard
  2. remove the compatibility mapper and consume the published shared API
  3. integrate current Factory main
  4. run a real deployed discover → ask → reply → persona publish/directory lookup, in addition to the checked-in mock E2E
  5. rerun all local and five hosted E2E gates and clear review threads

Marking draft so the passing mocked checks cannot be mistaken for full ecosystem proof.

Copy link
Copy Markdown
Member

Addressed the branch-integration finding from the merge-gate review on the current PR head.

  • Merged current origin/main at 0c70c64 (Factory 0.1.52) into this branch in commit 90b4837; the branch is conflict-free and GitHub reports it mergeable.
  • Kept the reply-matching, timeout, and idempotent card-registration hardening in 66a1fa2.
  • Exact-head local validation passed: npm run build; focused teammate/persona tests (3 files / 6 tests); full npm test (72 files / 1,313 tests); feature-map check; CLI help; and the tier-2 fixture cycle.
  • Fresh GitHub Actions CI run #456 passed all five jobs on 90b48377e7b24f0dc2d748a8777a92f1973e671c: package, verification-gate-e2e, load-e2e, verification-stack-e2e, and kubernetes-provider-e2e.

The PR remains draft. The external merge gates are still blocked exactly as identified: Relaycast #294 is not deployed, Workforce #296 has not published deriveAgentCard, the compatibility mapper therefore remains, and the required real deployed discover → ask → reply → publish/directory proof cannot yet be run. I am not treating the mocked/in-process E2E as production-readiness evidence.

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.

"Who can I ask?" — discover teammate agents by skill and engage them; publish cards for hosted personas

2 participants