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

+7 -3
View File
@@ -128,13 +128,15 @@ test("prompts/index.json lists every prompt text, each with a scope", () => {
test("the execution tools are the CLI's alone; the web UI runs no agent", () => {
// LLeMbas removed its SSH workplace, so nothing that runs a command or touches a file is shared.
const exec = ["apply_patch", "bash", "bash_kill", "bash_list", "bash_output", "edit", "glob", "grep", "list", "multiedit", "read", "write"]
const exec = ["apply_patch", "bash", "edit", "glob", "grep", "list", "multiedit", "read", "write"]
for (const n of exec) expect(TOOL_SPECS[n]!.scope).toBe("cli")
// The plan is one structure both keep (spec 1.1.0): its three tools are shared, and todo is
// plan_write's former name rather than a tool.
expect(TOOL_SPECS.todo).toBeUndefined()
expect(TOOL_SPECS.plan_write!.aliases.cli).toEqual(["todo"])
const shared = ["ask_user", "knowledge_get", "knowledge_search", "memory", "note_manage", "note_view", "notes_search", "plan_submit", "plan_update", "plan_write", "skill_manage", "skill_view", "skills_list", "task", "web_fetch", "web_search"]
// Spec 2.0.0: the job tools cover both kinds of background work and are shared (the CLI's former
// bash_* names are their aliases); report is a subagent's.
const shared = ["ask_user", "job_list", "job_output", "job_send", "job_stop", "knowledge_get", "knowledge_search", "memory", "note_manage", "note_view", "notes_search", "plan_submit", "plan_update", "plan_write", "report", "skill_manage", "skill_view", "skills_list", "task", "web_fetch", "web_search"]
for (const n of shared) expect(TOOL_SPECS[n]!.scope).toBe("shared")
expect(Object.values(TOOL_SPECS).filter((t) => t.scope === "shared").map((t) => t.name).sort()).toEqual(shared.sort())
})
@@ -180,7 +182,7 @@ test("every {{variable}} in a description is declared, and none reaches a model"
expect(JSON.stringify(toSpec(t).parameters)).not.toContain("{{")
}
const bash = app.engine.o.tools.find((t) => t.name === "bash")!
expect(bash.description).toContain("default 90 seconds, `timeout` up to 10 minutes). For a server")
expect(bash.description).toContain("default 90 seconds, up to 10 minutes) is not killed: it goes on in the background as a job, and the call returns what it had printed with the job id. For a server")
expect(PURPOSE_DEF.schema).toEqual({ type: "string", description: expect.stringContaining("what this call is for") })
// Nor through the assembled system prompt, in any mode, with or without a plan, with the
// instance's pieces or without them; the CLI's facts are in it and the bullets LLeMbas alone
@@ -194,6 +196,8 @@ test("every {{variable}} in a description is declared, and none reaches a model"
expect(system).toContain("- Your output is shown in a terminal and rendered as GitHub-flavoured markdown in a monospace font. Be concise")
expect(system).toContain("- Use the dedicated tools for files: read (not cat)")
expect(system).toContain("- A long-running command (a server, a watcher) runs with bash `background: true`")
expect(system).toContain("- For work with three or more steps, write a plan with plan_write")
expect(system).toContain("- When a decision is genuinely the user's")
expect(system).toContain("# Git\n- How git is used in this project")
expect(system).not.toContain("- \n")
if (p) expect(system).toContain("- ### The current plan\n\n# T\n## p1 P (pending)\n[t1] [ ] a\n\nThis is the plan as it stands now. Work to it, and quote the ids above rather than restating the plan in your answer.")