baa-conductor


baa-conductor / apps / conductor-daemon / src / instructions
codex@macbookpro  ·  2026-04-01

policy.ts

  1import type { BaaInstructionEnvelope } from "./types.js";
  2
  3const CONDUCTOR_TOOLS = [
  4  "conversation/mode",
  5  "conversation/pause",
  6  "conversation/resume",
  7  "describe",
  8  "describe/business",
  9  "describe/control",
 10  "exec",
 11  "files/read",
 12  "files/write",
 13  "status",
 14  "system/pause",
 15  "system/resume"
 16];
 17const BROWSER_LEGACY_TARGET_TOOLS = [
 18  "current",
 19  "send"
 20];
 21const DEFAULT_TARGET_TOOLS = new Map([
 22  ["conductor", CONDUCTOR_TOOLS],
 23  ["system", CONDUCTOR_TOOLS],
 24  ["browser.claude", BROWSER_LEGACY_TARGET_TOOLS],
 25  ["browser.chatgpt", BROWSER_LEGACY_TARGET_TOOLS],
 26  ["browser.gemini", BROWSER_LEGACY_TARGET_TOOLS]
 27]);
 28
 29export type BaaInstructionPolicyTargetToolsInput =
 30  | ReadonlyMap<string, Iterable<string> | null | undefined>
 31  | Record<string, Iterable<string> | null | undefined>;
 32
 33export interface BaaInstructionPolicyConfig {
 34  targets?: BaaInstructionPolicyTargetToolsInput | null;
 35}
 36
 37export interface BaaInstructionPolicy {
 38  targets: ReadonlyMap<string, ReadonlySet<string>>;
 39}
 40
 41export interface BaaInstructionPolicyDecision {
 42  code: string | null;
 43  message: string | null;
 44  ok: boolean;
 45}
 46
 47const DEFAULT_BAA_INSTRUCTION_POLICY = resolveBaaInstructionPolicy({
 48  targets: createDefaultBaaInstructionPolicyTargetTools()
 49});
 50
 51function normalizePolicyIdentifier(kind: "target" | "tool", value: string): string {
 52  const normalized = value.trim();
 53
 54  if (normalized === "") {
 55    throw new TypeError(`BAA instruction policy ${kind} names must be non-empty strings.`);
 56  }
 57
 58  return normalized;
 59}
 60
 61function normalizePolicyToolSet(
 62  target: string,
 63  tools: Iterable<string>
 64): ReadonlySet<string> {
 65  const normalizedTools = new Set<string>();
 66
 67  for (const tool of tools) {
 68    if (typeof tool !== "string") {
 69      throw new TypeError(`BAA instruction policy tools for "${target}" must be strings.`);
 70    }
 71
 72    normalizedTools.add(normalizePolicyIdentifier("tool", tool));
 73  }
 74
 75  return normalizedTools;
 76}
 77
 78function iteratePolicyTargetTools(
 79  input: BaaInstructionPolicyTargetToolsInput
 80): Iterable<[string, Iterable<string> | null | undefined]> {
 81  if (input instanceof Map) {
 82    return input.entries();
 83  }
 84
 85  return Object.entries(input);
 86}
 87
 88export function createDefaultBaaInstructionPolicyTargetTools(): Map<string, Set<string>> {
 89  return new Map(
 90    [...DEFAULT_TARGET_TOOLS.entries()].map(([target, tools]) => [target, new Set(tools)])
 91  );
 92}
 93
 94export function resolveBaaInstructionPolicy(
 95  config: BaaInstructionPolicyConfig | null | undefined = null
 96): BaaInstructionPolicy {
 97  const targetsInput = config?.targets ?? createDefaultBaaInstructionPolicyTargetTools();
 98  const targets = new Map<string, ReadonlySet<string>>();
 99
100  for (const [target, tools] of iteratePolicyTargetTools(targetsInput)) {
101    if (typeof target !== "string") {
102      throw new TypeError("BAA instruction policy target names must be strings.");
103    }
104
105    if (tools == null) {
106      continue;
107    }
108
109    const normalizedTarget = normalizePolicyIdentifier("target", target);
110    targets.set(normalizedTarget, normalizePolicyToolSet(normalizedTarget, tools));
111  }
112
113  return {
114    targets
115  };
116}
117
118export function evaluateBaaInstructionPolicy(
119  instruction: BaaInstructionEnvelope,
120  policy: BaaInstructionPolicy = DEFAULT_BAA_INSTRUCTION_POLICY
121): BaaInstructionPolicyDecision {
122  const supportedTools = policy.targets.get(instruction.target);
123
124  if (!supportedTools) {
125    return {
126      code: "unsupported_target",
127      message: `Target "${instruction.target}" is not supported in Phase 1.`,
128      ok: false
129    };
130  }
131
132  if (!supportedTools.has(instruction.tool)) {
133    return {
134      code: "unsupported_tool",
135      message: `Tool "${instruction.tool}" is not supported in Phase 1.`,
136      ok: false
137    };
138  }
139
140  return {
141    code: null,
142    message: null,
143    ok: true
144  };
145}