How to pause your agent for human input
Some tasks reach a decision point the agent cannot resolve on its own — a target environment, an approval threshold, a destructive step that needs sign-off. murmur/tool-request-input gives the model a ready-made tool to pause at that point, surface a question to the operator, and resume exactly where it left off once the operator replies.
The relevant manifest options are:
| Option | Controls |
|---|---|
| artifacts[].runtime | Whether a WASM artifact is a model-visible tool, inference driver, or hook |
| lifecycle.input_timeout_secs | Maximum seconds to wait before failing the task if no reply arrives |
Step 1 — write the manifest
Create a murmur.yaml file. Add murmur/tool-request-input to artifacts with runtime: tool, and write a system_prompt that tells the model when to pause:
name: my-agent
version: "0.1.0"
network:
internal_port: 52222
artifacts:
- name: murmur/driver-anthropic
version: "0.19.0"
runtime: driver
gateway:
endpoint: https://api.anthropic.com
api_key: ${ANTHROPIC_API_KEY}
- name: murmur/tool-request-input
version: "0.7.0"
runtime: tool
inference:
transport: http
model: claude-sonnet-5
driver:
artifact: murmur/driver-anthropic
system_prompt: |
You are a deployment agent. When you reach a decision you cannot make on your own —
such as which environment to target or whether to proceed with a destructive step —
call tool-request-input with a clear, specific question for the operator.
name: my-agent
version: "0.1.0"
network:
internal_port: 52222
artifacts:
- name: murmur/driver-openai
version: "0.19.0"
runtime: driver
gateway:
endpoint: https://api.openai.com/v1
api_key: ${OPENAI_API_KEY}
- name: murmur/tool-request-input
version: "0.7.0"
runtime: tool
inference:
transport: http
model: o3-mini-high
driver:
artifact: murmur/driver-openai
system_prompt: |
You are a deployment agent. When you reach a decision you cannot make on your own —
such as which environment to target or whether to proceed with a destructive step —
call tool-request-input with a clear, specific question for the operator.
name: my-agent
version: "0.1.0"
network:
internal_port: 52222
artifacts:
- name: murmur/driver-deepseek
version: "0.17.0"
runtime: driver
gateway:
endpoint: https://api.deepseek.com
api_key: ${DEEPSEEK_API_KEY}
- name: murmur/tool-request-input
version: "0.7.0"
runtime: tool
inference:
transport: http
model: deepseek-r1
driver:
artifact: murmur/driver-deepseek
system_prompt: |
You are a deployment agent. When you reach a decision you cannot make on your own —
such as which environment to target or whether to proceed with a destructive step —
call tool-request-input with a clear, specific question for the operator.
network.internal_port pins the capsule to a fixed port on every run. Without it the runtime picks an OS-assigned port at startup, which changes between runs.
runtime: tool registers the WASM component with the capsule runtime. The model sees it as a callable tool named tool-request-input, the part of the artifact name after the /, with one parameter:
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | yes | The question to present to the operator |
The system prompt controls when the model pauses. A vague instruction ("ask when unsure") produces over-cautious behavior; a concrete rule ("call the tool before any destructive step") produces predictable escalation.
Step 2 — install dependencies
With murmur.yaml in place, fetch all declared artifacts:
mur install
Different ways to install artifacts
mur install fetches an artifact from the source of its scope. murmur/ artifacts come from
the built-in source, murmur-nexus/default-artifacts, with no configuration.
Option A — name the source of a scope in ~/.murmur/config.yaml:
registry:
sources:
- repo: <owner>/<repo>
token: "${GITHUB_TOKEN}"
Then install by full name, with or without a version:
mur install <owner>/<artifact-name>@<version>
Option B — pass a full GitHub reference and skip configuration entirely:
mur install github:<owner>/<repo>@<tag>
See Installing artifacts to learn more.
murmur/tool-request-input is a WASM artifact — no platform tag is required. mur install resolves the correct variant for every artifact in the manifest automatically.
Step 3 — run the capsule and send a task
Start the capsule:
mur run
murmur: url localhost:52222
session: ses_019ed2af53da75c2aefee84ee10c34af
Note the URL. Set a shell variable for convenience, replacing 52222 with the port you see:
PORT=52222
Send a task that will require a human decision:
curl -s -X POST http://localhost:$PORT \
-H "Content-Type: application/json" \
-H "A2A-Version: 1.0" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-001",
"role": "ROLE_USER",
"parts": [{"text": "Deploy the latest build. Confirm the target environment before proceeding."}]
},
"configuration": {"returnImmediately": true}
}
}'
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"task": {
"id": "tsk_01jw...",
"contextId": "ctx_01jw...",
"status": { "state": "TASK_STATE_SUBMITTED", "timestamp": "2026-10-13T09:00:00.123Z" }
}
}
}
Save the task id at result.task.id: you need it to detect the pause and to answer.
"returnImmediately": true has the door answer as soon as it accepts the task. Without it the door
holds the request until the task pauses or ends, and answers with the task as it stands then: here,
TASK_STATE_INPUT_REQUIRED with the agent's question in its prompt artifact, which saves the
polling in step 4. See Agent Card: Answers.
Step 4 — detect when the agent is waiting
Poll GetTask with the task ID until the state changes to TASK_STATE_INPUT_REQUIRED:
curl -s -X POST http://localhost:$PORT \
-H "Content-Type: application/json" \
-H "A2A-Version: 1.0" \
-d '{"jsonrpc":"2.0","id":2,"method":"GetTask","params":{"id":"<your_task_id>"}}'
While the agent is waiting, the response includes the question it formed:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"id": "tsk_01jw...",
"contextId": "ctx_01jw...",
"status": { "state": "TASK_STATE_INPUT_REQUIRED", "timestamp": "2026-10-13T09:00:02.841Z" },
"artifacts": [
{
"artifactId": "prompt",
"name": "prompt",
"parts": [
{
"text": "Which environment should I deploy to? (staging, production)",
"mediaType": "text/plain"
}
]
}
]
}
}
The agent's question is at result.artifacts[0].parts[0].text. Read it and decide your answer.
Step 5 — send your answer
Send a message to the same capsule URL, naming the waiting task in taskId:
curl -s -X POST http://localhost:$PORT \
-H "Content-Type: application/json" \
-H "A2A-Version: 1.0" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "SendMessage",
"params": {
"message": {
"messageId": "reply-001",
"taskId": "<your_task_id>",
"role": "ROLE_USER",
"parts": [{"text": "staging"}]
},
"configuration": {"returnImmediately": true}
}
}'
The answer is delivered directly to the suspended tool call. The agent receives "staging" as the tool result and the loop resumes immediately:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"task": {
"id": "tsk_01jw...",
"contextId": "ctx_01jw...",
"status": { "state": "TASK_STATE_WORKING", "timestamp": "2026-10-13T09:00:09.307Z" }
}
}
}
A message without taskId starts a new task instead, and never reaches the waiting one. A reply
naming a task that is not waiting is refused: see
Continuing a task.
Poll GetTask again until state reaches TASK_STATE_COMPLETED or TASK_STATE_FAILED.
Optional — set an input timeout
To fail the task automatically if no reply arrives within a deadline, add lifecycle.input_timeout_secs to the manifest:
lifecycle:
task_acceptance: single
input_timeout_secs: 300
When the deadline passes, the tool call fails and the attempt ends. The task then ends TASK_STATE_FAILED with status message "input-timeout", unless an on-task-end hook reopens it. Omit the field to wait indefinitely.
Summary
| Feature / setting | How it works |
|---|---|
murmur/tool-request-input |
WASM tool artifact; runtime: tool; platform-independent |
prompt parameter |
The question the model asks the operator; string, required |
| Task state while waiting | "TASK_STATE_INPUT_REQUIRED" |
| Where to read the question | result.artifacts[0].parts[0].text from GetTask |
| How to resume the agent | Send a message to the same capsule URL whose taskId names the waiting task |
| State after reply | "TASK_STATE_WORKING" immediately; poll until "TASK_STATE_COMPLETED" |
lifecycle.input_timeout_secs |
Integer seconds to wait for a reply; absent = wait indefinitely |