Harness spec 2.0.0: background work is a job — commands and subagents alike, seen and steered

One registry per process, every job owned by a session: bash's background commands (and a command
still running at its timeout, which now goes to the background instead of being killed) and
subagents started with task background: true. job_list, job_output, job_stop (the former
bash_list, bash_output, bash_kill) and the new job_send and report; a finished job wakes the owning
session in every front end with its marked note. The TUI counts jobs, lists them (/jobs), opens a
command's output, enters a subagent and messages it, moves a command to the background (ctrl+b)
and asks before quitting with jobs running. The link gains the jobs capability. Subagents get a
session row of their own, the spec's preamble and agent texts, and their errors reach the report.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
HomerandClaude Fable 5.1 committed 2026-10-20 16:17:00 +00:00
1 parent aaa3e3a44f
commit 2183040db8
75 files changed
+3614 -437

No files matched your search

+5 -5
View File
@@ -14,7 +14,7 @@
"argument_aliases": {
"workdir": "cwd"
},
"description": "Run a shell command (bash) and return its combined output and exit status.\n\n- Each call is a fresh shell: `cd` and variables do not carry over. Use `cwd` instead of `cd`.\n- Use the dedicated tools for files: read, not cat; edit/write, not sed or heredocs; grep and glob, not find.\n- Quote paths that contain spaces.\n- Commands time out (default {{timeout}}, `timeout` up to {{timeout_max}}).{{background}}\n- Git: do not commit, amend, push or change git config unless asked. Before a commit, check `git status` and `git diff`, stage files by name, never commit secrets.",
"description": "Run a shell command (bash) and return its combined output and exit status.\n\n- Each call is a fresh shell: `cd` and variables do not carry over. Use `cwd` instead of `cd`.\n- Use the dedicated tools for files: read, not cat; edit/write, not sed or heredocs; grep and glob, not find.\n- Quote paths that contain spaces.\n- A command still running when `timeout` passes (default {{timeout}}, up to {{timeout_max}}) is not killed: it goes on in the background as a job, and the call returns what it had printed with the job id.{{background}}\n- Git: do not commit, amend, push or change git config unless asked. Before a commit, check `git status` and `git diff`, stage files by name, never commit secrets.",
"parameters": {
"type": "object",
"properties": {
@@ -31,13 +31,13 @@
"type": "string"
},
"timeout": {
"description": "Milliseconds",
"description": "Milliseconds to wait for the command before it goes on in the background",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 600000
},
"background": {
"description": "Run in the background and return a job id at once",
"description": "Run in the background from the start and return a job id at once",
"type": "boolean"
}
},
@@ -46,8 +46,8 @@
]
},
"variables": {
"timeout": "how long a command may run when the call names no timeout, in words (\"2 minutes\")",
"timeout": "how long a command may run before it goes on in the background when the call names no timeout, in words (\"2 minutes\")",
"timeout_max": "the longest timeout a call may ask for, in words (\"10 minutes\")",
"background": "where commands may run in the background: \" For a server, a watcher or anything long, set `background: true`: it returns at once with a job id; read its output with bash_output, list jobs with bash_list, stop one with bash_kill.\" — otherwise empty, and the `background` parameter is not sent"
"background": "where commands may run in the background: \" For a server, a watcher or anything long, set `background: true`: it returns at once with a job id. Read a job's output with job_output, list jobs with job_list, stop one with job_stop; you are told when one ends.\" — otherwise empty, and the `background` parameter is not sent"
}
}
-19
View File
@@ -1,19 +0,0 @@
{
"name": "bash_list",
"scope": "cli",
"risk": "read",
"exclusive": false,
"block": "tool",
"purpose": false,
"aliases": {
"cli": [],
"llembas": [
"job_list"
]
},
"description": "The background jobs of this session: id, state, how long each has run, and its command.",
"parameters": {
"type": "object",
"properties": {}
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"name": "job_list",
"scope": "shared",
"risk": "read",
"exclusive": false,
"block": "tool",
"purpose": false,
"aliases": {
"cli": [
"bash_list"
],
"llembas": []
},
"description": "The background jobs of this session — commands started with bash `background: true` (or moved there when their timeout passed) and subagents started with task `background: true`: id, kind, state, how long each has run, whether it has output you have not read, and its command or description.",
"parameters": {
"type": "object",
"properties": {}
}
}
@@ -1,17 +1,17 @@
{
"name": "bash_output",
"scope": "cli",
"name": "job_output",
"scope": "shared",
"risk": "read",
"exclusive": false,
"block": "shell",
"block": "tool",
"purpose": false,
"aliases": {
"cli": [],
"llembas": [
"job_output"
]
"cli": [
"bash_output"
],
"llembas": []
},
"description": "New output from a background job since you last looked, and whether it is still running. `wait` (seconds, up to 60) waits for more first.",
"description": "New output from a background job since you last looked, and whether it is still running: a command's output; a subagent's progress — its tool calls and what it has written so far. `wait` (seconds, up to 60) waits for more first.",
"parameters": {
"type": "object",
"properties": {
+29
View File
@@ -0,0 +1,29 @@
{
"name": "job_send",
"scope": "shared",
"risk": "interact",
"exclusive": false,
"block": "tool",
"purpose": false,
"aliases": {
"cli": [],
"llembas": []
},
"description": "Send a message to a subagent running as a background job — an answer to something it reported, a correction, more to do. It is given to it at its next step. To a subagent that has finished, the message starts it again with its context intact, under the same job id; its new report comes to you when it ends.",
"parameters": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string",
"minLength": 1
}
},
"required": [
"id",
"text"
]
}
}
@@ -1,17 +1,17 @@
{
"name": "bash_kill",
"scope": "cli",
"name": "job_stop",
"scope": "shared",
"risk": "execute",
"exclusive": false,
"block": "tool",
"purpose": false,
"aliases": {
"cli": [],
"llembas": [
"job_stop"
]
"cli": [
"bash_kill"
],
"llembas": []
},
"description": "Stop a background job started with bash background: true.",
"description": "Stop a background job. A command is terminated; a subagent is stopped where it is, and what it had worked out comes to you as its report.",
"parameters": {
"type": "object",
"properties": {
+26
View File
@@ -0,0 +1,26 @@
{
"name": "report",
"scope": "shared",
"risk": "interact",
"exclusive": false,
"block": "tool",
"purpose": false,
"aliases": {
"cli": [],
"llembas": []
},
"offered": "subagent",
"description": "Send a message to the main agent now, before your final reply: a finding it should act on at once, or a question you cannot settle yourself. It reaches the main agent as a message; an answer, if one comes, arrives as a message to you. Your final reply still goes to it when you finish.",
"parameters": {
"type": "object",
"properties": {
"text": {
"type": "string",
"minLength": 1
}
},
"required": [
"text"
]
}
}
+5 -1
View File
@@ -11,7 +11,7 @@
"subagent_run"
]
},
"description": "Hand a self-contained piece of work to a subagent, which works on its own and returns only its final report — keeping the searching and reading out of your context.\n\n- Use it for broad research (\"where is X handled, and what calls it?\") or a separable job. For one file you already know, just read it.\n- `prompt` must stand on its own: the subagent sees nothing of this conversation. Say what to find or do, and what to report back.\n- Independent tasks can run in the same turn. Subagents that change files at the same time should each have `worktree: true`, so they work on separate branches.\n- Agents: {{agents}}",
"description": "Hand a self-contained piece of work to a subagent, which works on its own and returns only its final report — keeping the searching and reading out of your context.\n\n- Use it for broad research (\"where is X handled, and what calls it?\") or a separable job. For one file you already know, just read it.\n- `prompt` must stand on its own: the subagent sees nothing of this conversation. Say what to find or do, and what to report back.\n- `background: true` returns at once with a job id and you carry on; the report comes to you as a message when it ends. Meanwhile job_output shows its progress, job_send gives it a message, job_stop stops it. Without it, the call waits for the report.\n- Independent tasks can run in the same turn. Subagents that change files at the same time should each have `worktree: true`, so they work on separate branches.\n- Agents: {{agents}}",
"parameters": {
"type": "object",
"properties": {
@@ -32,6 +32,10 @@
"description": "Work in a separate git checkout on a new branch, apart from yours — for changes made alongside other work; the result says how to merge it",
"type": "boolean"
},
"background": {
"description": "Run it as a background job: return its id at once, get its report as a message when it ends",
"type": "boolean"
},
"model": {
"description": "Run it on another configured model (connection/model, from the list in your instructions) — a second opinion, or a model better at this kind of work. Default: the agent's own model, else yours.",
"type": "string"