agentscope-extensions-agent-protocol exposes AgentScope’s Harness Agent as a standard Agent Protocol HTTP API, letting external systems (CI, other agent platforms, automation jobs) submit “tasks” using a uniform contract — no need to know the implementation details.
When to use
- You want the Agent to be remotely scheduled like a cloud function.
- An existing team uses an Agent Protocol client and you’d like to plug in directly.
- You’re embedding a Harness Agent in a Spring Boot service and want auto-exposed
/tasksREST endpoints. - You’re hosting a remote subagent that another Harness parent calls over HTTP.
Protocol layering
AgentScope uses different protocols for different trust / UX boundaries:Add the dependency
Enable
The module is delivered as a Spring Boot auto-configuration. In a Spring Boot app:- Provide a
HarnessAgentbean (or a customAgentFactory). - Enable in
application.yml:
/tasks REST endpoints are then registered automatically.
Control-plane vs execution workspace
AgentProtocolTaskStore persists protocol task metadata (TaskRecord for submit / resume /
snapshot) through a dedicated ProtocolTaskRepository. By default that repository is rooted at
agentscope.agent-protocol.task-store-path (${user.dir}/.agentscope/agent-protocol), under the
synthetic bucket agents/_agentscope_protocol/tasks/.
This path is independent of each HarnessAgent’s own WorkspaceManager (MEMORY, sessions,
skills). Multi-agent factories may give each agent a different .workspace(...); protocol
task_id lookup always goes through the control-plane repository.
You may supply your own ProtocolTaskRepository bean to override the default. Construct
AgentProtocolTaskStore with a ProtocolTaskRepository only — do not pass an execution agent’s
WorkspaceManager.
Concurrent execution
The agent is stateless between calls — a singleton handles multiple concurrent tasks. Each task carries its own(userId, sessionId) via RuntimeContext, so state is fully isolated:
Agent selection (AgentFactory)
Which agent runs a task is decided by an AgentFactory bean. Without one, the default factory returns the single HarnessAgent bean for every task.
Define your own bean to route by agent_id, tenant, or any custom submission-context key:
AgentRequest fields:
The factory is invoked once per run — on submit and again on every
/resume — with the original submission context, so routing decisions stay stable across HITL pauses.
Return a distinct instance per call (for example a prototype-scoped bean) when tasks run concurrently. Returning null fails the task with an error status.
Context attributes
Callers pass their own data incontext.attributes, nested so it never mixes with the protocol’s own context fields. Besides being visible to the AgentFactory, attributes reach the running agent through its RuntimeContext.
They arrive as one map under a single namespaced key, AgentProtocolConstants.RUNTIME_CONTEXT_ATTRIBUTES_KEY (agentprotocol.context.attributes):
agentId drives async tool wakeup routing, outboundAddress carries the gateway reply address. A caller naming an attribute after one of those would otherwise change how the agent behaves. Attributes are never rendered into the system prompt, so they do not affect what the model sees.
Promoting attributes to their own keys
RegisterRuntimeContextCustomizer beans when a tool expects a plain key such as ctx.get("tenant"), or to turn attributes into typed values. RuntimeContextCustomizer.flatten copies an explicit allow-list, silently skipping framework-reserved names:
@Order, after the namespaced injection — a later customizer overrides an earlier one. Hand-written customizers are trusted and may write any key, including reserved ones.
Sending attributes from a parent agent
A parent agent delegating to a remote subagent supplies attributes in two ways, merged with the per-call ones winning:Endpoints
Submit a task
POST /tasks
context fields:
Response on success:
{ "task_id", "status": "pending" }.
Poll / wait / cancel
While the remote agent waits for tool confirmation, the snapshot reports
status: awaiting_confirm but the stored TaskStatus remains RUNNING so parent barriers keep waiting.
Stream events (SSE)
GET /tasks/{taskId}/events
Server-Sent Events of agent progress. Requires agentscope.agent-protocol.streaming-enabled=true (default).
Reconnect / resume:
- Query param
from_seq— start after this sequence number - Header
Last-Event-ID— used whenfrom_seqis omitted (same meaning)
id, the remote event type as event, and a JSON body as data.
Stream detail levels
context.detail on submission decides how much of the run reaches subscribers. Each level is a superset of the previous one:
Only
verbose reproduces the agent’s own event stream in full. An unrecognized value is treated as status.
Every event body also carries two fields beyond its type-specific ones:
Both are additive: a client that ignores them keeps reading the flat fields (
text, toolCallId, status, …) exactly as before, and one that predates AGENT_EVENT simply skips those messages.
Resume after HITL
POST /tasks/{taskId}/resume
tool_call_id is also accepted as an alias for toolCallId. Requires agentscope.agent-protocol.hitl-enabled=true (default). On success returns { "task_id", "status": "running" }.
How remote HITL interacts with a calling parent harness is documented under Remote authorization.
Configuration
Example:
Whenenabledisfalse(the default) the dependency stays inert — no REST endpoints are exposed, safe to ship.
Workspace integration
Each task receives an isolated workspace fromWorkspaceManager. Once the task finishes, files and logs in the workspace are exposed via standard Agent Protocol endpoints so external clients can fetch artifacts.