Files
deepseek-harness/packages/experimental/agent-team

description, kind
description kind
Run a small team of named agents in one session: durable messages between members and a shared task board, for deployments composing the experimental Team plugins. package-reference

@deepseek-ai/dsh-experimental-agent-team

English | 中文

Summary

dsh-experimental-agent-team turns one coding session into a small working team: the session's agent becomes the Lead, creates named teammates for delegated work, exchanges durable messages with them, and tracks shared tasks on a common board. Messages and task state survive crashes, reloads, and interruptions, so a teammate that was offline receives its queued messages when it resumes. It provides no tools of its own — mount the sibling dsh-experimental-tool-agent-team so the model can create teammates, message them, and use the task board. It is experimental: excluded from official releases, carries no stability promise, and needs durable session storage to activate.

Table of Contents


Use this package

Add this package to a composition when one agent should run a small team of named helpers in its own working directory, with messages and task state that survive crashes and restarts. It ships no tools of its own: mount it together with @deepseek-ai/dsh-experimental-tool-agent-team so the model can create teammates, message them, and use the task board.

When to choose it

Choose it when several agents must cooperate on one shared workspace and their roster, messages, and task state must survive crashes and restarts. Avoid it when teammates need separate working directories, when several processes must coordinate over one team, or when a task owner should be released automatically — none of those are supported. The team features need durable session storage to activate.

Smallest working setup

The smallest addition to an existing composition is durable session storage plus both Team packages:

# smallest team setup — durable storage plus both Team packages
- name: '@deepseek-ai/dsh-session-persistence-jsonl'
- name: '@deepseek-ai/dsh-experimental-agent-team'
- name: '@deepseek-ai/dsh-experimental-tool-agent-team'

With the tools installed, the model does the rest on request — for example, "create a teammate named reviewer to check the diff", then "send reviewer the change summary". All limits are optional and validated at startup:

Field Default Meaning
maxMembers 8 Maximum teammates a team may ever create, including failed ones
maxTasks 256 Maximum active tasks on the board
maxPendingMessagesPerMember 64 Maximum queued messages for one member
maxMessageBytes 65,536 Maximum size of one sent message
disposalTimeoutMs 5,000 Time allowed for shutdown cleanup

The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.

Teammates

Ask the Lead to create a teammate: give it a unique lowercase name such as reviewer and describe its job. A teammate starts fresh with no memory of the Lead's conversation, or as a fork that inherits the Lead's completed turns; the creation request chooses which. Teammate names are permanent — even a teammate whose creation failed keeps its name, and no name is ever reused.

The roster shows every member with its role (lead or teammate) and current status: running, idle, inactive (a member that exists but is not loaded), provisioning, or failed. A member that is not loaded receives its messages when it wakes.

Only the Lead can create teammates or interrupt them.

Messages between teammates

Any member can send a message to any other member or to the Lead. A live member receives it immediately; an offline member's messages queue and arrive when it resumes. Messages are never lost and never delivered twice.

Two delivery modes cover the two common intents: a quiet message delivers information without starting an idle teammate (use it for updates that can wait), and a follow-up makes the message the recipient's next turn (use it to hand over work). The sender always sees the outcome — delivered now, or queued. A queued message is already safely stored, so it must not be resent.

Shared task board

Any member can add a task with a title, details, optional dependencies on other tasks, and optional hints about which files it will touch. A task is claimable only when everything it depends on is complete.

Tasks have an owner: a member claims a task to start work, completes it when done, releases it back, or reopens it; the Lead can assign a task to any member. Every change is compare-and-set: an update based on an outdated copy is rejected, so two members cannot silently overwrite each other's work.

File hints produce warnings when two in-progress tasks plan to touch overlapping paths — they never block anything. Deleted tasks remain in history but disappear from the active list.

Waiting and interruption

A member can wait for the next team change — a teammate's status, an incoming message, or a task update — instead of polling repeatedly; the wait reports only whether it timed out, and the caller re-reads the current state afterward.

The Lead can stop a teammate's current turn without deleting its queued messages; task ownership is unchanged.

What success and failure look like

Success looks like a teammate appearing in the roster, a message reporting accepted or queued, and task revisions advancing with each change. Likely failures are reported as specific errors instead of silently corrupting state: sending to a name that is not a member, claiming a task that is not ready, editing with an outdated revision, or creating a teammate beyond the member limit.


Understand the implementation

Implementation internals — click to expand

This section explains the design decisions behind the service and points at the code that realizes them; the observable behavior is fully covered in Use this package.

Design philosophy

The service is built on one separation and three commitments:

  • Durable log, derived state. The Lead Session log is the single source of truth; roster, mailbox, and task state are replayed from it on every read.
  • Process-local ownership. All coordination lives in one process; the guarantee is retry plus de-duplication, never cross-process consensus.
  • Explicit authority. Every service method takes the exact live calling Agent; only the Lead spawns, reassigns, or interrupts.
  • Bounds that fail loud. Every limit is a validated deployment value, and exhaustion reports a typed error instead of reusing an id or name.

The Agent Teams Agent Note owns the identity, mailbox, task, and shared-checkout decisions.

Source map

File Role
src/index.ts Plugin entry: Config schema, service registration, recovery scheduling
src/roster.ts Team identity, membership resolution, provisioning, and roster teardown
src/mailbox.ts Durable queue, target-local dispatch, acknowledgement, and recovery
src/task-board.ts Task CAS commands, DAG validation, and derived views
src/journal.ts Serialized Lead-log transactions and commit notification
src/fold.ts Strict replay fold that decodes and validates Team events
src/activity.ts One-shot change waiters and disposal release
src/lifecycle.ts Shared admission cutoff and bounded settlement
src/invariant.ts Invariant companion that replays candidate events before append

Team identity and roster

Every ordinary runtime root is the implicit Lead of a Team whose TeamId equals its SessionId; there is no creation event, and durable state begins with the first member, message, or task record. spawnTeammate() first appends and flushes a provisioning member record, then asks the configured provider to create the reserved child; a provider failure appends a durable failed member. A fresh child starts with no Lead history; a fork child captures the Lead's completed-turn prefix once. Recovery reconciles an unterminated provisioning record against the child's independently persisted Session: a matching direct-parent and continuable descriptor plus a recorded initial user message produces active, and anything else produces failed. If recovery wins a same-process race, the creator accepts the terminal state or reports TEAM_PROVISIONING_CONFLICT and drains the child. Names are reserved by the first provisioning record and never reused.

Durable mailbox

sendMessage() validates peer membership, appends team/message/queued, and flushes before attempting delivery. The target message begins with Team message <id> from <name>: and keeps the same id and sender in TeamMessageSource. A target receipt is acknowledged with team/message/delivered only after the target Session durably holds the message identity in its pending inbox or recorded history. Immediate admissions are serialized per target in durable queue order; recovery dispatches queued-minus-delivered records in the same order. Delivery folds both live and persisted target inbox/history state before retrying, so a crash between inbox acceptance and model claim does not duplicate the message. The guarantee is process-local retry plus target-Session de-duplication, not cross-process exactly-once delivery.

Shared task board

Tasks are complete versioned snapshots; every mutation carries expectedRevision, and a stale caller receives TEAM_TASK_STALE_REVISION instead of overwriting a newer value. Numeric task-<n> ids require a safe-integer suffix, and id-space exhaustion reports TEAM_TASK_LIMIT instead of reusing the final id. Deleted tasks remain tombstones for replay and id stability but do not consume maxTasks or appear in listTasks(). writeScopes are normalized workspace-relative prefixes; views warn on overlap with in-progress tasks but never block claim or authorize writes.

Waiting and interruption

waitForChange() waits for one roster, task, mailbox, or live-status edge that occurs after registration, from ten seconds through one hour, and reports only whether it timed out; runtime disposal releases current waits. Cancellation preserves an Error reason or reports a non-Error reason through TEAM_WAIT_ABORTED. interrupt() is Lead-only and delegates to the continuable-subagent interrupt path, which cancels only a live teammate's current turn with keepInbox; it neither releases task ownership nor deletes durable mail.

Durability model

Team events are appended to the exact live Lead Session and flushed before the operation reports success or wakes waiters. team/member, team/task, team/message/queued, and team/message/delivered are log-only: they never enter the conversation surface, so derived model history is untouched by coordination records. Session event seq and time own ordering and timing; snapshots do not duplicate them. The ./invariant companion replays each candidate Team event against its committed prefix and rejects invalid transitions before append.

Disposal

Disposal closes admission, aborts and awaits admitted creation and mailbox-dispatch transactions, then asks the continuation owner to release the roster's exact live direct children and their descendants; non-Team continuable children of the Lead remain untouched. Cleanup failures make disposal fail visibly, bounded by disposalTimeoutMs.


Further Exploration

Read these pages when the package-level contract is not enough. They move from the shared subsystem types to the tool surface and the decisions behind the design.


Browser Remote

TeamService owns the generated agentTeams/view, agentTeams/createTask, and agentTeams/updateTask Remote methods beside the roster, mailbox, task, and lifecycle operations. The ./remote export supplies the Client contribution mounted by the Web UI, while ./client re-exports the request, view, and task-mutation result types that are safe in a browser compilation face. Typert retains transport failures in its outer RemoteResult; create and update rejections remain explicit domain results inside a successful transport response, with stale update revisions distinguished as task conflicts.

Model Experience

Peer messages

What the model sees

Each delivered peer message is a user-role message. A short first text block names its stable message id and sender; the sender's original content blocks follow unchanged. Roster, task, and mailbox records are log-only and never enter derived model history.

Token effect

Each peer delivery adds the sender prefix plus message content to the target history. Task and roster mutations add no model tokens; their model-facing representation belongs to @deepseek-ai/dsh-experimental-tool-agent-team results.

KV Cache effect

Peer messages append after the target's reusable history prefix. Cold resume reuses the persisted conversation before appending a previously undelivered item.

Known Limitations and Deferred Work

These limits describe what a team cannot do yet or what needs special operational care. They are current package constraints, not a comparison with other coordination mechanisms.

  • Experimental prototype with no stability promise — the package is private, excluded from official releases, and its contracts change freely while it incubates.
  • One process and one shared checkout — members share cwd and observe edits immediately; this package provides no worktree, remote member, merge, or filesystem lock.
  • Advisory write scopes — Bash, formatters, code generators, and direct external writers can bypass filesystem version checks; Leads must coordinate ownership and review the final diff.
  • Flat immutable roster — only the Lead creates direct teammates; there is no nested Team, rename, deletion, or name reuse.
  • No automatic ownership release — idle, interruption, process exit, and failed work do not release a task owner.
  • Mailbox is not cross-process exactly-once — concurrent harness processes over one Team are unsupported.

Dev Note

Working context for maintainers — click to expand

This Dev Note is working context for maintainers and is explicitly non-authoritative.

Promotion

Promotion to a product-role group requires reviewing the public contract, limitations, test evidence, release payload, runtime dependents, and a named stable owner, per the experimental subtree rules.

Future directions

Undecided directions include nested Teams, automatic ownership release policies, cross-process mailbox transactions, and filesystem isolation via worktrees; none of these are committed.