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:
75 files changed
+3614
-437
No files matched your search
+21
-1
@@ -15,6 +15,7 @@ import { commandSet, sourcesOf } from "../session/commands.ts"
|
||||
import type { PlanReply, PlanRequest } from "../tool/plan_submit.ts"
|
||||
import type { QuestionReply, QuestionRequest } from "../tool/ask_user.ts"
|
||||
import { clampMode, effortOf, markRemote, permissionParams, planParams, planReplyFrom, questionParams, questionReplyFrom, replyFrom, STOP, type PromptResult } from "./agent.ts"
|
||||
import { jobList, jobOutput, jobSend, jobStop, jobTranscript } from "./jobs.ts"
|
||||
import type { SessionContext } from "../app.ts"
|
||||
import { sessionTurnFile, sessionTurns, type TurnLog } from "./turns.ts"
|
||||
import { dropAttachments, preparePrompt } from "./prompt.ts"
|
||||
@@ -501,7 +502,26 @@ export class Share {
|
||||
// The terminal's turns: `_lembas/session/status` for a shared session asks here.
|
||||
peer.handle("hub/status", (p) => {
|
||||
this.mine(p)
|
||||
return { ...(this.turns.running ? { turnId: this.turns.running } : {}), queued: this.turns.queued(), ...(this.turns.last ? { lastTurnId: this.turns.last } : {}), plan: this.app.engine.plan ?? null }
|
||||
return { ...(this.turns.running ? { turnId: this.turns.running } : {}), queued: this.turns.queued(), ...(this.turns.last ? { lastTurnId: this.turns.last } : {}), plan: this.app.engine.plan ?? null, jobs: jobList(this.app) }
|
||||
})
|
||||
// The session's jobs (capability `jobs`): `_lembas/jobs` and `_lembas/job/*` for a session
|
||||
// this terminal has open are answered here, by the same code the agent uses for its own.
|
||||
peer.handle("hub/job", async (p) => {
|
||||
this.mine(p)
|
||||
switch (p?.op) {
|
||||
case "list":
|
||||
return { jobs: jobList(this.app) }
|
||||
case "output":
|
||||
return jobOutput(this.app, p?.id, p?.since)
|
||||
case "stop":
|
||||
return jobStop(this.app, p?.id)
|
||||
case "send":
|
||||
return jobSend(this.app, p?.id, p?.text)
|
||||
case "transcript":
|
||||
return jobTranscript(this.app, p?.id)
|
||||
default:
|
||||
throw new RpcError(INVALID_PARAMS, `no such job operation: ${String(p?.op ?? "")}`)
|
||||
}
|
||||
})
|
||||
// The web UI is editing or regenerating from a turn: that turn and the later ones go, as /undo
|
||||
// drops one — the files stay.
|
||||
|
||||
Reference in new issue
Block a user