bus: tester identity for PM tests (row 110, #1604)
A tester binds with a process proof from the bus's own tester-cli.mjs when the business var bus.tester is true. It may send [mos test] messages to a role, receive replies on the tester address and use the read verbs; every other verb refuses. Messages store from_role tester and write no human.input event. turnRequest() marks them as never Jason's instruction. human.mjs is unchanged. Candidate manifest bdaf7f476abd0b2b5095ab2f7f43e38540e6ba48ee14a02a4f4995a1469b79df (Darkwing round 2, packet9f1563ec1), BASEbadfa52b9. Approved by Filbert (comment 28360) and Sage (comment 28361). Sage gate rerun passed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+26
-17
@@ -15,7 +15,7 @@ import { fileURLToPath } from "node:url";
|
||||
import { BINDING_NAME } from "../../discord/src/binding.mjs";
|
||||
import { CliError } from "./errors.mjs";
|
||||
import { bootConfig, loadSystem, socketPath } from "./config.mjs";
|
||||
import { humanTransport, refuseInsideAgent } from "./transport.mjs";
|
||||
import { humanTransport, refuseInsideAgent, testerTransport } from "./transport.mjs";
|
||||
import { formatAgents, formatDecision, formatInbox, formatTasks, formatTrail, shortId } from "./format.mjs";
|
||||
import { hostStatus, readHostState, startHost, stopHost } from "./host.mjs";
|
||||
import { createLauncher } from "./launcher.mjs";
|
||||
@@ -24,13 +24,13 @@ import { readSessions, stopSession } from "../../seat/src/session.mjs";
|
||||
import { SeatError } from "../../seat/src/seat.mjs";
|
||||
|
||||
export const USAGE = `usage:
|
||||
mosaic inbox [--business <id>] [--json]
|
||||
mosaic inbox [--business <id>] [--json] [--tester]
|
||||
mosaic decide <decision> <option> [--note <text>] [--yes] [--business <id>]
|
||||
mosaic tasks [--business <id>] [--json]
|
||||
mosaic agents [--business <id>] [--json]
|
||||
mosaic trail <task|decision> [--business <id>] [--json]
|
||||
mosaic talk <role> <text> [--wait <seconds> | --follow] [--business <id>]
|
||||
mosaic follow [--business <id>]
|
||||
mosaic tasks [--business <id>] [--json] [--tester]
|
||||
mosaic agents [--business <id>] [--json] [--tester]
|
||||
mosaic trail <task|decision> [--business <id>] [--json] [--tester]
|
||||
mosaic talk <role> <text> [--wait <seconds> | --follow] [--business <id>] [--tester]
|
||||
mosaic follow [--business <id>] [--tester]
|
||||
mosaic stop <run>
|
||||
mosaic launches off|on [--business <id>]
|
||||
mosaic launches list [--json]
|
||||
@@ -155,13 +155,22 @@ export async function resolveDecision({ call, ref, choice, note, write, ask }) {
|
||||
return d;
|
||||
}
|
||||
|
||||
async function human(verb, rest, io, deps) {
|
||||
const spec = verb === "decide" ? { flags: ["--yes"], values: ["--business", "--note"] } : { flags: ["--json"], values: ["--business"] };
|
||||
const parsed = parse(rest, spec);
|
||||
refuseInsideAgent(io.env);
|
||||
// The bus call for one command. --tester (row 110) binds as the business's
|
||||
// tester through tester-cli.mjs instead of as the human, and may run inside
|
||||
// an agent; the broker refuses it under a managed run or with bus.tester off.
|
||||
function callFor(parsed, io, deps) {
|
||||
const tester = parsed.flags.has("--tester");
|
||||
if (!tester) refuseInsideAgent(io.env);
|
||||
const system = deps.system ?? loadSystem({ env: io.env });
|
||||
const business = businessFor(parsed, system.dataRoot);
|
||||
const call = deps.transport ? deps.transport(business) : humanTransport({ socket: socketPath(system.dataRoot), business, env: io.env });
|
||||
if (tester) return deps.testerTransport ? deps.testerTransport(business) : testerTransport({ socket: socketPath(system.dataRoot), business, env: io.env, spawn: deps.testerSpawn });
|
||||
return deps.transport ? deps.transport(business) : humanTransport({ socket: socketPath(system.dataRoot), business, env: io.env });
|
||||
}
|
||||
|
||||
async function human(verb, rest, io, deps) {
|
||||
const spec = verb === "decide" ? { flags: ["--yes"], values: ["--business", "--note"] } : { flags: ["--json", "--tester"], values: ["--business"] };
|
||||
const parsed = parse(rest, spec);
|
||||
const call = callFor(parsed, io, deps);
|
||||
if (verb === "decide") return decide(parsed, call, io);
|
||||
const json = parsed.flags.has("--json");
|
||||
const print = (value, text) => io.stdout.write(json ? `${JSON.stringify(value, null, 2)}\n` : text(value));
|
||||
@@ -189,6 +198,7 @@ async function human(verb, rest, io, deps) {
|
||||
}
|
||||
|
||||
const TALK_WAIT_S = 900;
|
||||
const TESTER_PREFIX = "[mos test]";
|
||||
const TALK_POLL_MS = 2000;
|
||||
|
||||
function formatMessage(m) {
|
||||
@@ -200,18 +210,17 @@ function formatMessage(m) {
|
||||
// until the reply arrives or --wait runs out. --follow keeps receiving after
|
||||
// that reply; follow starts the same receive loop without sending.
|
||||
async function talk(rest, io, deps, receiveOnly = false) {
|
||||
const parsed = parse(rest, { flags: ["--follow"], values: ["--business", ...(receiveOnly ? [] : ["--wait"])] });
|
||||
const parsed = parse(rest, { flags: ["--follow", "--tester"], values: ["--business", ...(receiveOnly ? [] : ["--wait"])] });
|
||||
if (parsed.positional.length !== (receiveOnly ? 0 : 2)) throw usage(receiveOnly ? "follow takes no arguments" : "talk takes a role and one quoted text");
|
||||
if (receiveOnly && parsed.flags.has("--follow")) throw usage("follow already follows");
|
||||
const follow = receiveOnly || parsed.flags.has("--follow");
|
||||
if (follow && parsed.values["--wait"] !== undefined) throw usage("--follow and --wait cannot be combined");
|
||||
const wait = parsed.values["--wait"] === undefined ? TALK_WAIT_S : Number(parsed.values["--wait"]);
|
||||
if (!Number.isInteger(wait) || wait < 0) throw usage("--wait takes whole seconds, 0 or more");
|
||||
refuseInsideAgent(io.env);
|
||||
const system = deps.system ?? loadSystem({ env: io.env });
|
||||
const business = businessFor(parsed, system.dataRoot);
|
||||
const call = deps.transport ? deps.transport(business) : humanTransport({ socket: socketPath(system.dataRoot), business, env: io.env });
|
||||
const [to, body] = parsed.positional;
|
||||
// The broker refuses it too (tester-prefix); this is the clear early message.
|
||||
if (!receiveOnly && parsed.flags.has("--tester") && !body.startsWith(TESTER_PREFIX)) throw new CliError(`a tester message starts with "${TESTER_PREFIX}"`, 2);
|
||||
const call = callFor(parsed, io, deps);
|
||||
let sent;
|
||||
if (!receiveOnly) {
|
||||
sent = await call("message.send", { to, body, class: "REQUEST" });
|
||||
|
||||
Reference in New Issue
Block a user