feat: multi-work is able to delegate to root
CI / verify (pull_request) Successful in 9s

This commit is contained in:
2026-09-18 20:13:54 +02:00
parent 8b6836f5a4
commit 52e98c9d78
9 changed files with 376 additions and 88 deletions
+2 -2
View File
@@ -320,7 +320,7 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
if (agent === "multi-work") throw new Error("multi-work is primary only");
if (agent === "coordinate" && event.agent !== "multi-work")
throw new Error(
"coordinate cannot run as a subagent without a ready worktree binding",
"coordinate cannot run as a subagent without a ready multi-work binding",
);
if (workerNames.some((name) => name === event.agent))
throw new Error("Workers cannot delegate tasks");
@@ -349,7 +349,7 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
parent.location.directory !== binding.data.root.directory
)
throw new Error(
"coordinate cannot run as a subagent without a ready worktree binding",
"coordinate cannot run as a subagent without a ready multi-work binding",
);
}
if (
+87 -51
View File
@@ -9,16 +9,24 @@ import { randomUUID } from "node:crypto";
import { Herdr, rootSchema } from "./herdr.js";
import { multiWorkGuide } from "./routing.js";
export const bindingSchema = z.object({
owner: z.string(),
child: z.string(),
projectID: z.string(),
root: rootSchema,
directory: z.string(),
workspaceID: z.string(),
branch: z.string(),
ready: z.boolean(),
});
export const bindingSchema = z
.object({
owner: z.string(),
child: z.string(),
projectID: z.string(),
root: rootSchema,
directory: z.string(),
workspaceID: z.string(),
branch: z.string(),
ready: z.boolean(),
})
.refine(
(binding) =>
binding.directory === binding.root.directory
? binding.workspaceID === binding.root.workspaceID && binding.branch === "root"
: binding.workspaceID !== binding.root.workspaceID,
{ message: "Invalid multi-work placement metadata" },
);
export const bindingKey = (id: string) => `multi-work:${id}`;
const inputSchema = z
.object({
@@ -98,7 +106,7 @@ export const installMultiWork = (
agent.name = Agent.Name.make("multi-work");
agent.mode = "primary";
agent.description =
"Coordinate independent tasks in Herdr worktrees; retrieve local facts with fast-lo.";
"Coordinate independent tasks locally or in Herdr worktrees; retrieve local facts with fast-lo.";
agent.system = multiWorkGuide;
agent.permissions = [
...coordinate.permissions.filter((rule) => rule.action !== "subagent"),
@@ -137,14 +145,14 @@ export const installMultiWork = (
child.projectID !== binding.projectID ||
child.location.directory !== binding.directory
)
throw new Error("Coordinate child does not match its ready worktree binding");
throw new Error("Coordinate child does not match its ready multi-work binding");
const parent = yield* context.session.get({ sessionID: child.parentID });
if (
parent.agent !== "multi-work" ||
parent.projectID !== binding.projectID ||
parent.location.directory !== binding.root.directory
)
throw new Error("Worktree owner changed agent or location");
throw new Error("Binding owner changed agent or location");
return binding;
});
@@ -184,7 +192,7 @@ export const installMultiWork = (
agent: { type: "string", enum: ["fast-lo", "coordinate"] },
};
tool.description =
"Delegate to fast-lo locally, or coordinate in a new Herdr worktree using worktree: {base, branch?, label?}. Prefer descriptive human-readable branch and label values, for example {base:'main',branch:'fix/login-timeout',label:'Fix login timeout'}. Continue an owned coordinate child with sessionID and no worktree argument; naming fields are create-only. Native background and results apply.";
"Delegate to fast-lo for retrieval, or coordinate in this root checkout by omitting worktree. To isolate new coordinate work, use worktree: {base, branch?, label?}. Continue an owned coordinate child with sessionID and no worktree argument. Native background and results apply.";
}
}).pipe(Effect.orDie),
);
@@ -208,9 +216,7 @@ export const installMultiWork = (
throw new Error("A bound coordinate child cannot be repurposed");
if (call.agent !== "multi-work") {
if (input.worktree || input.agent === "coordinate")
throw new Error(
"Only multi-work may initialize coordinate worktrees",
);
throw new Error("Only multi-work may delegate to coordinate");
return yield* native.execute(input, call);
}
const parent = yield* context.session.get({ sessionID: call.sessionID });
@@ -240,29 +246,9 @@ export const installMultiWork = (
}
if (input.agent !== "coordinate")
throw new Error("multi-work delegates only to fast-lo or coordinate");
if (
input.sessionID
? input.worktree !== undefined
: input.worktree === undefined
)
if (input.sessionID && input.worktree !== undefined)
throw new Error(
"New coordinate work requires worktree: {base}; resumes require only sessionID",
);
const { data: agent } = yield* context.agent.get({
agentID: call.agent,
location: { directory: parent.location.directory },
});
const permission = [
...agent.permissions,
...(parent.permissions ?? []),
].findLast(
(rule) =>
matches("herdr_worktree", rule.action) &&
matches(root.directory, rule.resource),
)?.effect;
if (permission !== "allow")
throw new Error(
`herdr_worktree requires explicit allow; custom approval is unavailable (${permission ?? "ask"})`,
"Coordinate resumes require only sessionID; worktree is create-only",
);
const lock = input.sessionID ?? `${call.sessionID}:${call.id}`;
if (busy.has(lock)) throw new Error("This delegation is already active");
@@ -274,17 +260,48 @@ export const installMultiWork = (
if (input.sessionID) {
const binding = yield* validate(Session.ID.make(input.sessionID));
if (binding.owner !== call.sessionID)
throw new Error("Cannot resume another parent's worktree");
yield* Effect.tryPromise((signal) =>
herdr.verify(
binding.directory,
binding.workspaceID,
binding.root,
binding.branch,
signal,
),
);
throw new Error("Cannot resume another parent's binding");
placement = binding;
}
if (
input.worktree ||
(placement && placement.directory !== placement.root.directory)
) {
const { data: agent } = yield* context.agent.get({
agentID: call.agent,
location: { directory: parent.location.directory },
});
const permission = [
...agent.permissions,
...(parent.permissions ?? []),
].findLast(
(rule) =>
matches("herdr_worktree", rule.action) &&
matches(root.directory, rule.resource),
)?.effect;
if (permission !== "allow")
throw new Error(
`herdr_worktree requires explicit allow; custom approval is unavailable (${permission ?? "ask"})`,
);
}
if (placement) {
const binding = placement;
if (
root.directory !== binding.root.directory ||
root.workspaceID !== binding.root.workspaceID ||
root.repo !== binding.root.repo
)
throw new Error("Bound Herdr root is no longer available");
if (binding.directory !== binding.root.directory)
yield* Effect.tryPromise((signal) =>
herdr.verify(
binding.directory,
binding.workspaceID,
binding.root,
binding.branch,
signal,
),
);
initialized = true;
}
const { worktree, ...args } = input;
@@ -310,8 +327,6 @@ export const installMultiWork = (
);
childLock = id;
busy.add(id);
if (!worktree)
throw new Error("Missing worktree initialization");
const sessionID = Session.ID.make(id);
const child = yield* context.session.get({ sessionID });
if (
@@ -323,6 +338,27 @@ export const installMultiWork = (
throw new Error(
"Native executor returned an unexpected child",
);
if (!worktree) {
const binding = {
owner: call.sessionID,
child: id,
projectID: parent.projectID,
root,
directory: root.directory,
workspaceID: root.workspaceID,
branch: "root",
ready: true,
};
yield* context.storage.set(callKey, {
child: id,
branch: binding.branch,
root,
});
yield* context.storage.set(bindingKey(id), binding);
placement = binding;
initialized = true;
return;
}
const branch =
worktree.branch ?? `multi-work/${randomUUID()}`;
yield* context.storage.set(callKey, {
+6 -4
View File
@@ -16,15 +16,17 @@ export const descriptions = {
export const multiWorkGuide = `You are multi-work, a primary orchestrator in a verified Herdr root checkout.
Read, plan, track todos and delegate; never implement or execute shell commands yourself.
Use native subagent with agent fast-lo for focused local retrieval. Use agent coordinate
for work in a new Herdr worktree, supplying descriptive human-readable names such as
without worktree to edit concurrently in this root checkout, or isolate work in a new
Herdr worktree by supplying descriptive human-readable names such as
worktree: {base: "main", branch: "fix/login-timeout", label: "Fix login timeout"}, plus
description and a complete prompt with scope, constraints and acceptance checks. The tool
uses the requested branch and Herdr workspace label; when omitted, it creates the prior unique
uses the requested branch and Herdr workspace label; when the branch is omitted, it creates the prior unique
multi-work/<uuid> branch and lets Herdr choose its default label. It moves the coordinate child
before its task begins, and preserves your root session. Dirty root changes are not copied.
Resume an owned coordinate child using its sessionID and no worktree argument. Never switch
its agent or location; naming fields are create-only. Only fast-lo and coordinate are permitted. Workers are leaves.
Parallelize independent work with disjoint ownership; serialize dependencies. Native background
its agent or assigned checkout; naming fields are create-only. Only fast-lo and coordinate are permitted. Workers are leaves.
Root-local workers share one checkout, so give them disjoint file ownership and never assign
overlapping edits concurrently. Parallelize independent work; serialize dependencies. Native background
delegation returns immediately and notifies you when complete; do not poll or duplicate work.
Reconcile results and report checkout, branch and child IDs. Retained resources on failure
must not be deleted automatically. Never retry ambiguous creation failures blindly.
+1 -1
View File
@@ -80,7 +80,7 @@ export default Plugin.define({
return (
<Show when={status()}>
<Show when={props.centered} fallback={<Content />}>
<box width="100%" alignItems="center">
<box position="absolute" top={2} left={0} width="100%" alignItems="center">
<Content />
</box>
</Show>