Let task agents use client Macs directly: bash, copy and hands

This commit is contained in:
Ian Gulliver
2026-10-17 23:45:31 -07:00
parent 8d9ba10849
commit 9b79ade073
25 changed files with 1382 additions and 952 deletions
+51 -45
View File
@@ -911,41 +911,29 @@ paths:
schema: { $ref: "#/components/schemas/InboxItem" }
"404": { $ref: "#/components/responses/Error" }
/api/inbox/{itemId}/bundle:
parameters:
- { name: itemId, in: path, required: true, schema: { type: string } }
/api/clients/connect:
get:
operationId: getRunBundle
description: A client_run item's files, as a gzipped tar of its paths relative to the worktree. Served while the item is open.
operationId: connectClient
description: >
A WebSocket over which task agents use this Mac: the server sends ClientRequest text messages and the client
answers each with a ClientReply text message carrying the same id, in any order. The Byre-Host header names
the client; a newer connection from the same host replaces the older one.
responses:
"101":
description: Switched to the WebSocket protocol.
"400": { $ref: "#/components/responses/Error" }
/api/clients/transfers/{transferId}:
parameters:
- { name: transferId, in: path, required: true, schema: { type: string } }
get:
operationId: getClientTransfer
description: The file or directory a copy ClientRequest sends, as a gzipped tar holding it under its own name. Served while the request is waiting for its reply.
responses:
"200":
description: The bundle.
description: The archive.
content:
application/gzip: { schema: { type: string, format: binary } }
"400": { $ref: "#/components/responses/Error" }
"404": { $ref: "#/components/responses/Error" }
"409": { $ref: "#/components/responses/Error" }
/api/inbox/{itemId}/runs:
parameters:
- { name: itemId, in: path, required: true, schema: { type: string } }
post:
operationId: reportRun
description: >
Records one Mac's run of a client_run item as a run response, sent to the agent like an answer.
The item stays as it is, so other Macs can run it too; it is accepted after the item closes.
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/RunReport" }
responses:
"200":
description: The item with its responses.
content:
application/json:
schema: { $ref: "#/components/schemas/InboxItem" }
"400": { $ref: "#/components/responses/Error" }
"404": { $ref: "#/components/responses/Error" }
/api/inbox/responses:
@@ -1540,7 +1528,7 @@ components:
task_id: { type: string }
agent_session_id: { type: string }
thread_id: { type: string }
kind: { type: string, enum: [question, session_error, rate_limited, client_run] }
kind: { type: string, enum: [question, session_error, rate_limited] }
payload:
type: object
description: >
@@ -1548,7 +1536,6 @@ components:
question, why_blocked, options (each choice's text, in order), multiple (true for pick-any), recommended (the marked choices' text), attachments.
session_error: error.
rate_limited: vendor, rate_limit (the vendor's rate-limit status), error.
client_run: why, command, paths (worktree paths in the bundle), bytes (the bundle's size).
state: { $ref: "#/components/schemas/InboxState" }
created_at: { type: string, format: date-time }
resolved_by: { type: string }
@@ -1559,7 +1546,7 @@ components:
InboxResponseType:
type: string
enum: [option, other, dismiss, run]
enum: [option, other, dismiss]
InboxResponse:
type: object
@@ -1572,9 +1559,7 @@ components:
options:
type: array
items: { type: string }
text: { type: string, description: "run: the command's output, at most its last 64 KiB." }
client: { type: string, description: "run: the host name of the Mac that ran the command." }
exit_code: { type: integer, description: "run: the command's exit status." }
text: { type: string }
images:
type: array
items: { type: string }
@@ -1604,15 +1589,6 @@ components:
type: array
items: { $ref: "#/components/schemas/Attachment" }
RunReport:
type: object
required: [run_id, client, exit_code, output]
properties:
run_id: { type: string, description: "The Mac's id for this one run, used as the response's id; reporting it again records nothing new." }
client: { type: string }
exit_code: { type: integer }
output: { type: string }
InboxRespond:
type: object
required: [responses]
@@ -1736,6 +1712,36 @@ components:
connected_at: { type: string, format: date-time, description: When the client's oldest open stream connected. }
last_action_at: { type: string, format: date-time, description: When byre last received a request the human made from this client since byre started; absent when none. }
ClientRequest:
type: object
required: [id, op]
description: Work a task agent asks a connected Mac to do, sent over /api/clients/connect.
properties:
id: { type: string }
op: { type: string, enum: [bash, copy, hands] }
command: { type: string, description: "bash: run with zsh as a login shell in the home directory, in its own process group." }
timeout_ms: { type: integer, description: "bash: kill the process group after this long." }
transfer: { type: string, description: "copy: the transfer id to download from /api/clients/transfers/{transferId}." }
dest: { type: string, description: "copy: where the archive's one entry ends up, absolute or starting with ~/; whatever is there is replaced." }
tool: { type: string, description: "hands: the byre-hands tool to call." }
arguments:
type: object
additionalProperties: true
description: "hands: the tool's arguments."
ClientReply:
type: object
required: [id]
properties:
id: { type: string, description: The request's id. }
error: { type: string, description: Set when the request couldn't be carried out. }
exit_code: { type: integer, description: "bash: the exit status, -1 when it was killed by its timeout." }
output: { type: string, description: "bash: stdout and stderr together, at most their last 64 KiB." }
result:
type: object
additionalProperties: true
description: "hands: the tool's MCP CallToolResult."
TurnRef:
type: object
required: [session_id, task_id, vendor]