Skip to content

darrelmiller/agentdock

v0.2.0MIT

Install and operate AgentDock workspace-backed A2A agents, discover them through AI Catalog, and delegate tracked work.

AgentDock

A Spec-Works project for hosting workspace-grounded agents through standard discovery and A2A.

AgentDock is currently incubated in the private darrelmiller/agentdock repository. Project affiliation does not imply repository ownership: transfer to the spec-works organisation and public distribution are deferred. The repository was renamed from portfolioagent to agentdock; local installation paths remain unchanged.

The host contract documents AgentDock-specific behaviour. specs.json links the underlying specifications in the Spec-Works linkset convention. See contributing and the MIT license.

This is an incubating host/tool, not a published conformant Spec-Works part. ADR 0001 records the affiliation and staging exceptions.

A local multi-agent A2A host backed by the GitHub Copilot SDK for .NET. Each configured workspace supplies an agent's knowledge, instructions, skills, and descriptive identity. Each agent owns a persistent Copilot session and a separate A2A task store. Workspaces currently use local Git repositories, but their public agent cards do not expose that implementation detail.

The private agent repository and the grounding repository are separate. No portfolio content is copied into this repository.

Install as a .NET tool

The repository builds SpecWorks.AgentDock.Cli, invoked as agentdock. The first tool packages target Windows x64 and ARM64. A small manifest package selects the matching RID package, which includes both the .NET/ASP.NET runtimes and the SDK's Copilot runtime assets. Installation requires the .NET 10 SDK or later for platform-specific tool support; no separate .NET runtime or Copilot executable is required after installation.

Not published to NuGet.org yet. Once a version is published:

dotnet tool install --global SpecWorks.AgentDock.Cli
agentdock serve

To build and install from an authorized local checkout:

dotnet pack .\src\PortfolioAgent.Api\PortfolioAgent.Api.csproj -c Release `
  -p:Version=0.2.0-preview.1 --output .\artifacts\packages
dotnet tool install --global SpecWorks.AgentDock.Cli --version 0.2.0-preview.1 `
  --add-source .\artifacts\packages
agentdock --version
agentdock serve

Keep all three generated packages together: the manifest plus .win-x64 and .win-arm64 packages. Do not install the RID packages directly. See tool packaging and release for the local verification command and the gated publication workflow.

Requirements

  • .NET 10 SDK to build from source or install the NuGet tool
  • A GitHub Copilot subscription and a Copilot SDK runtime authenticated for your account
  • The a2a CLI for A2A discovery and smoke tests

The application binds only to 127.0.0.1. Its A2A endpoint is unauthenticated. Tool permission requests are approved automatically so the headless agent can edit repository files and run local commands as the service account. Any local process that can reach the endpoint can therefore make those changes. Use only with a trusted local caller and repository; do not expose it beyond loopback without adding authentication and an approval mechanism.

Run locally

From the repository root, run.ps1 restores the persisted agent registrations:

.\run.ps1

Pass another local Git repository path directly when needed:

.\run.ps1 -RepositoryPath "D:\github\some-repository"

An initial repository path is optional when starting the host. It seeds a new registry only; otherwise serve restores saved registrations, or creates an empty registry on first run. Workspace paths are not read from an environment variable. The folder name supplies the friendly agent ID: Portfolio becomes portfolio, AI Catalog becomes ai-catalog.

Host and agent lifetimes

The host is an ordinary foreground application. No Windows Service, installation, or service manager is involved. Run serve in one terminal and use other CLI invocations to control it:

# Terminal 1: start the host; Ctrl+C shuts down the host and all its agents.
.\publish\win-arm64\AgentDock.exe serve

# Terminal 2: register another workspace and start its agent in the running host.
.\publish\win-arm64\AgentDock.exe start "D:\github\darrelmiller\ai-catalog"
.\publish\win-arm64\AgentDock.exe start "D:\github\another\portfolio" --id another-portfolio
.\publish\win-arm64\AgentDock.exe start "D:\github\darrelmiller\margo" --id assistant --persona margo
.\publish\win-arm64\AgentDock.exe list
.\publish\win-arm64\AgentDock.exe stop another-portfolio

start and stop apply to agents, not the host. start requires an already running host. stop cancels and joins outstanding A2A work before unregistering the agent. It does not delete workspace files or saved session state. Re-registering the same ID and workspace resumes its saved session. Other agents continue serving while registrations change.

Registration does not start a Copilot CLI process or load a conversation. The host owns one shared Copilot runtime, started lazily on the first work request or explicit /new-session. Concurrent first requests are serialized only for runtime initialization, so they cannot spawn duplicate processes. Each agent creates/resumes its own session with its own workspace, skills, configuration, and event subscription; requests target that session object, never a global "current session". Different agents can execute concurrently on the shared runtime.

Stopping an agent disposes only its session, not the shared runtime or other agents' sessions. The shared runtime is disposed when the host shuts down. Loaded sessions currently stay resident until their agent stops, /new-session replaces them, or the host shuts down; idle eviction and a multi-runtime pool are not implemented. A runtime failure affects all agents sharing it and is reported; there is no silent session replacement or automatic failover.

Equivalent development commands:

.\run.ps1 -Command start -RepositoryPath "D:\github\darrelmiller\ai-catalog"
.\run.ps1 -Command start -RepositoryPath "D:\github\another\portfolio" -AgentId another-portfolio
.\run.ps1 -Command list
.\run.ps1 -Command stop -AgentId another-portfolio

IDs are 1-64 lowercase ASCII letters/digits separated by single hyphens. Automatic IDs use the folder name, not a README heading that may change. Duplicate IDs and duplicate workspace paths are rejected; supply --id to distinguish repositories with the same folder name. An ID is not a Copilot session ID.

Registrations persist under %LOCALAPPDATA%\AgentDock\hosts\port-5080\agents.json by default. The initial path seeds a new registry only; on subsequent host starts the saved registrations are restored, including the absence of agents previously stopped. Agents that fail to start are shown by list with their error, but are excluded from discovery. Stop and re-register to retry them.

Without an explicit initial path, there is no default agent or root A2A alias. Discover agents through /catalog and use their /agents/{id} URLs. Passing an initial path retains the legacy root aliases for that workspace; the host never guesses a default from the catalog.

CLI control uses a local named pipe restricted to the same OS user as the host. There is no HTTP administration API or management token. An optional REPOAGENT_ALLOWED_ROOTS setting (semicolon-separated absolute paths) restricts which workspaces may be registered. Without it, the same-user CLI may register any supported local workspace. Paths through junctions or symbolic links are rejected.

The A2A agent card does not expose its workspace provider. To set its public name and description, add .agent/agent-profile.json to the workspace:

{
  "name": "Portfolio",
  "description": "A private knowledge base for project decisions and connections."
}

If that file is absent, the name comes from the root README.md level-one heading and the description from its first prose paragraph. If neither source provides a value, generic defaults are used. The runtime supplies the A2A endpoint and protocol fields.

By default, the server listens on http://127.0.0.1:5080. Override the port with REPOSITORY_AGENT_PORT and the model with REPOSITORY_AGENT_MODEL. The default model is gpt-5.4.

Use the same REPOSITORY_AGENT_PORT for host and CLI commands. Separate ports address separate hosts. REPOAGENT_STATE_DIRECTORY overrides the host's registry and session-state directory; one process may own a registry directory at a time.

Publish a Windows ARM64 binary

On the Windows ARM64 development machine, publish a self-contained executable:

dotnet publish .\src\PortfolioAgent.Api\PortfolioAgent.Api.csproj `
  -c Release -r win-arm64 --self-contained true `
  -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true `
  -o .\publish\win-arm64

Run the published executable to restore the registry:

.\publish\win-arm64\AgentDock.exe serve

The agent scans text files when it receives a question, so changes to bundle documents are picked up without restarting. Skill directories are discovered when a Copilot session is created; restart the agent to refresh changed or added skills.

Talk to it with A2A CLI

a2a card get http://127.0.0.1:5080
a2a send -a http://127.0.0.1:5080 --transport rest --stream "What projects are active?"

# Each agent has independent discovery, task management, and message endpoints.
a2a card get http://127.0.0.1:5080/agents/portfolio
a2a send -a http://127.0.0.1:5080/agents/portfolio --transport rest --stream -o jsonl "What projects are active?"

Health check: http://127.0.0.1:5080/healthz

Work responses contain task.id and result artifacts, rather than a plain message. Use task operations against the same agent URL that created the task. The original root discovery and /a2a URLs remain aliases for the initial workspace when it is registered; its card advertises the friendly-ID endpoint.

HTTP+JSON is the preferred card interface, at /agents/{id}. JSON-RPC remains available at /agents/{id}/a2a for existing clients. All agents support SSE streaming. Use --transport rest --stream with a2a send; -o jsonl is useful for processing individual stream events. Submitted, Working, public assistant intent/message updates, result artifacts, and Completed are streamed as A2A events. Assistant updates are Working status events with a text message, not separate tasks. Reasoning and raw tool arguments/results are not forwarded. Final text is delivered as a result artifact; streaming does not yet emit token-by-token artifact deltas.

AI Catalog discovery

GET http://127.0.0.1:5080/catalog returns application/ai-catalog+json with entries of type application/a2a-agent-card+json, pointing to each available agent's card. Entries are generated from the live registry, not a separately maintained list. Catalog identifiers include a durable host identity plus the friendly agent ID, so IDs from separate hosts do not collide.

ai-catalog catalog add local-agents http://127.0.0.1:5080/catalog
ai-catalog search portfolio --json
ai-catalog catalog update local-agents

Catalog clients cache documents; refresh after starting or stopping agents.

Custom agent personas

Register an installed custom-agent name with --persona; it is independent of the friendly ID and the workspace's public card:

.\publish\win-arm64\AgentDock.exe start "D:\knowledge\assistant" --id assistant --persona margo
.\run.ps1 -Command start -RepositoryPath "D:\knowledge\assistant" -AgentId assistant -Persona margo

list includes the persisted persona name. Existing registrations without it still run the default agent. The SDK resolves the name from available custom agents, including workspace agent roots and the host user's ~\.copilot\agents directory. AgentDock uses the SDK's native session-scoped selection API rather than parsing agent definitions itself. Persona selection is validated on first work, not during registration, to preserve lazy runtime startup. An unavailable or disabled persona fails the request; it never silently falls back to the default agent.

Selection is reapplied before work on created/resumed sessions and when /new-session creates a replacement. It affects only that agent's session, not other sessions on the shared runtime. To change a registered persona, stop and re-register the same ID/workspace with the new option (or omit it to return to the default). This preserves the saved conversation; request /new-session explicitly if you want fresh history.

Selecting Margo does not import this app's conversation, grant private-memory filesystem access, or configure Work IQ. Existing workspace/tool boundaries and disabled hook/MCP discovery still apply. AgentDock does not copy persona definitions into the workspace or expose private persona paths in public cards.

Consumer skill

The AgentDock CLI skill teaches consumers how to acquire the ai-catalog and a2a CLIs, run and manage AgentDock, discover cards, and track tasks. The repository root is an Agent Plugins 1.0 plugin named agentdock, with a root plugin.json and a discoverable skills/agentdock-cli/SKILL.md. Install the repository or the generated plugin ZIP using a compatible client's plugin installer. See plugin distribution and installation. Installation syntax is client-specific; the standard does not define a universal install command.

Alternatively, install just the skill in a directory supported by your agent harness:

$destination = Join-Path $HOME ".agents\skills\agentdock-cli"
New-Item -ItemType Directory -Path $destination -Force | Out-Null
Copy-Item "skills\agentdock-cli\SKILL.md" (Join-Path $destination "SKILL.md")

Use your harness's documented skill root if different. The skill does not install executables automatically. Publishing also copies it alongside the executable as skills\agentdock-cli\SKILL.md.

Repository discovery and safety

  • The configured path must be a Git repository. The agent scans supported text formats, skips common build, dependency, and Git directories, and does not follow directory junctions or symlinks outside normal traversal.
  • Skills are found by locating SKILL.md files and passing their skill roots to the Copilot SDK. Repository instructions and local custom agents are loaded, but automatic configuration discovery is disabled so repository-provided MCP servers and hooks are not started.
  • Tool calls run under the current Windows account. The agent is instructed to preserve unrelated changes and not commit, push, change remotes, or perform destructive actions unless requested.
  • The A2A endpoint has no authentication yet. Loopback binding prevents remote network access, not access by other processes running on the same machine.
  • Responses should cite repository-relative source paths and distinguish current facts from dated or exploratory material.
  • Agents are instructed to check repository freshness and use git pull --ff-only on a clean branch with a configured upstream. Local changes, divergence, missing upstream, and update failures must be reported rather than bypassed. External service calls are not categorically prohibited by the host prompt; credential and private-data protections still apply.
  • One Copilot session is shared by all work for each agent workspace, regardless of the caller's A2A context ID. Turns are serialized. Different callers share history; this is not caller isolation.
  • Different agents have independent sessions, turn gates, and task stores, and can work concurrently. This separates their conversation history, not their OS permissions. They run under the same account with the same automatically approved tool permissions; it is not a sandbox.
  • Each work message creates an A2A task with Submitted, Working, and Completed states and a result artifact. Messages targeting an existing nonterminal task continue that task. A2A context IDs remain correlation handles, not Copilot session selectors. The SDK handles task errors and cancellation.
  • Send /new-session to explicitly rotate the agent's session. Empty messages and this control operation return messages, not tasks. Status enquiries should use A2A task get, not a work message.
  • The active Copilot session ID is stored in the host state directory under sessions, scoped to the friendly agent ID and canonical workspace path, and resumed on the next work request after restart. Reusing an ID for a different workspace does not reuse its old session. Existing single-agent session state is imported if available. Resume failures are reported rather than replaced with a fresh session. Only one running agent may own a workspace at a time, even across hosts.
  • A2A task records currently use the SDK's in-memory store and do not survive restart, even though the Copilot session does. Task completion means execution finished, not that the caller accepted the outcome. Interactive input/approval bridging is not implemented yet.
  • Retrieved repository content is sent to the configured Copilot runtime for inference. Do not point the agent at data that is not permitted to leave the machine through that inference service.