Multiple Agents in One Repo

Use multiple agentIds to isolate different SDK Agents in the same project directory

If you create several local SDK Agents inside the same project directory, the most important rule is:

  • give each agent a stable and different id

For example:

const planner = new Agent({ id: "planner" });
const planner_workspace = new Workspace({ id: "planner-project", path: repo_path });
const planner_session = await planner.sessions.create({ workspace: planner_workspace });

const reviewer = new Agent({ id: "reviewer" });
const reviewer_workspace = new Workspace({ id: "reviewer-project", path: repo_path });
const reviewer_session = await reviewer.sessions.create({ workspace: reviewer_workspace });

Both Workspaces may target the same physical directory. Session storage remains partitioned by agent_id, while each Session explicitly selects its Workspace. If the Agents need isolated command environments, create and pass a separate Shell instance to each Workspace.

Coordinate a Group with a decision model

A Group selects the members who should respond in the current round and runs them in parallel. Once all selected members finish, their replies enter the shared context for another decision. An empty member set returns the Group to idle. The dispatcher does not generate assignment text or stage plans.

Each GroupSession has one dispatch loop. User input received during execution is saved immediately and joins the whole completed round in the next pending_messages batch. Input received during a decision cancels that decision and triggers a fresh one; a stale result cannot start members even if it arrives late. Its audit status is superseded, not a failure. Ordinary messages do not interrupt members already executing; use stop to interrupt immediately.

Recovery checkpoints contain unconsumed pending_message_ids, including inputs awaiting the end of an executing round. After restart, these messages return to the same loop for a fresh decision.

/** Select parallel responders through Federation Jev. */
import { Group } from "@downcity/agent";
import { Embassy } from "@downcity/federation";

const embassy = new Embassy({
  federation_url: "http://localhost:3000",
  user_token: process.env.FEDERATION_USER_TOKEN,
});
const group = new Group({
  id: "delivery",
  instruction: "Research and implement the solution without repeating completed work.",
  members: [planner, reviewer],
  model: {
    decision: (input, signal) => embassy.user.ai.decision({
      ...input,
      model: "jev-latest",
    }, signal),
  },
});
const group_session = await group.sessions.create({ workspace: planner_workspace });
await group_session.prompt({ query: "Analyze the current design and suggest actionable improvements." });

group.sessions.create({ workspace }) creates an independent project conversation. Use group.sessions.self({ workspace }) for the Group's fixed self conversation: its ID always equals group.id; the first call creates it and subsequent calls restore it. The host supplies a stable Workspace and must use the same Workspace when restoring. The default session store is in memory; recovery across processes requires a persistent storage provider.

In Desktop, selecting a Group in Chat opens its fixed self conversation in a private groups/<group_id>/main Workspace. Project conversations are created independently in Work. The self conversation uses the Group name as its title, exposes configuration through the chat settings menu, and contributes unread attention to Chat.

Configure an execution model and capability description for each member. One decision call submits a boolean question for every member; the Group collects answers into member_ids. A custom dispatch_strategy.decide_dispatch() also returns only { member_ids: [...] }; an empty array ends the responses. For new user messages, the default decision prompt allows greetings, availability checks, mentions, and direct questions to receive replies. Each member is sampled independently using its reply probability: at or below 40% it does not respond; between 40% and 75% its response rate is (reply_probability - 0.4) / 0.35; at or above 75% it always responds. A 60% reply probability therefore gives approximately a 57% response rate. Sampling can still select zero or multiple members. Decision answers must include a probability in the 0–1 range, representing confidence in the selected boolean value. For value: false, the reply probability is 1 - probability; missing or invalid probabilities fail the dispatch. Automatic rounds after member replies retain boolean selection and require necessary follow-up work or information, avoiding repeated acknowledgements. Execution failures are recorded in the conversation. Stopping cancels the decision and member executions, and automatic rounds remain bounded by the round limit and repeated-content detection.

Each round supplies members with the actual Group roster, trigger, latest user request, and new messages. User input received during execution becomes the focus of the next round instead of being displaced by a completed member reply. The latest user request takes priority over the long-term Group objective. Greetings, availability checks, and role questions call for brief replies and do not authorize creating documents or starting development. Automatic rounds default to stopping and continue only for explicitly unfinished user-authorized work, necessary handoffs, or questions for a member. General invitations, plans, and requests for user requirements do not justify replying to oneself.

The default strategy saves each member's model value, confidence, reply probability, selection probability, random value (only when sampling), and final selection in decision.member_decisions in the dispatch record. These records distinguish model decisions from random sampling when a member does not respond.

Desktop displays a Decision models section. Choose Jev as the Dispatch model when creating or editing a Group. Existing Groups configured with text models must select a decision model. AI-assisted team drafting continues to use a text model, separately from runtime dispatch.