stricter checks
CI / verify (pull_request) Successful in 12s

This commit is contained in:
2026-10-10 15:16:53 +02:00
parent ced43fbb11
commit 513d51f702
30 changed files with 984 additions and 470 deletions
+32 -32
View File
@@ -10,6 +10,7 @@ import { z } from "zod";
import { randomUUID } from "node:crypto";
import { Herdr, rootSchema } from "./herdr.js";
import { multiWorkGuide } from "../agents/routing.js";
import { matches } from "../agents/wildcard.js";
export const bindingSchema = z
.object({
@@ -62,14 +63,8 @@ const readTools = new Set([
export const childProtocol =
"Bound child sessions use stanp-opencode multi-work protocol 1 and must remain in their assigned worktree.";
function matches(value: string, pattern: string) {
return new RegExp(
`^${pattern
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*")
.replace(/\?/g, ".")}$`,
"s",
).test(value);
function present(value: string | undefined): value is string {
return value !== undefined && value !== "";
}
export const installMultiWork = (
@@ -158,6 +153,20 @@ export const installMultiWork = (
return binding;
});
const verifyLocalChild = (
sessionID: Session.ID,
parent: { id: string; location: { directory: string } },
) =>
Effect.gen(function* () {
const child = yield* context.session.get({ sessionID });
if (
child.parentID !== parent.id ||
child.agent !== "fast-lo" ||
child.location.directory !== parent.location.directory
)
throw new Error("Local fast-lo child binding changed");
});
// Commands can create children without going through the subagent tool.
yield* context.session.hook("prompt", (event) =>
Effect.gen(function* () {
@@ -192,12 +201,12 @@ export const installMultiWork = (
if (event.agent === "coordinate" && session.parentID)
yield* validate(event.sessionID);
if (event.agent !== "multi-work") {
const tool = event.tools.subagent;
const tool = event.tools["subagent"];
if (tool) {
const properties = z
.record(z.string(), z.unknown())
.parse(tool.input.properties ?? {});
delete properties.worktree;
.parse(tool.input["properties"] ?? {});
delete properties["worktree"];
tool.input = { ...tool.input, properties };
}
return;
@@ -211,13 +220,13 @@ export const installMultiWork = (
);
if (!root)
throw new Error("multi-work requires a live, unambiguous Herdr root checkout");
const tool = event.tools.subagent;
const tool = event.tools["subagent"];
if (tool) {
tool.input = z.toJSONSchema(inputSchema);
const properties = z
.record(z.string(), z.unknown())
.parse(tool.input.properties);
tool.input.properties = {
.parse(tool.input["properties"]);
tool.input["properties"] = {
...properties,
agent: { type: "string", enum: ["fast-lo", "coordinate"] },
};
@@ -238,7 +247,7 @@ export const installMultiWork = (
if (input.agent === "multi-work")
throw new Error("multi-work is primary only");
if (
input.sessionID &&
present(input.sessionID) &&
(yield* context.storage.get(bindingKey(input.sessionID))) !==
undefined &&
(call.agent !== "multi-work" || input.agent !== "coordinate")
@@ -261,22 +270,13 @@ export const installMultiWork = (
);
if (input.agent === "fast-lo") {
if (input.worktree) throw new Error("fast-lo is local retrieval only");
if (input.sessionID) {
const child = yield* context.session.get({
sessionID: Session.ID.make(input.sessionID),
});
if (
child.parentID !== parent.id ||
child.agent !== "fast-lo" ||
child.location.directory !== parent.location.directory
)
throw new Error("Local fast-lo child binding changed");
}
if (present(input.sessionID))
yield* verifyLocalChild(Session.ID.make(input.sessionID), parent);
return yield* native.execute(input, call);
}
if (input.agent !== "coordinate")
throw new Error("multi-work delegates only to fast-lo or coordinate");
if (input.sessionID && input.worktree !== undefined)
if (present(input.sessionID) && input.worktree !== undefined)
throw new Error(
"Coordinate resumes require only sessionID; worktree is create-only",
);
@@ -287,7 +287,7 @@ export const installMultiWork = (
return yield* Effect.gen(function* () {
let initialized = false;
let placement: z.infer<typeof bindingSchema> | undefined;
if (input.sessionID) {
if (present(input.sessionID)) {
const binding = yield* validate(Session.ID.make(input.sessionID));
if (binding.owner !== call.sessionID)
throw new Error("Cannot resume another parent's binding");
@@ -336,7 +336,7 @@ export const installMultiWork = (
}
const { worktree, ...args } = input;
const callKey = `multi-work-call:${call.sessionID}:${call.id}`;
if (!input.sessionID) {
if (!present(input.sessionID)) {
const previous = yield* context.storage.get(callKey);
if (previous !== undefined)
throw new Error(
@@ -350,7 +350,7 @@ export const installMultiWork = (
// Native 2.0.22 awaits this update after creation and before prompt admission.
yield* call.progress(metadata);
if (initialized) return;
const id = z.string().parse(metadata.sessionID);
const id = z.string().parse(metadata["sessionID"]);
if (busy.has(id))
throw new Error(
"This child is already being delegated",
@@ -433,7 +433,7 @@ export const installMultiWork = (
coordinator?.mode === "all" &&
coordinator.system?.includes(
childProtocol,
) &&
) === true &&
[
"coordinate",
"fast-lo",
@@ -503,7 +503,7 @@ export const installMultiWork = (
Effect.ensuring(
Effect.sync(() => {
busy.delete(lock);
if (childLock) busy.delete(childLock);
if (present(childLock)) busy.delete(childLock);
}),
),
);